自己相似形
提供:kuhalaboWiki
(版間での差分)
(→openFrameworksによる実装) |
(→コッホ図形の描画) |
||
21行: | 21行: | ||
class Koch { | class Koch { | ||
public: | public: | ||
− | ofColor bcolor; | + | ofColor bcolor; //描画色 |
int generation; // 世代 | int generation; // 世代 | ||
− | void recurse(int n, ofPoint p1, ofPoint p2); | + | void recurse(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド |
}; | }; | ||
</pre> | </pre> | ||
34行: | 34行: | ||
void Koch::recurse(int n, ofPoint p1, ofPoint p2){ | void Koch::recurse(int n, ofPoint p1, ofPoint p2){ | ||
ofPoint p3,p4,p5; | ofPoint p3,p4,p5; | ||
− | double s = sin( PI / 3.0); | + | |
− | double c = cos( PI / 3.0); | + | // ofLine(p1,p2); |
+ | // ofCircle(200,300,100); | ||
+ | |||
+ | double s = sin( PI / 3.0); // sin(60) | ||
+ | double c = cos( PI / 3.0); // cos(60) | ||
if( n > 0 ){ | if( n > 0 ){ | ||
− | p3 = ( 2 * p1 + p2 ) / 3.0; | + | p3 = ( 2 * p1 + p2 ) / 3.0; //内分点 |
p4 = ( p1 + 2 * p2 ) / 3.0; | p4 = ( p1 + 2 * p2 ) / 3.0; | ||
− | p5.x = p3.x + ( p4.x - p3.x ) * c + (p4.y - p3.y) * s; | + | p5.x = p3.x + ( p4.x - p3.x ) * c + (p4.y - p3.y) * s; //回転移動 |
p5.y = p3.y - ( p4.x - p3.x ) * s + (p4.y - p3.y) * c; | p5.y = p3.y - ( p4.x - p3.x ) * s + (p4.y - p3.y) * c; | ||
ofSetColor(0,0,0); | ofSetColor(0,0,0); | ||
− | ofLine(p3,p4); | + | ofLine(p3,p4); //余分な線を消す |
ofSetColor(bcolor); | ofSetColor(bcolor); | ||
− | ofLine(p1,p3); | + | ofLine(p1,p3); //線分の描画 |
ofLine(p3,p5); | ofLine(p3,p5); | ||
ofLine(p5,p4); | ofLine(p5,p4); | ||
ofLine(p4,p2); | ofLine(p4,p2); | ||
− | recurse( n-1, p1, p3); | + | recurse( n-1, p1, p3); //再帰呼び出し n>0 |
recurse( n-1, p3, p5); | recurse( n-1, p3, p5); | ||
recurse( n-1, p5, p4); | recurse( n-1, p5, p4); | ||
58行: | 62行: | ||
</pre> | </pre> | ||
− | + | * ofApp.h | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
<pre> | <pre> | ||
#pragma once | #pragma once | ||
+ | |||
#include "ofMain.h" | #include "ofMain.h" | ||
− | #include | + | #include "Koch.h" |
− | class | + | class ofApp : public ofBaseApp{ |
− | public: | + | |
− | + | public: | |
− | + | void setup(); | |
+ | void update(); | ||
+ | void draw(); | ||
+ | |||
+ | void keyPressed(int key); | ||
+ | void keyReleased(int key); | ||
+ | void mouseMoved(int x, int y ); | ||
+ | void mouseDragged(int x, int y, int button); | ||
+ | void mousePressed(int x, int y, int button); | ||
+ | void mouseReleased(int x, int y, int button); | ||
+ | void windowResized(int w, int h); | ||
+ | void dragEvent(ofDragInfo dragInfo); | ||
+ | void gotMessage(ofMessage msg); | ||
− | + | Koch myKoch; //コッホ図形のクラスインスタンス | |
+ | ofPoint pos1; //初期位置 | ||
+ | ofPoint pos2; | ||
+ | |||
}; | }; | ||
</pre> | </pre> | ||
− | + | * ofApp.cpp | |
− | + | ||
<pre> | <pre> | ||
− | + | #include "ofApp.h" | |
− | + | ||
− | + | ||
− | + | //-------------------------------------------------------------- | |
− | + | void ofApp::setup(){ | |
− | + | ofBackground(0, 0, 0); //背景色の設定 | |
− | + | ofSetFrameRate(30); //フレームレイト設定 | |
− | + | pos1 = ofPoint(ofGetWidth()*1/4, ofGetHeight()*2/4);//初期点の設定 | |
− | + | pos2 = ofPoint(ofGetWidth()*3/4, ofGetHeight()*2/4); | |
− | + | myKoch.generation = 5; //世代の設定 | |
+ | myKoch.bcolor = ofColor(0,255,0); //描画色の設定 | ||
+ | } | ||
+ | |||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::update(){ | ||
+ | } | ||
+ | |||
+ | //-------------------------------------------------------------- | ||
+ | void ofApp::draw(){ | ||
+ | myKoch.recurse(myKoch.generation, pos1, pos2); | ||
+ | } | ||
</pre> | </pre> | ||
2014年12月2日 (火) 06:26時点における版
目次 |
再帰的呼出しによる樹木の描画
- 再帰的( recursive )呼び出しとは,サブルーチンや関数が,自分自身を呼び出すことをいう。樹木は,枝の1つを取り出して拡大しても,元の枝と同じ形(相似形)をしている。これは,同じサブルーチンで枝を描画しているからである。
openFrameworksによる実装
コッホ図形の描画
- Kochクラスの作成
- Koch.h
#pragma once #include "ofMain.h" #include <math.h> class Koch { public: ofColor bcolor; //描画色 int generation; // 世代 void recurse(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド };
- Koch.cpp
#include "Koch.h" void Koch::recurse(int n, ofPoint p1, ofPoint p2){ ofPoint p3,p4,p5; // ofLine(p1,p2); // ofCircle(200,300,100); double s = sin( PI / 3.0); // sin(60) double c = cos( PI / 3.0); // cos(60) if( n > 0 ){ p3 = ( 2 * p1 + p2 ) / 3.0; //内分点 p4 = ( p1 + 2 * p2 ) / 3.0; p5.x = p3.x + ( p4.x - p3.x ) * c + (p4.y - p3.y) * s; //回転移動 p5.y = p3.y - ( p4.x - p3.x ) * s + (p4.y - p3.y) * c; ofSetColor(0,0,0); ofLine(p3,p4); //余分な線を消す ofSetColor(bcolor); ofLine(p1,p3); //線分の描画 ofLine(p3,p5); ofLine(p5,p4); ofLine(p4,p2); recurse( n-1, p1, p3); //再帰呼び出し n>0 recurse( n-1, p3, p5); recurse( n-1, p5, p4); recurse( n-1, p4, p2); } }
- ofApp.h
#pragma once #include "ofMain.h" #include "Koch.h" class ofApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void keyPressed(int key); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(int x, int y, int button); void windowResized(int w, int h); void dragEvent(ofDragInfo dragInfo); void gotMessage(ofMessage msg); Koch myKoch; //コッホ図形のクラスインスタンス ofPoint pos1; //初期位置 ofPoint pos2; };
- ofApp.cpp
#include "ofApp.h" //-------------------------------------------------------------- void ofApp::setup(){ ofBackground(0, 0, 0); //背景色の設定 ofSetFrameRate(30); //フレームレイト設定 pos1 = ofPoint(ofGetWidth()*1/4, ofGetHeight()*2/4);//初期点の設定 pos2 = ofPoint(ofGetWidth()*3/4, ofGetHeight()*2/4); myKoch.generation = 5; //世代の設定 myKoch.bcolor = ofColor(0,255,0); //描画色の設定 } //-------------------------------------------------------------- void ofApp::update(){ } //-------------------------------------------------------------- void ofApp::draw(){ myKoch.recurse(myKoch.generation, pos1, pos2); }
ドラゴン図形の描画
- Taneクラスのメソッド
public void Dragon(int n, double x1, double y1, double x2, double y2, Graphics g, Pen pen) { double x3, y3, x4, y4, x5, y5; if (n > 0) { x3 = 0.5 * ( x1 + x2); y3 = 0.5 * ( y1 + y2); x4 = 0.5 * (x1 + x3 - y1 + y3); y4 = 0.5 * (x1 - x3 + y1 + y3); x5 = 0.5 * (x2 + x3 - y2 + y3); y5 = 0.5 * (x2 - x3 + y2 + y3); // 枝を描画する pen.Color = Color.Black; g.DrawLine(pen, (float)x1, (float)y1, (float)x3, (float)y3); g.DrawLine(pen, (float)x3, (float)y3, (float)x2, (float)y2); pen.Color = Color.Green; g.DrawLine(pen, (float)x1, (float)y1, (float)x4, (float)y4); g.DrawLine(pen, (float)x4, (float)y4, (float)x3, (float)y3); g.DrawLine(pen, (float)x3, (float)y3, (float)x5, (float)y5); g.DrawLine(pen, (float)x5, (float)y5, (float)x2, (float)y2); Dragon(n - 1, x1, y1, x4, y4, g, pen); Dragon(n - 1, x4, y4, x3, y3, g, pen); Dragon(n - 1, x3, y3, x5, y5, g, pen); Dragon(n - 1, x5, y5, x2, y2, g, pen); } }
- ドラゴン図形描画ボタンクリックの中身
Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 7; //子の世代数 double x0 = pictureBox1.Width * 0.1; //開始位置 x座標 double y0 = pictureBox1.Height * 0.5; //開始位置 y座標 double x1 = pictureBox1.Width * 0.9; //終了位置 x座標 double y1 = pictureBox1.Height * 0.5; //終了位置 y座標 tane.Dragon(n, x0, y0, x1, y1, g, pen);
シダ葉の描画
- ドラゴン図形の変化形
- ドラゴン図形で使用した(x1,y1), (x2,y2), (x3,y3), (x4,y4), (x5,y5)を使う
- 直線(x1,y1)-(x2,y2), 直線(x1,y1)-(x4,y4), 直線(x3,y3)-(x5,y5)を基本図形とする。
- Taneクラスのメソッド
public void Fern(int n, double x1, double y1, double x2, double y2, Graphics g, Pen pen) { double x3, y3, x4, y4, x5, y5; if (n > 0) { x3 = ( x1 + x2 ) / 2.0; y3 = ( y1 + y2 ) / 2.0; x4 = ( x1 + x3 - y1 + y3) / 2.0; y4 = ( x1 - x3 + y1 + y3) / 2.0; x5 = ( x2 + x3 - y2 + y3) / 2.0; y5 = ( x2 - x3 + y2 + y3) / 2.0; // 枝を描画する pen.Color = Color.Green; g.DrawLine(pen, (float)x1, (float)y1, (float)x2, (float)y2); g.DrawLine(pen, (float)x1, (float)y1, (float)x3, (float)y3); g.DrawLine(pen, (float)x3, (float)y3, (float)x5, (float)y5); Fern(n - 1, x1, y1, x4, y4, g, pen); Fern(n - 1, x1, y1, x3, y3, g, pen); Fern(n - 1, x3, y3, x2, y2, g, pen); Fern(n - 1, x3, y3, x5, y5, g, pen); } }
- シダ葉描画ボタンクリックの中身
Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 7; //子の世代数 double x0 = pictureBox1.Width * 0.1; //開始位置 x座標 double y0 = pictureBox1.Height * 0.5; //開始位置 y座標 double x1 = pictureBox1.Width * 0.9; //終了位置 x座標 double y1 = pictureBox1.Height * 0.5; //終了位置 y座標 tane.Fern(n, x0, y0, x1, y1, g, pen);
Cカーブの描画
- Taneクラスのメソッド
public void Ccurve(int n, double x1, double y1, double x2, double y2, Graphics g, Pen pen) { double x3, y3; if (n > 0) { x3 = 0.5 * (x1 + x2 - y1 + y2); y3 = 0.5 * (x1 - x2 + y1 + y2); // 枝を描画する pen.Color = Color.Black; g.DrawLine(pen, (float)x1, (float)y1, (float)x2, (float)y2); pen.Color = Color.Green; g.DrawLine(pen, (float)x1, (float)y1, (float)x3, (float)y3); g.DrawLine(pen, (float)x3, (float)y3, (float)x2, (float)y2); Ccurve(n - 1, x1, y1, x3, y3, g, pen); Ccurve(n - 1, x3, y3, x2, y2, g, pen); } }
- Cカーブ画ボタンクリックの中身
Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 12; //子の世代数 double x0 = pictureBox1.Width * 0.25; //開始位置 x座標 double y0 = pictureBox1.Height * 0.75; //開始位置 y座標 double x1 = pictureBox1.Width * 0.75; //終了位置 x座標 double y1 = pictureBox1.Height * 0.75; //終了位置 y座標 tane.Ccurve(n, x0, y0, x1, y1, g, pen);
樹木の描画
- Taneクラスのメソッド
線分(x1,y1)-(x2,y2)が与えられたら、(x2,y2)の先端に(x3,y3), (x4,y4)を取り、線分(x2,y2)-(x3,y3)と線分(x2,y2)-(x4,y4)を描画する。
class Tane { public void Eda(int n, double x1, double y1, double x2, double y2, double angle, Graphics g, Pen pen) { double x3, y3, x4, y4; double s = Math.Sin(angle * Math.PI / 180.0); double c = Math.Cos(angle * Math.PI / 180.0); double dx = 0.7 * (x2 - x1); double dy = 0.7 * (y2 - y1); if (n > 0) { x3 = x2 + dx * c - dy * s; y3 = y2 + dx * s + dy * c; x4 = x2 + dx * c + dy * s; y4 = y2 - dx * s + dy * c; // 枝を描画する g.DrawLine(pen, (float)x1, (float)y1, (float)x2, (float)y2); g.DrawLine(pen, (float)x2, (float)y2, (float)x3, (float)y3); g.DrawLine(pen, (float)x2, (float)y2, (float)x4, (float)y4); //子の再起呼び出し Eda(n - 1, x2, y2, x3, y3, angle, g, pen); Eda(n - 1, x2, y2, x4, y4, angle, g, pen); } } }
- 樹木描画ボタンクリックの中身
Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 10; //枝の世代数 double x0 = pictureBox1.Width / 2; //開始位置 x座標 double y0 = pictureBox1.Height; //開始位置 y座標 double x1 = pictureBox1.Width / 2; //開始位置 x座標 double y1 = pictureBox1.Height * 0.8; //開始位置 y座標 double angle = 30.0; //子供の枝の角度の差分 tane.Eda(n, x0, y0, x1, y1, angle, g, pen);
内分点と回転によるカスタムジェネレータの描画
- Taneクラスのメソッド
public void Gene01(int n, double x1, double y1, double x2, double y2, Graphics g, Pen pen) { double x3, y3, x4, y4; double p = 2.0; double q = 3.0; double th = -30.0; double s = Math.Sin(th * Math.PI / 180.0); double c = Math.Cos(th * Math.PI / 180.0); if (n > 0) { x3 = (q * x1 + p * x2) / (p + q); y3 = (q * y1 + p * y2) / (p + q); x4 = x1 + (x3 - x1) * c - (y3 - y1) * s; y4 = y1 + (x3 - x1) * s + (y3 - y1) * c; pen.Color = Color.Yellow; g.DrawLine(pen, (float)x1, (float)y1, (float)x2, (float)y2); g.DrawLine(pen, (float)x1, (float)y1, (float)x4, (float)y4); Gene01(n - 1, x1, y1, x3, y3, g, pen); Gene01(n - 1, x1, y1, x4, y4, g, pen); Gene01(n - 1, x3, y3, x2, y2, g, pen); } }
- カスタムジェネレータ描画ボタンクリックの中身
Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 7; //子の世代数 double x0 = pictureBox1.Width * 0.1; //開始位置 x座標 double y0 = pictureBox1.Height * 0.5; //開始位置 y座標 double x1 = pictureBox1.Width * 0.9; //終了位置 x座標 double y1 = pictureBox1.Height * 0.5; //終了位置 y座標 tane.Gene01(n, x0, y0, x1, y1, g, pen);
- マウスドラッグで始点と終点を決めて描く
- プロパティウィンドウにイベント(稲妻のアイコン)のリストを表示させ、MouseDownイベントをダブルクリックすると、MouseDownのメソッドが自動生成されます。
- label1,label2に始点の座標を、label3, label4に終点の座標を入れる
private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { label1.Text = e.X.ToString(); label2.Text = e.Y.ToString(); } private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); int n = 7; double x0 = double.Parse(label1.Text); double y0 = double.Parse(label2.Text); double x1 = double.Parse(label3.Text); double y1 = double.Parse(label4.Text); tane.Gene01(n, x0, y0, x1, y1, g, pen); } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { label3.Text = e.X.ToString(); label4.Text = e.Y.ToString(); }
p,q,thをクラスのメンバー変数にして、外部からランダムに与える。
class Tane { public double p { get; set; } public double q { get; set; } public double th { get; set; } public void Gene01(int n, double x1, double y1, double x2, double y2, Graphics g, Pen pen) { double x3, y3, x4, y4; double s = Math.Sin(th * Math.PI / 180.0); double c = Math.Cos(th * Math.PI / 180.0); if (n > 0) { x3 = (q * x1 + p * x2) / (p + q); y3 = (q * y1 + p * y2) / (p + q); x4 = x1 + (x3 - x1) * c - (y3 - y1) * s; y4 = y1 + (x3 - x1) * s + (y3 - y1) * c; pen.Color = Color.Yellow; g.DrawLine(pen, (float)x1, (float)y1, (float)x2, (float)y2); g.DrawLine(pen, (float)x1, (float)y1, (float)x4, (float)y4); Gene01(n - 1, x1, y1, x3, y3, g, pen); Gene01(n - 1, x1, y1, x4, y4, g, pen); Gene01(n - 1, x3, y3, x2, y2, g, pen); } } }
private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 2); Tane tane = new Tane(); Random rnd = new Random(); int n = 7; double x0 = double.Parse(label1.Text); double y0 = double.Parse(label2.Text); double x1 = double.Parse(label3.Text); double y1 = double.Parse(label4.Text); tane.p = rnd.Next(2, 6); tane.q = rnd.Next(3, 7); tane.th = rnd.Next(10, 90); tane.Gene01(n, x0, y0, x1, y1, g, pen); }
タイマーを使用した樹木のアニメーション
private void button3_Click(object sender, EventArgs e) { timer1.Enabled = true; timer1.Start(); } private void button4_Click(object sender, EventArgs e) { timer1.Stop(); timer1.Enabled = false; } public int cnt; //タイマー用カウンタ private void timer1_Tick(object sender, EventArgs e) { Graphics g = pictureBox1.CreateGraphics(); Pen pen = new Pen(Color.Green, 1); Tane tane = new Tane(); g.Clear(Color.Black); cnt++; //タイマー用カウンタのカウントアップ int n = 10; //枝の世代数 double x0 = pictureBox1.Width / 2; //開始位置 x座標 double y0 = pictureBox1.Height; //開始位置 y座標 double x1 = pictureBox1.Width / 2; //開始位置 x座標 double y1 = pictureBox1.Height * 0.9 - cnt * 2; //開始位置 y座標 double angle = 30.0; //子供の枝の角度の変化の差分 double a_rate = angle + cnt * 2; tane.Eda(n, x0, y0, x1, y1, a_rate, g, pen); }