自己相似形

提供:kuhalaboWiki
移動: 案内, 検索

目次

openFrameworksのサンプル

再帰的呼出しによる樹木の描画

  • 再帰的( recursive )呼び出しとは,サブルーチンや関数が,自分自身を呼び出すことをいう。樹木は,枝の1つを取り出して拡大しても,元の枝と同じ形(相似形)をしている。これは,同じサブルーチンで枝を描画しているからである。
  • 再帰的呼出し的なCollaborate Flash Animation:zoomquilt (swf)
  • 例:実行ファイル,ソース,Taneクラス

コッホ図形の描画

Koch.jpg Koch2.jpg

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"

//三角関数の値をstaticグローバル定数として定義
static const double S60 = sin( PI / 3.0); // sin(60)
static const double C60 = cos( PI / 3.0); // cos(60)

void Recurs::koch(int n, ofPoint p1, ofPoint p2){
	ofPoint p3,p4,p5;

	if( n > 0 ){
		p3 = ( 2 * p1 + p2 ) / 3.0; //内分点
		p4 = ( p1 + 2 * p2 ) / 3.0;
		p5.x = p3.x + ( p4.x - p3.x ) * C60 + (p4.y - p3.y) * S60; //回転移動
		p5.y = p3.y - ( p4.x - p3.x ) * S60 + (p4.y - p3.y) * C60;
		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);
}

動かしてみる

  • Recurs.h
    • 現在位置と速度のプロパティを追加
    • 現在位置と速度を更新するupdate()メソッドの追加
class Recurs {
public:
	ofColor bcolor;  //描画色
	int generation;  // 世代
//ここから追加
	ofPoint pos1; //初期位置
	ofPoint pos2;
	ofPoint vel1; //初期速度
	ofPoint vel2;
//ここまで追加

