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(); }
© Copyright 2025 ExpyDoc