C#による アルゴリズムとデータ構造

9.その他のアルゴリズム
9.1 曜日を求める
ツェラー(Zeller)の公式を使うと西暦の年月日から曜日を求め
ることができます。
例題では,その日が日曜日であれば0,月曜日であれば1,…,
土曜日であれば6を返却する関数を示します。なお,2月の日
数が一定でないので,1月・2月は前年の13月,14月とみなし
ます。
なお,うるう年は4で割れる年ですが,100で割れるが400で
割れない年は平年です。
例題
private int WeekDay(int Y, int M, int D) // ツェラー(Zeller)の公式
{
int YY=Y; int MM=M;
if (M<3){YY=Y-1; MM=M+12;}
return (YY +(YY/4)-(YY/100)+(YY/400)+(13*MM + 8)/5+D) % 7;
}
private void button1_Click(object sender, System.EventArgs e)
{
int Y=int.Parse(textBox1.Text);
int M=int.Parse(textBox2.Text);
int D=int.Parse(textBox3.Text);
int R=WeekDay(Y,M,D);
string X="日月火水木金土";
label4.Text = X[R]+"曜日";
}
9.2 等高線の描画
横Δx,縦Δyの長方形の各辺にhとなる点を見つけて,
線を引くと等高線を描くことができる。
Δx
Z3
Δy
Z2
f(x, y)=h の
等高線
h-Z1
Z2-Z1
Z0
h-Z0
Z1-Z0
Z1
Δx
Δy
9.2 等高線の描画
以下のような等高線の場合,点線のように描くことになるが,
格子を十分小さくとれば,実用上問題は生じない。
フォーム定義
例題
Name : textBox1 Name : button2
Name : openFileDialog1
Name : button1
実行例
初期画面
数値地図読込み表示
(国土地理院発行50mメッシュ)
例題
(以下のネームスペースを追加)
using System.Drawing.Drawing2D;
using System.IO;
例題
(データの宣言)
private int numX=201;
private int numY=201;
private double dx=1.0;
private double dy=1.0;
private double X0, Y0;
private string pbA;
private double Hstep;
private double[,] Z=new double[201,201];
private Matrix matrix=new Matrix();
private Image image;
例題
public double 補間(double H, double Z1, double Z2)
{
return ((H-Z1)/(Z2-Z1));
}
public void Contour(Graphics g, int j, int k, double H){
double Z0=Z[j , k ]; double Z1=Z[j+1, k ];
double Z2=Z[j+1, k+1]; double Z3=Z[j , k+1];
bool P01 = (Z0> H) ^ (Z1 > H); bool P12 = (Z1> H) ^ (Z2 > H);
bool P23 = (Z2> H) ^ (Z3 > H); bool P30 = (Z3> H) ^ (Z0 > H);
if( P01 || P12 || P23 || P30) {
float x0=0; float y1=0; float x2=0;
float y3=0;
Pen pen = new Pen(Color.Black,0.01F);
if(P01) x0 =(float)( dx*(((double)j)+補間(H, Z0,Z1)));
if(P12) y1 =(float)( dy*(((double)k)+補間(H, Z1,Z2)));
if(P23) x2 =(float)( dx*(((double)j)+補間(H, Z3,Z2)));
if(P30) y3 =(float)( dy*(((double)k)+補間(H, Z0,Z3)));
float yk =(float)(dy*(double)(k ));
float yk1=(float)(dy*(double)(k+1));
float xj =(float)(dx*(double)(j ));
float xj1=(float)(dx*(double)(j+1));
if(P01 && P12) g.DrawLine(pen, x0 , yk , xj1, y1 );
if(P12 && P23) g.DrawLine(pen, xj1, y1 , x2 , yk1);
if(P23 && P30) g.DrawLine(pen, x2 , yk1, xj , y3 );
if(P30 && P01) g.DrawLine(pen, xj , y3 , x0 , yk );
if(P01 && P23) g.DrawLine(pen, x0 , yk , x2 , yk1);
if(P12 && P30) g.DrawLine(pen, xj1, y1 , xj , y3 );
}
}
例題
public void Contours(Graphics g, double v1, double v2, double dv)
{
例題
for(int j=0; j<numX-1; j++)
for(int k=0; k<numY-1; k++)
for(double v=v1; v<=v2; v +=dv) Contour(g,j,k,v);
}
public void ColorMap(Graphics g, double v1, double v2, double dv)
{
Color color; float w =(float) dx; float h =(float) dy;
double DD=(v2-v1)/20;
for(int j=0; j<numX-1; j++)
for(int k=0; k<numY-1; k++){
float x1 =(float)( dx*((double)(j)));
float y1 =(float)( dy*((double)(k)));
int ID=(int)((Z[j,k]-v1)/DD);
switch(ID)
{
case 0 : color=Color.FromArgb( 0, 63, 0); break;
case 1 : color=Color.FromArgb( 0, 95, 0); break;
case 2 : color=Color.FromArgb( 0,127, 0); break;
case 3 : color=Color.FromArgb( 0,159, 0); break;
case 4 : color=Color.FromArgb( 0,191, 0); break;
case 5 : color=Color.FromArgb( 0,223, 0); break;
case 6 : color=Color.FromArgb( 0,255, 0); break;
case 7 : color=Color.FromArgb( 31,255, 0); break;
case 8 : color=Color.FromArgb( 63,255, 0); break;
case 9 : color=Color.FromArgb(127,255, 0); break;
case 10 : color=Color.FromArgb(159,255, 0); break;
case 11 : color=Color.FromArgb(191,255, 0); break;
case 12 : color=Color.FromArgb(223,255, 0); break;
case 13 : color=Color.FromArgb(255,255, 0); break;
case 14 : color=Color.FromArgb(255,223, 0); break;
case 15 : color=Color.FromArgb(255,191, 0); break;
case 16 : color=Color.FromArgb(255,159, 0); break;
case 17 : color=Color.FromArgb(255,127, 0); break;
case 18 : color=Color.FromArgb(255, 63, 0); break;
case 19 : color=Color.FromArgb(255, 31, 0); break;
default: color=Color.FromArgb(255, 0, 0); break;
}
Brush brush = new SolidBrush(color);
g.FillRectangle(brush,x1,y1,w,h);
}
}
例題
例題
public double minZ()
{
double R=Z[0,0];
for(int j=0; j<numX-1; j++)
for(int k=0; k<numY-1; k++) if(Z[j,k]<R) R=Z[j,k];
return R;
}
public double maxZ()
{
double R=Z[0,0];
for(int j=0; j<numX-1; j++)
for(int k=0; k<numY-1; k++) if(Z[j,k]>R) R=Z[j,k];
return R;
}
private void 描画()
{
image =new Bitmap(1000,1000);
Graphics g=Graphics.FromImage(image);
g.Clear(this.BackColor);
g.Transform=matrix;
double V1=minZ(); double V2=maxZ();
if(Math.Abs(V2-V1)<0.0000001) V2=V1+1;
ColorMap(g, V1, V2, 0.1);
Contours(g, V1, V2, Hstep);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawImage(image,0,0);
}
例題
private void window(double X1, double Y1, double X2, double Y2)
{
例題
double W=this.ClientSize.Width; double H=this.ClientSize.Height;
float SX=(float)(W/(X2-X1)); float SY=(float)(H/(Y2-Y1));
matrix.Scale(SX,SY);
matrix.Translate(-(float)X1,-(float)Y1);
}
private void Form1_Load(object sender, System.EventArgs e)
{
double XX=((double) numX)*0.5; double YY=((double) numY)*0.5;
X0 = dx * XX; Y0= dy * YY;
double C = 0.02; //倍率
Hstep=double.Parse(textBox1.Text);
for(int i=0; i<numX; i++)
for(int j=0; j<numY; j++)
{
double x=C*(((double)i)-XX); double y=C*(((double)j)-YY);
Z[i,j]=(x-y)*Math.Exp(-(x*x+y*y))*5000;
}
window(-20,240,220,-50);
描画();
}
例題
private void button1_Click(object sender, System.EventArgs e)
{
openFileDialog1.ShowDialog();
}
private string midStr(string DT, int ist,int N)
{
string S="";int k=ist-1;
for(int i=1 ;i<=N;i++){S = S + DT[k]; k=k+1;}
return S;
}
private void openFileDialog1_FileOk
(object sender, System.ComponentModel.CancelEventArgs e)
{
StreamReader DTS; int ii, pbII;
string FName=openFileDialog1.FileName;
if(FName == "") return;
DTS=new StreamReader(FName,System.Text.Encoding.Default);
pbA=DTS.ReadLine(); pbII=0;string DT=DTS.ReadLine();
while(DT !=null)
{
ii = 5;
for(int j=0;j<200;j++)
{
ii = ii + 5;
Z[pbII, j] = int.Parse(midStr(DT, ii, 5));
}
DT=DTS.ReadLine();
pbII++;
}
DTS.Close();
描画();
this.Invalidate();
}
例題
例題
private void textBox1_TextChanged(object sender, System.EventArgs e)
{
Hstep=double.Parse(textBox1.Text);
}
private void button2_Click(object sender, System.EventArgs e)
{
描画();
this.Invalidate();
}
9.3 陰線消去
等高線は,3次元形状を数値的に正確に表示するという意味で
は有効だが,直感的に図形を把握するのが困難
普段,見慣れた形で表示
ワイヤフレーム
3次元図形を単純に平行投影してみると,
本来は見えない部分まで表示されてしまう。
見えない部分の線を消す処理(陰線消去)を入れる
例題データ
2次元配列に以下のデータを入れて高さデータとします。
f  x, y  
sin x 2  y 2
x2  y2
3次元データの平行投影
次のように変換します。
z, Y
X j  j  x  cos  k  y  cos 
Y j  j  x  sin   k  y  sin 
y
x