	Recurs();
	void update(); // 追加分
	void koch(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド
};
  • Recurs.cpp
    • Recurs()で初期の位置と速度を設定
    • update()で位置と速度を更新
#include "Recurs.h"

//三角関数の値をstaticグローバル定数として定義
static const double S60 = sin( PI / 3.0); // sin(60)
static const double C60 = cos( PI / 3.0); // cos(60)

Recurs::Recurs(){
	pos1 = ofPoint(ofGetWidth()*1/4, ofGetHeight()*2/4);
	vel1 = ofPoint(ofRandom(-5,5),ofRandom(-3,3));
	pos2 = ofPoint(ofGetWidth()*3/4, ofGetHeight()*2/4);
	vel2 = ofPoint(ofRandom(-5,5),ofRandom(-3,3));
	generation = 7; //世代の設定
	bcolor = ofColor(0,255,0); //描画色の設定
}

void Recurs::update(){
	pos1 += vel1;
	pos2 += vel2;
	if(pos1.x > ofGetWidth() || pos1.x < 0){
		vel1.x *= -1;
	}
	if(pos1.y > ofGetHeight() || pos1.y < 0){
		vel1.y *= -1;
	}
	if(pos2.x > ofGetWidth() || pos2.x < 0){
		vel2.x *= -1;
	}
	if(pos2.y > ofGetHeight() || pos2.y < 0){
		vel2.y *= -1;
	}
}

void Recurs::koch(int n, ofPoint p1, ofPoint p2){
	ofPoint p3,p4,p5;

	if( n > 0 ){
		p3 = ( 2 * p1 + p2 ) / 3.0; //内分点
		p4 = ( p1 + 2 * p2 ) / 3.0;
		p5.x = p3.x + ( p4.x - p3.x ) * C60 + (p4.y - p3.y) * S60; //回転移動
		p5.y = p3.y - ( p4.x - p3.x ) * S60 + (p4.y - p3.y) * C60;
		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.cpp
    • update()でmyKoch.update()を呼び出し、位置、速度を更新
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

	ofBackground(0, 0, 0); //背景色の設定
	ofSetFrameRate(30); //フレームレイト設定
}

//--------------------------------------------------------------
void ofApp::update(){
	myKoch.update();
}

//--------------------------------------------------------------
void ofApp::draw(){
	myKoch.koch(myKoch.generation, myKoch.pos1, myKoch.pos2);
}

複数個の図形を配置

  • ofApp.h
    • myKochとmyKoch2を作る
#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;
		Recurs myKoch2;
};
  • ofApp.cpp
    • update()で位置と速度を更新
    • myKoch2はmyKochとミラー配置
//--------------------------------------------------------------
void ofApp::update(){
	myKoch.update();
	myKoch2.pos1 = myKoch.pos2;
	myKoch2.pos2 = myKoch.pos1;
}

//--------------------------------------------------------------
void ofApp::draw(){
	myKoch.koch(myKoch.generation, myKoch.pos1, myKoch.pos2);
	myKoch2.koch(myKoch2.generation, myKoch2.pos1, myKoch2.pos2);
}

ドラゴン図形の描画

Dragon.jpg Dragon2.jpg

dragon()メソッドの追加

Recurs.cppに以下のメソッドを追加する。

void Recurs::dragon(int n, ofPoint p1, ofPoint p2){
	ofPoint p3,p4,p5;

	if( n > 0 ){
		p3 = ( p1 + p2 ) / 2.0; //内分点
		p4.x = ( p1.x + p3.x - p1.y + p3.y ) / 2.0;
		p4.y = ( p1.x - p3.x + p1.y + p3.y ) / 2.0;
		p5.x = ( p2.x + p3.x - p2.y + p3.y ) / 2.0;
		p5.y = ( p2.x - p3.x + p2.y + p3.y ) / 2.0;
		ofSetColor(0,0,0);
		ofLine(p1,p3); //余分な線を消す
		ofLine(p3,p2); //余分な線を消す
		ofSetColor(bcolor);
		ofLine(p1,p4); //線分の描画
		ofLine(p4,p3);
		ofLine(p3,p5);
		ofLine(p5,p2);

		dragon( n-1, p1, p4); //再帰呼び出し n>0
		dragon( n-1, p4, p3); //再帰呼び出し n>0
		dragon( n-1, p3, p5); //再帰呼び出し n>0
		dragon( n-1, p5, p2); //再帰呼び出し n>0
	}
}

シダ葉の描画

ドラゴン図形の変化形
  • ドラゴン図形で使用した(x1,y1), (x2,y2), (x3,y3), (x4,y4), (x5,y5)を使う
  • 直線(x1,y1)-(x2,y2), 直線(x1,y1)-(x4,y4), 直線(x3,y3)-(x5,y5)を基本図形とする。

Sida2.JPG

sida()メソッドの追加
void Recurs::shida(int n, ofPoint p1, ofPoint p2){
	ofPoint p3,p4,p5;

	if( n > 0 ){
		p3 = ( p1 + p2 ) / 2.0; //内分点
		p4.x = ( p1.x + p3.x - p1.y + p3.y ) / 2.0;
		p4.y = ( p1.x - p3.x + p1.y + p3.y ) / 2.0;
		p5.x = ( p2.x + p3.x - p2.y + p3.y ) / 2.0;
		p5.y = ( p2.x - p3.x + p2.y + p3.y ) / 2.0;
		ofSetColor(0,0,0);
		ofLine(p4,p5); //余分な線を消す
		ofSetColor(bcolor);
		ofLine(p1,p2); //線分の描画
		ofLine(p1,p4);
		ofLine(p3,p5);

		shida( n-1, p1, p3); //再帰呼び出し n>0
		shida( n-1, p3, p2); //再帰呼び出し n>0
		shida( n-1, p1, p4); //再帰呼び出し n>0
		shida( n-1, p3, p5); //再帰呼び出し n>0
	}
}

Cカーブの描画

Ccurve.jpg Ccurve2.jpg

ccurve()メソッドの追加
void Recurs::ccurve(int n, ofPoint p1, ofPoint p2){
	ofPoint p3;

	if( n > 0 ){
		p3.x = ( p1.x + p2.x - p1.y + p2.y ) / 2.0;
		p3.y = ( p1.x - p2.x + p1.y + p2.y ) / 2.0;
		ofSetColor(0,0,0);
		ofLine(p1,p2); //余分な線を消す
		ofSetColor(bcolor);
		ofLine(p1,p3); //線分の描画
		ofLine(p3,p2);

		ccurve( n-1, p1, p3); //再帰呼び出し n>0
		ccurve( n-1, p3, p2); //再帰呼び出し n>0
	}
}

樹木の描画

Bintree.jpg Bintree2.JPG

線分(x1,y1)-(x2,y2)が与えられたら、(x2,y2)の先端に(x3,y3), (x4,y4)を取り、線分(x2,y2)-(x3,y3)と線分(x2,y2)-(x4,y4)を描画する。


