![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(1)スプライン曲線とベジェ曲線 GraphicsクラスのDrawCurveメソッドや DrawBezierメソッドを使うと スプライン曲線や,ベジェ曲線を描くことができます。 ![]() ![]() スプライン曲線では,閉曲線も描くことができます。 さらにメソッドにテンションを引数で受け渡すことで, 歪曲度を変更することができます。 ![]() ![]() 以下の例は,テンションを連続的に変化させ, テンションの値によって線の色を変えたものです。 ![]() ![]() [確認用プログラム] ![]() 2次元図形を描画しますので以下の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); ベジェ曲線を描くには,DrawBezierメソッドを用います。 private void ベジェ曲線(PaintEventArgs e ) { base.OnPaint(e); e.Graphics.DrawBezier(pen1,構成点[0],構成点[1],構成点[2],構成点[3]); e.Graphics.DrawLines(pen4,構成点); } スプライン曲線を描くには,DrawCurveメソッドを用います。 private void スプライン曲線(PaintEventArgs e ) { base.OnPaint(e); e.Graphics.DrawCurve(pen1,構成点); e.Graphics.DrawLines(pen4,構成点); } スプライン閉曲線を描くには,DrawClosedCurveメソッドを用います。 private void 閉曲線(PaintEventArgs e ) { base.OnPaint(e); e.Graphics.DrawClosedCurve(pen1,構成点); e.Graphics.DrawLines(pen4,構成点); } DrawCurveメソッドでは,以下のようにしてテンションを指定します。 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); } DrawClosedCurveメソッドでは,以下のようにしてテンションを指定します。 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); 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); } } OnPaintのオーバライドで各曲線のプログラムを呼び出します。 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; } } 以下,それぞれのボタンのClickイベントハンドラです。 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曲線を描くプログラムです。 最大長を小さくするとより複雑な曲線が得られます。 ![]() [プログラム例] フォームには,オプション入力用として, textBox1(開始位置X座標), textBox2(現在位置Y座標), textBox3(最大長), textBox4(線の太さ)を用意します。 更に,button1(描画開始),button2(表示クリア)用の コマンドボタンを用意します。 private Image image; private Point currentPoint=new Point(); 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 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; } コッホ曲線を描く処理は, 現進行方向に1/3だけ進み,60度方向を変える。 その方向に1/3だけ進み,-120度方向を変える。 更に1/3だけ進み,60度方向を変える。 方向は,60度→ -120度→ 60度, すなわち,最後は最初の方向に戻ります。 以上から,以下のように再帰的に表現できます。 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度になっていることを確認してください。 [プログラム例] データ宣言や共通処理は,コッホ曲線と同じです。 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); } [実行例] ![]()
(6)コッホ曲線以降の表示 上記,(3)以降については, 初期化やコマンドボタンの Click イベントハンドラ等を 示していませんでしたので,ここで提示します。 まず,フォーム定義では,button1,button2,button3を 右上に配置しておきます。 コッホのプログラムで示したデータ宣言に 以下の変数宣言を追加します。 public Matrix matrix=new Matrix(); public string 処理; これまでに示したプログラムに 以下の記述を追加します。 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(); }
(7)反復関数系 自己相似図形があれば,その図形を構成する 反復関数系を見つけ,各関数に確率を割り振って, 乱数で得られた座標に点を打つことで, 色々な図形を生成できます。 もちろんシェルピンスキーのギャスケットも, この方法で表現できます。 ここでは,別の例を示しますので, シェルピンスキーのギャスケットについては, 皆さんでチャレンジしてみてください。 なお,フラクタル図形の教科書を参照するのも ひとつの手段でしょう。 ここでは,以下のような反復関数系を実行してみます。 @)反復関数系 ![]() A)発生確率 ![]() B)実行例 ![]() [プログラム例] (追加する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);} (ボタンClick処理) 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(); }
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() 上のタイトルをクリックします |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||