自己相似形
提供:kuhalaboWiki
(版間での差分)
(→コッホ図形の描画) |
(→openFrameworksによる実装) |
||
| 5行: | 5行: | ||
*例:実行ファイル,ソース,Taneクラス | *例:実行ファイル,ソース,Taneクラス | ||
| − | + | === コッホ図形の描画 === | |
| − | + | ||
[[ファイル:Koch.jpg]] | [[ファイル:Koch.jpg]] | ||
[[ファイル:Koch2.jpg]] | [[ファイル:Koch2.jpg]] | ||
| − | ; | + | ;Recursクラスの作成 |
| − | * | + | * Recurs.h |
<pre> | <pre> | ||
#pragma once | #pragma once | ||
#include "ofMain.h" | #include "ofMain.h" | ||
| − | #include <math.h> | + | #include <math.h> //算術関数の読み込み |
| − | class | + | class Recurs { |
public: | public: | ||
ofColor bcolor; //描画色 | ofColor bcolor; //描画色 | ||
int generation; // 世代 | int generation; // 世代 | ||
| − | void | + | void koch(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド |
}; | }; | ||
</pre> | </pre> | ||
| − | * | + | * Recurs.cpp |
<pre> | <pre> | ||
| − | #include " | + | #include "Recurs.h" |
| − | void | + | void Recurs::koch(int n, ofPoint p1, ofPoint p2){ |
ofPoint p3,p4,p5; | ofPoint p3,p4,p5; | ||
| − | |||
| − | |||
| − | |||
| − | |||
double s = sin( PI / 3.0); // sin(60) | double s = sin( PI / 3.0); // sin(60) | ||
double c = cos( PI / 3.0); // cos(60) | double c = cos( PI / 3.0); // cos(60) | ||
| 54行: | 49行: | ||
ofLine(p4,p2); | ofLine(p4,p2); | ||
| − | + | koch( n-1, p1, p3); //再帰呼び出し n>0 | |
| − | + | koch( n-1, p3, p5); | |
| − | + | koch( n-1, p5, p4); | |
| − | + | koch( n-1, p4, p2); | |
} | } | ||
} | } | ||
| 67行: | 62行: | ||
#include "ofMain.h" | #include "ofMain.h" | ||
| − | #include " | + | #include "Recurs.h" |
class ofApp : public ofBaseApp{ | class ofApp : public ofBaseApp{ | ||
| 86行: | 81行: | ||
void gotMessage(ofMessage msg); | void gotMessage(ofMessage msg); | ||
| − | + | Recurs myKoch; | |
| − | ofPoint pos1; | + | ofPoint pos1; |
ofPoint pos2; | ofPoint pos2; | ||
| 99行: | 94行: | ||
//-------------------------------------------------------------- | //-------------------------------------------------------------- | ||
void ofApp::setup(){ | void ofApp::setup(){ | ||
| + | |||
ofBackground(0, 0, 0); //背景色の設定 | ofBackground(0, 0, 0); //背景色の設定 | ||
ofSetFrameRate(30); //フレームレイト設定 | ofSetFrameRate(30); //フレームレイト設定 | ||
| 113行: | 109行: | ||
//-------------------------------------------------------------- | //-------------------------------------------------------------- | ||
void ofApp::draw(){ | void ofApp::draw(){ | ||
| − | myKoch. | + | myKoch.koch(myKoch.generation, pos1, pos2); |
} | } | ||
</pre> | </pre> | ||
| − | ==== | + | *update()で動かしてみる |
| + | <pre> | ||
| + | //-------------------------------------------------------------- | ||
| + | void ofApp::update(){ | ||
| + | // pos1 = pos1 + (1,1); | ||
| + | // pos2 = pos2 + (-1,-1); | ||
| + | } | ||
| + | </pre> | ||
| + | |||
| + | |||
| + | === ドラゴン図形の描画 === | ||
[[ファイル:Dragon.jpg]] | [[ファイル:Dragon.jpg]] | ||
| 154行: | 160行: | ||
} | } | ||
</pre> | </pre> | ||
| + | |||
| + | * Dragon()メソッドの追加 | ||
| + | <pre> | ||
| + | |||
| + | void Koch::dragon(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); | ||
| + | } | ||
| + | } | ||
| + | </pre> | ||
| + | |||
| + | |||
| + | |||
;ドラゴン図形描画ボタンクリックの中身 | ;ドラゴン図形描画ボタンクリックの中身 | ||
| 171行: | 213行: | ||
</pre> | </pre> | ||
| − | + | === シダ葉の描画 === | |
;ドラゴン図形の変化形 | ;ドラゴン図形の変化形 | ||
* ドラゴン図形で使用した(x1,y1), (x2,y2), (x3,y3), (x4,y4), (x5,y5)を使う | * ドラゴン図形で使用した(x1,y1), (x2,y2), (x3,y3), (x4,y4), (x5,y5)を使う | ||
2014年12月2日 (火) 06:39時点における版
目次 |
再帰的呼出しによる樹木の描画
- 再帰的( recursive )呼び出しとは,サブルーチンや関数が,自分自身を呼び出すことをいう。樹木は,枝の1つを取り出して拡大しても,元の枝と同じ形(相似形)をしている。これは,同じサブルーチンで枝を描画しているからである。
コッホ図形の描画
- Recursクラスの作成
- Recurs.h
#pragma once
#include "ofMain.h"
#include <math.h> //算術関数の読み込み
class Recurs {
public:
ofColor bcolor; //描画色
int generation; // 世代
void koch(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド
};
- Recurs.cpp
#include "Recurs.h"
void Recurs::koch(int n, ofPoint p1, ofPoint p2){
ofPoint p3,p4,p5;
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);
koch( n-1, p1, p3); //再帰呼び出し n>0
koch( n-1, p3, p5);
koch( n-1, p5, p4);
koch( n-1, p4, p2);
}
}
- ofApp.h
#pragma once
#include "ofMain.h"
#include "Recurs.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);
Recurs 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.koch(myKoch.generation, pos1, pos2);
}
- update()で動かしてみる
//--------------------------------------------------------------
void ofApp::update(){
// pos1 = pos1 + (1,1);
// pos2 = pos2 + (-1,-1);
}
ドラゴン図形の描画
- 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);
}
}
- Dragon()メソッドの追加
void Koch::dragon(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);
}
}
- ドラゴン図形描画ボタンクリックの中身
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);
}






