9.5 色々な曲線/フラクタル

番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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();}


番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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();
  }



番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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);
    }
  }



番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(4)立ち枯れ木立
コッホ曲線では,長さを1/3にして,60度向きを変え,
3回再帰的呼出しを行っていますが,

これらの長さの比率,向き,呼出し回数を
色々と変えると様々な図形が得られます。

ここでは,以下のように進行方向,比率を変えてみましょう。

向き 比率
0度 0.28
88度 0.28
-176度 0.28
88度 0.70

ここで,向きの合計が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]);
      }
    }
  }

[実行例]

以下のような絵になりますので,立ち枯れ木立と呼ばれます。

    

皆さんも
色々と比率や方向を変えて実行してみると
面白いでしょう。


番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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);
   }

[実行例]

       



番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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(); }


番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系

(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();
  }


番号 以下をクリックすると,該当箇所にジャンプします
(1) スプライン曲線とベジェ曲線
(2) C曲線
(3) コッホ曲線
(4) 立ち枯れ木立
(5) シェルピンスキーのギャスケット
(6) コッホ曲線以降を表示する
(7) 反復関数系


1. 基本的なアルゴリズム

2. 基本的なデータ構造

3. 操作を伴うデータ構造

4. 探索

5. 再帰的アルゴリズム

6. ソート

7. 集合

8. 文字列処理

9. 色々なアルゴリズム


上のタイトルをクリックします