  • Recurs.cppにjumoku()メソッドを追加
void Recurs::jumoku(int n, ofPoint p1, ofPoint p2){
	double s = sin( theta * PI / 180.0); // sin(theta)
	double c = cos( theta * PI / 180.0); // cos(theta)

	ofPoint p3,p4,dp;

	dp = 0.7 * (p2 - p1); // 枝の収縮率を0.7とする

	if( n > 0 ){
		p3.x = p2.x + c * dp.x - s * dp.y;
		p3.y = p2.y + s * dp.x + c * dp.y;
		p4.x = p2.x + c * dp.x + s * dp.y;
		p4.y = p2.y - s * dp.x + c * dp.y;

		ofSetColor(bcolor);
		ofLine(p1,p2); //線分の描画
		ofLine(p2,p3);
		ofLine(p2,p4);

		jumoku( n-1, p2, p3); //再帰呼び出し n>0
		jumoku( n-1, p2, p4); //再帰呼び出し n>0
	}
}

内分点と回転によるカスタムジェネレータの描画

Gene01.JPG Gene012.JPG

  • Recurs.hにtheta, p, q, gene()を追加する。
class Recurs {
public:
	ofColor bcolor;  //描画色
	int generation;  // 世代
	ofPoint pos1;
	ofPoint pos2;
	ofPoint vel1;
	ofPoint vel2;
	double theta; //樹木の枝の広がりの角度
	double p; //内分点の比
	double q; //内分点の比

	void gene(int n, ofPoint p1, ofPoint p2); //再帰的呼び出しメソッド カスタム
  • Recurs.cppにgene()を追加する。
Recurs::Recurs(){

	theta = 30;
	p = 2.0;
	q = 3.0;
}

void Recurs::gene(int n, ofPoint p1, ofPoint p2){
	ofPoint p3,p4,dp;

	double s = sin( theta * PI / 180.0); // sin(theta)
	double c = cos( theta * PI / 180.0); // cos(theta)

	if( n > 0 ){
		p3 = ( q * p1 + p * p2 ) / ( p + q ); //内分点
		dp = p3 - p1;
		p4.x = p1.x + c * dp.x - s * dp.y;
		p4.y = p1.y + s * dp.x + c * dp.y;

		ofSetColor(bcolor);
		ofLine(p1,p2); //線分の描画

		gene( n-1, p1, p3); //再帰呼び出し n>0
		gene( n-1, p1, p4); //再帰呼び出し n>0
		gene( n-1, p3, p2); //再帰呼び出し n>0
	}
}

カスタムジェネレータの位置とtheta, p, qを動かしてみる

  • Recurs.cppのupdate()にp,q,thetaの変化を追加する。
void Recurs::update(){
	pos1 += vel1;
	pos2 += vel2;
	if(pos1.x > ofGetWidth() || pos1.x < 0){
		vel1.x *= -1;
	}
	if(pos1.y > ofGetHeight() || pos1.y < 0){
		vel1.y *= -1;
	}
	if(pos2.x > ofGetWidth() || pos2.x < 0){
		vel2.x *= -1;
	}
	if(pos2.y > ofGetHeight() || pos2.y < 0){
		vel2.y *= -1;
	}
	p += 0.1;
	q += 0.01;
	theta += 0.7;
}
  • ofApp()のupdate()でmyGeneを更新する。
void ofApp::update(){
	myGene.update();
}

参考

ジェネラティブアート論

個人用ツール
名前空間

変種
操作
案内
ツールボックス