X
y
x
浮動水平線アルゴリズム
■ 一番,手前から描く。
■ 現在描いている曲線の1点のY座標が,
それ以前に描かれた曲線の最大値より大きければ,
(水平線より上に位置すれば),
その点が見えるものとして描く。
■ 描画の進行に伴って,
水平線がYの正の方向に上がっていくので,
浮動水平線アルゴリズムと呼ばれる。
浮動水平線アルゴリズム
曲線が描かれていく様子
z
y
x
曲線を
描く順序
例題
(以下のネームスペースを追加)
using System.Drawing.Drawing2D;
(データ宣言)
public double Hidden_dlx;
// 表示刻み幅(dl)
public double Hidden_alpha;
// x軸と水平軸との角度(α)
public double Hidden_beta;
// y軸と水平軸との角度(β)
public double Hidden_dx;
// x軸方向の単位メッシュの長さ(dx)
public double Hidden_dy;
// y軸方向の単位メッシュの長さ(dy)
public double[,] 高さ=new double[51,51]; // 高さ(z値)
public double[] YMax=new double[2000]; // Y座標値の最大値(上の浮動水平線)
public double[] YMin=new double[2000]; // Y座標値の最小値(下の浮動水平線)
public double Hidden_Xlen;
// 表示上のX方向長さ=(numX-1)*dx*cos(α)
public double Hidden_Ylen;
// 表示上のX方向長さ=(numY-1)*dx*cos(β)
public int Hidden_NR;
// 浮動水平線用配列の長さ
public double beforX;
// 現在ペン位置X
public double beforY;
// 現在ペン位置Y
public int numX=51;
// x方向メッシュ数
public int numY=51;
// y方向メッシュ数
public double Hidden_dxCosA;
// dx*cos(α)
public double Hidden_dyCosB;
// dy*cos(β)
public double Hidden_dxSinA;
// dx*sin(α)
public double Hidden_dySinB;
// dy*sin(β)
public double Hidden_dlxTanA;
// dl*tan(α)
public double Hidden_dlxTanB;
// dl*tan(β)
public Matrix matrix= new Matrix(); // グローバル座標系への変換マトリックス
例題
(陰線処理)
public bool Hidden_Draw(PaintEventArgs e,Pen pen,double px,double py,
int p, bool Visible, bool Update)
{
// 陰線かどうかを判断し,陰線でない場合,線を描く。
// 関数値 : 表示後の可視フラグ
// e
: 描画用引数
// pen
: ペン属性
// px
: 補間されたX座標値(平面座標系)
// py
: 補間されたY座標値(平面座標系)
// p
: 比較する浮動水平線の位置
// Visible : 現ペン位置が見えているかどうかを示すフラグ(可視フラグ)
// Update : 陰線でないとき,浮動水平線を更新するかどうかを示すフラグ
if((py>=YMax[p])||(py<=YMin[p])){
if(Update && py >=YMax[p])YMax[p]=py;
if(Update && py <=YMin[p])YMin[p]=py;
if(Visible){
float fx1=(float)beforX; float fy1=(float)beforY;
float fx2=(float)px
; float fy2=(float)py;
e.Graphics.DrawLine(pen,fx1,fy1,fx2,fy2);
}
beforX=px;beforY=py; return true;
}
else{
beforX=px;beforY=py; return false;
}
}
例題
(OnPaintのオーバーライド(その1))
protected override void OnPaint(PaintEventArgs e )
{
bool 可視フラグ=true;
base.OnPaint(e);
e.Graphics.Clear(Color.White);
Pen pen=new Pen(Color.Black,0.02F);
e.Graphics.Transform=matrix;
for (int j=0;j<Hidden_NR;j++){ YMax[j]=-1E20; YMin[j]=1E20;} // 浮動水平線の初期化
double X0=80;
// 表示始点位置
double Y0=100;
for(int k=0;k<numY;k++)
{
可視フラグ=false;
for(int j=0;j<numX-1;j++){
// X軸方向描画
int p1=(int)((0.5+(Hidden_Ylen+Hidden_GroundX(j,k)) /Hidden_dlx));
int p2=(int)((0.5+(Hidden_Ylen+Hidden_GroundX(j+1,k))/Hidden_dlx));
for(int p=p1; p<=p2;p++){
// 補間
double PH=(double)(p-p1);
double fp = 高さ[j,k]+(高さ[j+1,k]-高さ[j,k]) * PH * Hidden_dlx
/ Hidden_dxCosA;
double px = PH*Hidden_dlx + Hidden_GroundX(j,k)+ X0;
double py = PH*Hidden_dlxTanA + Hidden_GroundY(j,k) + fp + Y0;
if((j<numX-2 && p<p2) || (j == numX-2))
可視フラグ=Hidden_Draw(e, pen, px, py, p,可視フラグ, true);
}
}
例題
例題
( OnPaintのオーバーライド(その2))
for(int j=0;j<numX && k<numY-1;j++) {
// Y軸方向描画
可視フラグ=false;
int p1=(int)((0.5+(Hidden_Ylen+Hidden_GroundX(j,k)) /Hidden_dlx));
int p2=(int)((0.5+(Hidden_Ylen+Hidden_GroundX(j,k+1))/Hidden_dlx));
for(int p=p1; p>=p2;p--){
// 補間
double PH=(double)(p-p1);
double fp=高さ[j,k]-(高さ[j,k+1]-高さ[j,k])*PH*Hidden_dlx
/ Hidden_dyCosB;
double px = PH*Hidden_dlx + Hidden_GroundX(j,k) + X0;
double py =-PH*Hidden_dlxTanB + Hidden_GroundY(j,k) + fp + Y0;
可視フラグ=Hidden_Draw(e, pen, px, py, p,可視フラグ, p!=p2);
}
}
}
}
(初期座標値)
private double Hidden_GroundX(int j,
{ return (double)j * Hidden_dxCosA private double Hidden_GroundY(int j,
{ return (double)j * Hidden_dxSinA +
int k) // X0=Y0=0のときのX座標
(double) k * Hidden_dyCosB;}
int k) // X0=Y0=0のときのY座標
(double) k * Hidden_dySinB;}
(変換マトリックスの設定)
private void window(double X1, double Y1, double X2, double Y2)
{
float W=this.ClientSize.Width; float H=this.ClientSize.Height;
float SX=W/((float)(X2-X1)); float SY=H/((float)(Y2-Y1));
matrix.Scale(SX,SY);
matrix.Translate(-(float)X1,-(float)Y1);
}
例題
例題
(初期化)
private void Form1_Load(object sender, System.EventArgs e){
double DNX2= ((double)numX)/2; double DNY2= ((double)numY)/2; // 高さデータの設定
double X,Y,R,fxy;
for(int j=0; j<numX; j++){
X=0.3*((double)j-DNX2);
for(int k=0; k<numY; k++){
Y=0.3*((double)k-DNY2); R=Math.Sqrt(X*X+Y*Y);
if(R==0.0) fxy=1.0; else fxy=Math.Sin(R)/R;
高さ[j,k]=40.0*fxy;
}
}
Hidden_dlx
= 0.1; Hidden_alpha = Math.PI/12; // 表示用パラメータの設定
Hidden_beta = Math.PI/8; Hidden_dx = 2;
Hidden_dy = 1.4;
Hidden_dxCosA=Hidden_dx*Math.Cos(Hidden_alpha); // 計算に用いる値の設定
Hidden_dyCosB=Hidden_dx*Math.Cos(Hidden_beta);
Hidden_dxSinA=Hidden_dx*Math.Sin(Hidden_alpha);
Hidden_dySinB=Hidden_dx*Math.Sin(Hidden_beta);
Hidden_Xlen=(numX-1)*Hidden_dxCosA;
Hidden_Ylen=(numY-1)*Hidden_dyCosB;
Hidden_dlxTanA=Hidden_dlx*Math.Tan(Hidden_alpha);
Hidden_dlxTanB=Hidden_dlx*Math.Tan(Hidden_beta);
Hidden_NR=(int)((Hidden_Xlen+Hidden_Ylen)/Hidden_dlx)+1;
// 表示座標マトリックスの設定
window(-10,200,200,60);
// 表示座標マトリックスの設定
}
9.4 ランダムドット
3次元的な物体を2次元平面上に描いて,
(1)立体視とは
その2次元平面の図形から元の3次元物体を
再構成してみること
2枚の絵を左右別々に見ることで
立体視ができる
左目には青が消えて見え,
右目には赤が消えて見える
ので立体に見える
(2)ランダムドット・ステレオグラム
1枚の図で立体視が可能な絵
■ 見えている物体面が紙面に対して平行であると仮定。
■ ある基準となる面から物体面の距離をdとすると,
紙面上の間隔Tは以下の式で近似することができる。
T=C-R・d
ここで,
C:物体が基準となる位置にある場合に対応する
紙面上の点の間隔,
R:ずらすための定数(値が大きいとき,飛び出し方や
引っ込み方が大きくなる)
ランダムドット・ステレオグラムの見方
ランダムドット・ステレオグラムの上方に,以下の(a)のような2つの点があるので,
目をリラックスさせた状態で紙面に視線を向ける。
平行法の場合は,紙面より先のほうへ,交差法の場合は,紙面より手前のほう
に焦点を合わせるように見ると,これがぼやけて見える。
ぼやけた円が2個,中央にはっきりした円が1個に見えるように
目の焦点をずらすと,ステレオグラムが立体に見えてくる。
(a)最初の状態
焦点をずらす
(b)立体視できる状態
(ぼやけてみえる)
Sin(R)/R
例
半球
例
双曲放物面
例
プログラム
フォーム定義
Name:comboBox1
Name:textBox1
例題
(以下のネームスペースを追加)
using System.Drawing.Drawing2D;
(データ宣言)
public Matrix matrix =new Matrix();
public string 処理;
(各種関数その1)
private double SinR_dev_R(double X, double Y)
{
double R = Math.Sqrt(X*X+Y*Y);
if(R<=0.0000001) return 1.0;
return Math.Sin(R)/R;
}
private double 半球(double X, double Y)
{
double R = Math.Sqrt(X*X+Y*Y);
if(R>5) return -1.0;
else
{ R=R/5; return Math.Sqrt(1-R*R)-1;}
} private double 円筒(double X, double Y)
{
double R = Math.Abs(X);
if(R>5) return -1.0;
else { R=R/5; return Math.Sqrt(1-R*R)-1;}
}
private double ピラミッド(double X, double Y)
{
double XX = Math.Abs(X); double R = Math.Abs(Y);
if(XX>R) R=XX;
if(R>5) return -1;
else { R=R/2.5; return 1 - R; }
}
例題
(各種関数その2)
private double fmod(double D, double M)
{
return D- (int)(D/M)*M; }
private double 上下開き(double X, double Y)
{
double R = Math.Sqrt(X*X+Y*Y);
if(R>=10) return 0;
else if (fmod(R, 4.0)>2.0) return Y/10;
else return -Y/10;
}
private double 双曲放物線(double X, double Y)
{
return (X*X-Y*Y)/50;}
private double 二次錘面(double X, double Y)
{
return 1- Math.Sqrt(X*X+Y*Y)/5;}
private double 十字(double X, double Y)
{
double XX=Math.Abs(X) ; double YY=Math.Abs(Y);
if
(XX>5 || YY>5) return -1;
else if(XX<1 || YY<1) return 1;
else
return -1;
}
private double Def_Exp(double X, double Y)
{
double XX=X/5 ; double YY=Y/5;
double R=XX*XX+YY*YY;
return (XX-YY) *Math.Exp(-R);
}
例題
例題
(各種関数その3)
private double mult_R(double X, double Y)
{
double XX=fmod(X,4)-2 ; double YY=fmod(Y,4)-2;
double R=Math.Sqrt(XX*XX+YY*YY);
if (R>1) return 0;
else return Math.Sqrt(1-R*R);
}
private double func10(double X, double Y)
{
return (Math.Cos(X/1.5)+Math.Sin(Y/1.5))/2;}
private double func11(double X, double Y)
{
double XX=X/3 ; double YY=Y/3;
if( XX>0) XX=-XX;
if(YY>0) YY=-YY;
return Math.Exp(XX+YY)*2-1;
}
例題
(各種関数呼出処理)
private double 関数呼出(double X, double Y)
{
int ID=comboBox1.SelectedIndex;
switch(ID)
{
case 0:return SinR_dev_R(X,Y);
case 1:return 半球(X,Y);
case 2:return 円筒(X,Y);
case 3:return ピラミッド(X,Y);
case 4:return 上下開き(X,Y);
case 5:return 双曲放物線(X,Y);
case 6:return 二次錘面(X,Y);
case 7:return 十字(X,Y);
case 8:return Def_Exp(X,Y);
case 9:return mult_R(X,Y);
case 10:return func10(X,Y);
case 11:return func11(X,Y);
}
return 0;
}
(ドットの表示)
public void ドット表示(PaintEventArgs e)
{
double X1 = -12; double X2 = 12; double Y1 = 10; double Y2 = -10;
double R0 = double.Parse(textBox1.Text);
double UNIT = 6; double XY =1; int Loop=3000;
double Period=(X2-X1)/6; double Z0=R0*Period; double R, X, Y, Z;
例題
Pen pen = new Pen(Color.Black,0.001F);
Brush brush =new SolidBrush(Color.Black);
e.Graphics.Clear(Color.White);
float XX=(float)(Period/6.0);float YY=(float)(Y1*1.1);
e.Graphics.DrawEllipse(pen, -XX, YY, 0.1F, 0.1F);
e.Graphics.DrawEllipse(pen, XX, YY, 0.1F, 0.1F);
Random RD = new Random();
for(int i=0;i<Loop;i++)
{
R=RD.NextDouble(); Y=R*(Y2-Y1)+Y1; Z=関数呼出(X1,Y);
R=RD.NextDouble(); X=(Period + Math.Abs(Z0*Z))*R+X1;
while(X<=X2)
{ double XP=(X - (X1+X2)*0.5)*UNIT;
double YP=(Y - (Y1+Y2)*0.5)*UNIT;
e.Graphics.FillRectangle(brush,(float)(X-0.025),(float)(Y-0.025),0.1F,0.1F);
Z = 関数呼出(X + Period * 0.5, Y);
X = X + Period - XY * Z0 * Z;
}
}
}
例題
( OnPaint のオーバーライド)
protected override void OnPaint(PaintEventArgs e )
{
base.OnPaint(e );
e.Graphics.Clear(Color.White);
e.Graphics.Transform = matrix;
if (処理!="")ドット表示(e);
Pen pen = new Pen(Color.Black,0.001F);
e.Graphics.Transform = matrix;
e.Graphics.DrawLine(pen, -12F, 10F,12F, 10F);
e.Graphics.DrawLine(pen, -12F,-10F,12F,-10F);
e.Graphics.DrawLine(pen, -12F,10F,-12F,-10F);
e.Graphics.DrawLine(pen, 12F,10F, 12F,-10F);
}
例題
(関数の選択時の処理)
private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
処理="Exe"; this.Invalidate(); }
(座標変換マトリックスの設定)
private void window(float X1, float Y1, float X2, float Y2)
{
float W= this.Width; float H=this.Height;
float SX=W/(X2-X1); float SY=H/(Y2-Y1);
matrix.Scale(SX,SY); matrix.Translate(-X1,-Y1);
}
(初期設定)
private void Form1_Load(object sender, System.EventArgs e)
{
処理="";
window(-12F*1.2F, 12F*1.4F, 10F*1.4F,-10F*1.4F);
comboBox1.Items.Clear();
comboBox1.Items.Add("sin(R)/R");
comboBox1.Items.Add("半球");
comboBox1.Items.Add("円筒");
comboBox1.Items.Add("ピラミッド");
comboBox1.Items.Add("上下開き");
comboBox1.Items.Add("双曲放物面");
comboBox1.Items.Add("二次錘面");
comboBox1.Items.Add("十字");
comboBox1.Items.Add("(x-y)exp(-(x*x+y*y))");
comboBox1.Items.Add("繰返し");
comboBox1.Items.Add("cos(x)+sin(y)");
comboBox1.Items.Add("exp(-(abs(x)+abs(y))");
comboBox1.SelectedIndex=0;
}
例題
9.5 スプライン曲線とベジェ曲線
GraphicsクラスのDrawCurveメソッドや DrawBezierメソッドを使う
とスプライン曲線や,ベジェ曲線を描くことができます。
9.5 スプライン曲線とベジェ曲線
スプライン曲線では,閉曲線も描くことができます。
さらにメソッドにテンションを引数で受け渡すことで,歪曲度を変
更することができます。
9.5 スプライン曲線とベジェ曲線
以下の例は,テンションを連続的に変化させ,テンションの値に
よって線の色を変えたものです。
プログラム
フォーム定義
Name:button1
Name:button4
Name:button5
Name:button3
Name:button2 Name:button3
Name:button3
(追加するUsing)
using System.Drawing.Drawing2D;
確認用プログラム
(データ宣言)
private string 曲線="ベジェ曲線";
private Point[] 構成点={ new Point(200,100),new Point(250,200),
new Point(350,150), new Point(400,250)};
private Pen pen1 = new Pen(Color.Black,2);
private Pen pen2 = new Pen(Color.Blue ,1);
private Pen pen3 = new Pen(Color.Green,1);
private Pen pen4 = new Pen(Color.Red ,1);
private Pen pen5 = new Pen(Color.Black,1);
(ベジェ曲線を描く)
private void ベジェ曲線(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawBezier(pen1,構成点[0],構成点[1],構成点[2],構成点[3]);
e.Graphics.DrawLines(pen4,構成点);
}
(スプライン曲線)
private void スプライン曲線(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawCurve(pen1,構成点);
e.Graphics.DrawLines(pen4,構成点);
}
private void 閉曲線(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawClosedCurve(pen1,構成点);
e.Graphics.DrawLines(pen4,構成点);
}
private void 閉曲線テンション指定(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawClosedCurve(pen2,構成点,0.0F,FillMode.Alternate);
e.Graphics.DrawClosedCurve(pen3,構成点,0.5F,FillMode.Alternate);
e.Graphics.DrawClosedCurve(pen4,構成点,1.0F,FillMode.Alternate);
e.Graphics.DrawClosedCurve(pen5,構成点,2.0F,FillMode.Alternate);
}
確認用プログラム
private void テンション指定(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawCurve(pen2, 構成点, 0.0F);
e.Graphics.DrawCurve(pen3, 構成点, 0.5F);
e.Graphics.DrawCurve(pen4, 構成点, 1.0F);
e.Graphics.DrawCurve(pen5, 構成点, 2.0F);
}
private void 閉図形(PaintEventArgs e )
{
base.OnPaint(e);
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(i,0,0));
e.Graphics.DrawClosedCurve(pen, 構成点,
(float)i * 0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(255,i,0));
e.Graphics.DrawClosedCurve(pen, 構成点, 0.5F+(float)i *
0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(255-i,255,0));
e.Graphics.DrawClosedCurve(pen, 構成点, 1F+(float)i *
0.5F/255F,FillMode.Alternate);
}
確認用プログラム
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(0,255,i));
e.Graphics.DrawClosedCurve(pen, 構成点, 1.5F+(float)i
0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(0,255-i,255));
e.Graphics.DrawClosedCurve(pen, 構成点, 2.0F+(float)i
0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(i,0,255));
e.Graphics.DrawClosedCurve(pen, 構成点, 2.5F+(float)i
0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(255,0,255-i));
e.Graphics.DrawClosedCurve(pen, 構成点, 3.0F+(float)i
0.5F/255F,FillMode.Alternate);
}
for(int i=0;i<255;i++)
{ Pen pen =new Pen(Color.FromArgb(255-i,0,0));
e.Graphics.DrawClosedCurve(pen, 構成点, 3.5F+(float)i
0.5F/255F,FillMode.Alternate);
}
確認用プログラム
}
*
*
*
*
*
確認用プログラム
private void 図形(PaintEventArgs e )
{
base.OnPaint(e);
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(i,0,0));
e.Graphics.DrawCurve(pen, 構成点, (float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(255,i,0));
e.Graphics.DrawCurve(pen, 構成点, 0.5F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(255-i,255,0));
e.Graphics.DrawCurve(pen, 構成点, 1F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(0,255,i));
e.Graphics.DrawCurve(pen, 構成点, 1.5F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(0,255-i,255));
e.Graphics.DrawCurve(pen, 構成点, 2.0F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(i,0,255));
e.Graphics.DrawCurve(pen, 構成点, 2.5F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(255,0,255-i));
e.Graphics.DrawCurve(pen, 構成点, 3.0F+(float)i * 0.5F/255F);
}
for(int i=0;i<255;i++)
{
Pen pen =new Pen(Color.FromArgb(255-i,0,0));
e.Graphics.DrawCurve(pen, 構成点, 3.5F+(float)i * 0.5F/255F);
}
確認用プログラム
}
protected override void OnPaint(PaintEventArgs e )
{
switch (曲線)
{
case "ベジェ曲線"
: ベジェ曲線(e)
;break;
case "スプライン曲線" : スプライン曲線(e);break;
case "テンション指定" : テンション指定(e);break;
case "図形"
: 図形(e);break;
case "閉曲線"
: 閉曲線(e);break;
case "閉曲線テンション": 閉曲線テンション指定(e);break;
case "閉図形"
: 閉図形(e);break;
}
}
確認用プログラム
private void button1_Click(object sender, System.EventArgs e)
{ 曲線=“ベジェ曲線”; Invalidate();}
private void button2_Click(object sender, System.EventArgs e)
{ 曲線="スプライン曲線"; Invalidate(); }
private void button3_Click(object sender, System.EventArgs e)
{ 曲線="テンション指定"; Invalidate();}
private void button4_Click(object sender, System.EventArgs e)
{ 曲線=“図形”; Invalidate();}
private void button5_Click(object sender, System.EventArgs e)
{ 曲線="閉曲線"; Invalidate(); }
private void button6_Click(object sender, System.EventArgs e)
{ 曲線=“閉曲線テンション”; Invalidate();}
private void button7_Click(object sender, System.EventArgs e)
{ 曲線="閉図形"; Invalidate();}
(2) C曲線
以下のような曲線を各プログラムである。現在位置から相対座
標に至るC曲線を描くプログラムである。
private Image image;
確認用プログラム①
private Point currentPoint=new Point();
private System.Windows.Forms.TextBox textBox1;
private Point nextPoint=new Point();
private double maxLength=2;
private int lineWidth=1;
protected override void OnPaint(PaintEventArgs e )
{
base.OnPaint(e);
e.Graphics.DrawImage(image,0,0);
}
private void drawRelative(int X, int Y)
{
nextPoint.X=currentPoint.X+X;
nextPoint.Y=currentPoint.Y+Y;
Graphics g =Graphics.FromImage(image);
Pen pen = new Pen(Color.Black,lineWidth);
g.DrawLine(pen,currentPoint,nextPoint);
currentPoint=nextPoint;
}
確認用プログラム②
private void moveAbsolute(int X, int Y)
{ currentPoint.X=X; currentPoint.Y=Y;}
private void C_Curve(double X, double Y)
{ if(X*X+Y*Y<=maxLength*maxLength) drawRelative((int)X, (int)Y);
else{ C_Curve((X+Y)/2,(Y-X)/2); C_Curve((X-Y)/2,(Y+X)/2);}
}
private void button1_Click(object sender, System.EventArgs e)
{
Graphics g =Graphics.FromImage(image);
g.Clear(BackColor);
maxLength=double.Parse(textBox1.Text);
int X=int.Parse(textBox2.Text);
int Y=int.Parse(textBox3.Text);
lineWidth=int.Parse(textBox4.Text);
moveAbsolute(X,Y);
C_Curve(500,0);
this.Invalidate();
}
確認用プログラム③
private void moveAbsolute(int X, int Y)
{ currentPoint.X=X; currentPoint.Y=Y;}
private void C_Curve(double X, double Y)
{ if(X*X+Y*Y<=maxLength*maxLength) drawRelative((int)X, (int)Y);
else{ C_Curve((X+Y)/2,(Y-X)/2); C_Curve((X-Y)/2,(Y+X)/2);}
}
private void button1_Click(object sender, System.EventArgs e)
{
Graphics g =Graphics.FromImage(image);
g.Clear(BackColor);
maxLength=double.Parse(textBox1.Text);
int X=int.Parse(textBox2.Text);
int Y=int.Parse(textBox3.Text);
lineWidth=int.Parse(textBox4.Text);
moveAbsolute(X,Y);
C_Curve(500,0);
this.Invalidate();
}
確認用プログラム④
private void button2_Click(object sender, System.EventArgs e)
{
Graphics g =Graphics.FromImage(image);
g.Clear(BackColor);
this.Invalidate();
}
(3) コッホ曲線
長さ1の線分を三等分し,中央の1/3を削除し,そこに1/3の長さ
の正三角形を挿入する操作を何回も繰り返して得られる曲線を
コッホ曲線と呼びます。
描画例
[例]
プログラム例①
(追加するusing)
using System.Drawing.Drawing2D;
(データ宣言)
public struct 位置データ
{
public double X; public double Y; }
public 位置データ 現在位置 = new 位置データ();
public double 角度;
public void t_position(double X, double Y)
{
現在位置.X=X; 現在位置.Y=Y;}
public void t_Degree(double Theta)
{
角度=Theta; }
public void t_turn(double Theta)
{
角度 += Theta; }
プログラム例②
public void t_forward(PaintEventArgs e, double length)
{
double TH = 角度 * 3.1415926/180;
double X=現在位置.X+length * Math.Cos(TH);
double Y=現在位置.Y+length * Math.Sin(TH);
Pen pen =new Pen(Color.Black);
e.Graphics.DrawLine(pen, (int)現在位置.X, 300-(int)現在位置.Y, (int)X,300-(int)Y);
現在位置.X=X;
現在位置.Y=Y;
}
(以下のように再帰的に表現できる)
public void koch( PaintEventArgs e,int n,double length)
{ if(n<=0) t_forward(e,length);
else
{
int nn=n-1; double len=length/3;
koch(e, nn,len); t_turn(60);
koch(e, nn,len); t_turn(-120);
koch(e, nn,len); t_turn(60);
koch(e, nn,len);
}
}
(4) 立ち枯れ木立
コッホ曲線では,長さを1/3にして,60度向きを変えて,3回再帰的呼出しを
行っているが,長さの比率,向きを色々と変えて再帰呼び出しを行うと色々
な図形が得られる。
ここでは,以下のように進行方向,比率を変えた例を示す。
向き
0
88
-176
88
比率
0.28
0.28
0.28
0.7
(向きの合計が 0+88-176+88=0 となることに注意)
実行例
[例]
public double[,] element
=new double[,]{{
0.0, 0.28},{88.0, 0.28},
{-176.0, 0.28},{88.0, 0.70}};
public void frctl( PaintEventArgs e, double length)
{
int j;
if (length <=2.0) t_forward(e,length);
else{
for(j=0; j<4; j++){
t_turn(element[j,0]);
frctl(e, length*element[j,1]);
}}
}
(5) シェルピンスキーのギャスケット
シェルピンスキーのギャスケットとは,
三角形の中に1辺の長さ1/2の三角形を順次描くことによって得
られます。これもコッホ曲線と同様,自己相似を内部に持つ形
になります。
プログラムとギャスケットの例
public void 三角形(PaintEventArgs e,double X, double Y, double L)
{
Pen pen =new Pen(Color.Black);
int IX1=(int)X; int IX2=(int)(X + L ); int IX3=(int)(X + L / 2);
int IY1=300-(int)Y; int IY2=300-(int)(Y- L*Math.Sin(60*3.1415926/180));
e.Graphics.DrawLine(pen, IX1,IY1, IX2,IY1);
e.Graphics.DrawLine(pen, IX1,IY1, IX3,IY2);
e.Graphics.DrawLine(pen, IX2,IY1, IX3,IY2);
}
public void sier( PaintEventArgs e, int n, double X, double Y, double L)
{
if(n==0) return;
double H = L * Math.Sin(60*3.1415926/180);
三角形(e, X + L / 4, Y + H / 2, L/2);
sier(e, n-1, X
, Y
, L / 2);
sier(e, n-1, X + L/2, Y
, L / 2);
sier(e, n-1, X + L/4, Y + H / 2, L / 2);
}
public void sier( PaintEventArgs e)
{
三角形(e,290.0,10.0,-280.0);
sier(e, 6, 10.0, 10.0, 280.0);
}
プログラム例①
(追加するusing)
using System.Drawing.Drawing2D;
(データ宣言)
public struct 位置データ
{
public double X; public double Y; }
public 位置データ 現在位置 = new 位置データ();
public double 角度;
public Matrix matrix=new Matrix();
public string 処理;
public void t_position(double X, double Y)
{
現在位置.X=X; 現在位置.Y=Y;}
public void t_Degree(double Theta)
{
角度=Theta; }
public void t_turn(double Theta)
{
角度 += Theta; }
プログラム例②
public void t_forward(PaintEventArgs e, double length)
{
double TH = 角度 * 3.1415926/180;
double X=現在位置.X+length * Math.Cos(TH);
double Y=現在位置.Y+length * Math.Sin(TH);
Pen pen =new Pen(Color.Black);
e.Graphics.DrawLine(pen, (int)現在位置.X, 300-(int)現在位置.Y, (int)X,300-(int)Y);
現在位置.X=X;
現在位置.Y=Y;
}
public void koch( PaintEventArgs e,int n,double length)
{ if(n<=0) t_forward(e,length);
else
{
int nn=n-1; double len=length/3;
koch(e, nn,len); t_turn(60);
koch(e, nn,len); t_turn(-120);
koch(e, nn,len); t_turn(60);
koch(e, nn,len);
}
}
プログラム例③
public double[,] element =new double[,]{{
0.0, 0.28},{88.0, 0.28},
{-176.0, 0.28},{88.0, 0.70}};
public void frctl( PaintEventArgs e, double length)
{
int j;
if (length <=2.0) t_forward(e,length);
else
{
for(j=0; j<4; j++){ t_turn(element[j,0]);frctl(e, length*element[j,1]);}
}
}
プログラム例④
public void 三角形(PaintEventArgs e,double X, double Y, double L)
{
Pen pen =new Pen(Color.Black);
int IX1=(int)X; int IX2=(int)(X + L ); int IX3=(int)(X + L / 2);
int IY1=300-(int)Y; int IY2=300-(int)(Y- L*Math.Sin(60*3.1415926/180));
e.Graphics.DrawLine(pen, IX1,IY1, IX2,IY1);
e.Graphics.DrawLine(pen, IX1,IY1, IX3,IY2);
e.Graphics.DrawLine(pen, IX2,IY1, IX3,IY2);
}
public void sier( PaintEventArgs e, int n, double X, double Y, double L)
{
if(n==0) return;
double H = L * Math.Sin(60*3.1415926/180);
三角形(e, X + L / 4, Y + H / 2, L/2);
sier(e, n-1, X
, Y
, L / 2);
sier(e, n-1, X + L/2, Y
, L / 2);
sier(e, n-1, X + L/4, Y + H / 2, L / 2);
}
public void sier( PaintEventArgs e)
{
三角形(e,290.0,10.0,-280.0);
sier(e, 6, 10.0, 10.0, 280.0);
}
プログラム例⑤
protected override void OnPaint( PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Clear(Color.WhiteSmoke);
Pen pen =new Pen(Color.Red);
Rectangle rect = new Rectangle(0,0,300,300);
e.Graphics.Transform=matrix; e.Graphics.DrawRectangle(pen, rect);
t_position(0.0,40.0); t_Degree(0.0);
switch (処理)
{
case ""
: break;
case "koch" : koch(e, 6, 300.0); break;
case "frctl": frctl(e, 300.0) ; break;
case "sier" : sier(e)
; break;
}
}
public void window(float X1,float Y1,float X2,float Y2, float R)
{
float W=ClientSize.Width; float H=ClientSize.Height;
float SX=W/(X2-X1);
float SY=H/(Y2-Y1);
matrix.Scale(SX,SY);
matrix.Rotate(R);
matrix.Translate(-X1,-Y1);
}
プログラム例⑥
private void Form1_Load(object sender, System.EventArgs e)
{
処理=“”; window(-10F,-10F,310F,310F,0); this.Invalidate();
private void button1_Click(object sender, System.EventArgs e)
{
処理="koch"; this.Invalidate();}
private void button2_Click(object sender, System.EventArgs e)
{
処理="frctl"; this.Invalidate();}
private void button3_Click(object sender, System.EventArgs e)
{
処理="sier"; this.Invalidate(); }
}
(6)反復関数系
自己相似図形があれば,その図形を構成する
反復関数系を見つけ,各関数に確率を割り振って,
乱数で得られた座標に点を打つことで,
色々な図形を生成可能
もちろんシェルピンスキーのギャスケットも,
この方法で表現できます。
ここでは,別の例を示す。
 x   0.8560 0.0414  x1  0.070
(1)  2   





y
y

0
.
0205
0
.
8580
0
.
147
 1 

 2 
 x2  0.2440  0.385  x1  0.393
(2)    
  y   0.102
y
0
.
1760
0
.
8224
 1 

 2 
 x2   0.144 0.3900  x1   0.527 
(3)    





y
y
0
.
1810
0
.
2590

0
.
014
 1 

 2 
0.   x1  0.486
 x2   0.
(4)    





y
y
0
.
3550
0
.
2160
0
.
05
 1 

 2 
(1) 0.73, (2) 0.13, (3) 0.13, (4) 0.01
プログラム例①
(追加するusing)
using System.Drawing.Drawing2D;
(データ宣言)
public double [,,] Fsys1=new double[,,]
{{{ 0.856, 0.0414}, {-0.0205, 0.858}},
{{ 0.244,-0.3850}, { 0.1760, 0.224}},
{{-0.144, 0.3900}, { 0.1810, 0.259}},
{{ 0.000, 0.0000}, { 0.3550, 0.216}}};
public double [,] Fsys2 = new double [,]
{{0.07,0.147},{0.393,0.102},{0.527,-0.014},{0.486,0.05}};
public bool[,] 点=new bool[1001,1001];
public Matrix matrix=new Matrix();
private Image image;
プログラム例①
public void 描画()
{
Brush brush = new SolidBrush(Color.DarkGreen);
Graphics g=Graphics.FromImage(image);
g.Clear(Color.White);
for(int i=0; i<1000; i++)
for(int j=0; j<1000; j++)
{
if(点[i,j]) g.FillRectangle(brush,i,(1000-j),1,1);
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.Transform=matrix;
e.Graphics.DrawImage(image,100,100);
}
プログラム例①
private void window(float X1,float Y1,float X2, float Y2, float R)
{
float W =ClientSize.Width; float H=ClientSize.Height;
float SX=W/(X2-1); float SY = H / (Y2 -Y1);
float MX= -SX *X1; float MY = - SY * Y1;
matrix.Scale(SX,SY); matrix.Rotate(R);
matrix.Translate(MX,MY);
}
private void 初期化()
{
for(int i=0; i<1001; i++)
for(int j=0; j<1001; j++) 点[i,j]=false;
描画();
}
private void Form1_Load(object sender, System.EventArgs e)
{ 初期化(); window(0,0,1000,1000,0);}
プログラム例①
private void button1_Click(object sender, System.EventArgs e)
{ int ID;double X1,Y1;
Random RD = new Random();
double X0=1.0; double Y0=1.0;
for(int i=0; i<20000; i++)
{
double R=RD.NextDouble();
if
(R < 0.73) ID = 0;
else if (R < 0.86) ID = 1;
else if (R < 0.99) ID = 2;
else
ID = 3;
X1=Fsys1[ID,0,0]*X0+Fsys1[ID,0,1]*Y0+Fsys2[ID,0];
Y1=Fsys1[ID,1,0]*X0+Fsys1[ID,1,1]*Y0+Fsys2[ID,1];
if(X1<1.0 && Y1<1.0 && X1>=0 && Y1>=0)
点[(int) (X1*1000.0),(int) (Y1*1000.0)]=true;
X0=X1;Y0=Y1;
}
描画(); this.Invalidate();
}