★galapagosで一筆書きのサンプルを作ってみた
以前から気になっていた、Grasshopperに存在するgalapagosを使ってみたくなり、自分なりに調べてサンプルを作ってみました。
galapagosは遺伝的アルゴリズムをGrasshopper上で再現するためのコンポーネントです。
遺伝的アルゴリズムとは、簡単に言うと、遺伝子の組み合わせを何通りか作成し、その中の遺伝子を組み替えたり差し替えたりした中から最も優秀な(環境に強くて生き残る)組み合わせを見つけるというもの。
その考え方をプログラミングに応用すると、例えば乗換案内アプリのように、最も運賃の安いルート、最も移動距離の短いルートなど、条件に合わせた最適な答えが導けます。
※巡回セールスマン問題というのがそれに相当するので気になる方は調べてみてください。
組み合わせを総当たりで計算すると膨大な時間がかかりますが、遺伝子(例えば経由地など)を部分的に差し替えたり切り捨てることで範囲を絞りながら最適値を求めていくので時間が短くて済むというメリットがありますが、最初の組み合わせが悪いと永遠に最適解が求まらないというデメリットもあるみたいです。
今回のサンプルは、galapagosで立方体の中にあるランダムな点を結んだpolylineカーブの最短距離のときの一筆書き順路を求めます。
ビジュアル的な勉強も兼ねて、processingで出力してみました。
Step.1 適当な大きさの立方体を作る
Step.2 ランダムな点を作る
・Step.1で作成した立方体の中に、Populate 3Dコンポーネントを使ってランダムな点を作る。
・個数はお好みで。
・並行して、一筆書きの開始点と終了点を求めます。Deconstruct Brepコンポーネントを使ってボックスの頂点を求め、List Itemで指定した点をそれぞれ設定します。
Step.3 シャッフルの設定
・Step.2で求めたランダムな点を、Jitterコンポーネントで中の順番をシャッフルします。
※パラメータは後でgalapagos側で変更されるので適当な数字でOKです。
Step.4 点のリスト一本化と座標値の分解
・Step.3で求めたJitterコンポーネントの出力に、Step.2で求めた開始点と終了点をInsert Itemsコンポーネントを使って0番目と末尾(-1番目)に挿入して、点群を一つのリストにまとめます。
・Processing側に、まとめた点の座標値を送るので、X,Y,Zの各値に分解します。
Step.5 Galapagosの設定
・galapagosコンポーネントを上図のようにつなぎます。
・Fitness(評価関数)とは、求めたい最適な答えのことです。今回はpolylineカーブの長さの最短値を求めたいので、Step.4でまとめた点群をpolylineコンポーネントでカーブを作り、Lengthコンポーネントの出力をFitnessにつなぎます。
・Genomeとは組み替える遺伝子のことです。今回は、点群の順序を入れ替えて最短距離を求めますので、JitterコンポーネントのJとSにつないでいるスライダーをつなぎます。
Step.6 パラメータの設定
・続いて、計算の際のパラメータの設定です。galapagosのアイコンをダブルクリックすると上図のような画面が開きます。
・Grasshopperの遺伝的アルゴリズムは、面積や長さなどのパラメータが最小、あるいは最大になるときのgenomeの組み合わせを見つけるのが目的です。今回は最小の長さを求めたいのでMinimizeを選択します。
・残りの設定はデフォルトでOK。
Step.7 解析を実行
・続いてSolverタブを開いてStart Solverボタンを押すと解析が始まります。
Step.8 結果
・画面をOKボタンを押して閉じると結果は上の図のような感じです。
Step.9 Processingに出力
・Processingには点の座標値のみを送信し、残りはProcessing内で描画します。
・galapagosで求める座標がリアルタイムに送られます。
・X,Y,Z座標をそれぞれxでつないたテキストをUDP Senderコンポーネントにつなぎます。
・ポート番号は4000とし、Processingも同じにします。
以下、processingのソースコードです↓
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------- | |
// data transfar from Grasshopper to Processing | |
// Ryo Yamada | |
// date: 2017.12.12 | |
// | |
//------------------------------------------------ | |
import processing.opengl.*; | |
import hypermedia.net.*; | |
import peasy.*; | |
PeasyCam cam; //カメラ操作のためのクラスのインスタンスを宣言 | |
UDP udp; //通信のためのクラスのインスタンスを宣言 | |
DRAW d;//DRAWクラスのインスタンスを宣言 | |
PImage img; | |
void setup() { | |
size(1280,960, P3D);//画面サイズ | |
smooth();//アンチエイリアス | |
udp = new UDP( this, 4000 );//4000番ポートで待ち状態 Grasshopper側もこれに合わせる | |
//udp.log( true ); // <-- printout the connection activity | |
udp.listen( true ); | |
d = new DRAW();//Meshクラスのインスタンスを作成 | |
cam = new PeasyCam(this, 100);// カメラワーク操作オブジェクト。親オブジェクトと距離?を指定 | |
cam.setMinimumDistance(10);// カメラの最小距離定義 | |
cam.setMaximumDistance(10000);// カメラの最大距離定義 | |
cam.setDistance(0); | |
frameRate(60);//フレームレートの設定 | |
hint(DISABLE_DEPTH_TEST);//zテストを無効化 | |
blendMode(ADD);//加算合成 | |
imageMode(CENTER); | |
img = createLight(random(0.5,0.8),random(0.5,0.8),random(0.5,0.8));//画像の生成 | |
} | |
void draw() { | |
background(0,15,30);//背景色(1回ずつリセット) | |
/*光源設定*/ | |
//ambientLight(255, 255, 255); //環境光(方向を持たないので陰影は付かない) R,G,B | |
//directionalLight(204,204,204,0,0,-1); //平行光(指向性がある)R,G,B,vx,vy,vz | |
//lightSpecular(255,255,255);//ハイライトの色を設定 | |
//pointLight(63, 127, 255, 0,0,-1400); //点光源 | |
//spotLight(100, 100, 100, 0,0,-1400, 0, 0, -1, PI, 2); //スポットライト | |
translate(0,0,-1400);//初期の位置 | |
rotateY(frameCount * 0.005);//Y軸周りに回転 | |
/*BOXの描画*/ | |
stroke(20); | |
strokeWeight(1);//線の幅 | |
noFill(); | |
box(800); | |
/*円と線の描画*/ | |
d.drawVertices();//描画のメソッド | |
} | |
void receive( byte[] data,String ip, int port){ // <-- extended handler | |
//int time = millis(); | |
String message = new String( data ); | |
//println("receive: \""+message+"\"from"+ip+"on port "+port); | |
d.prepareData(message);//受信データを整理するメソッド | |
} | |
/*マウスクリックで発光色を変更*/ | |
void mousePressed(){ | |
img = createLight(random(0.5, 0.8), random(0.5, 0.8), random(0.5, 0.8));//画像の生成(色はランダム) | |
} | |
/*データを仕分けして描画するクラス*/ | |
public class DRAW{ | |
DRAW(){} //コンストラクタ | |
ArrayList<Vertex> v = new ArrayList<Vertex>(50); | |
/*受信データを仕分けするメソッド*/ | |
public void prepareData(String udp_data){ | |
ArrayList<Vertex> v_tmp = new ArrayList<Vertex>(50);//受信1回につき仮に格納するアレイリストのオブジェクトを作成 | |
String lines[] = split(udp_data, '\n');//改行コードで分離して、lines配列にデータを格納 | |
String tmpLine = ""; | |
for(int i = 0; i < lines.length; i++){ | |
tmpLine = lines[i]; | |
String coords[] = tmpLine.split("x");//xで分離 | |
//v_tmp.add(new Conversion(float(coords[0]),float (coords[1]),float (coords[2]),float(coords[3])));//v_temp配列にxで区切った数値を格納 | |
Vertex vert = new Vertex(float(coords[0]), float(coords[1]),float(coords[2])); | |
v_tmp.add(vert);//v_temp配列にxで区切った数値を格納 | |
} | |
if( v_tmp.size() == 0){v_tmp = null;} | |
v = v_tmp; | |
} | |
/*線を描くメソッド*/ | |
void drawLine(){ | |
for(int i=0;i<v.size()-1;i++){ //ArrayListの中身を読み込んでそれぞれを計算する | |
line(v.get(i).x,v.get(i).y,v.get(i).z,v.get(i+1).x,v.get(i+1).y,v.get(i+1).z); | |
} | |
} | |
/*描画するメソッド*/ | |
public void drawVertices(){ | |
if( v == null) return; | |
for( Vertex vt : v ){ | |
noFill(); | |
/*色の設定*/ | |
//specular(125,125,125);//オブジェクトの色を設定 | |
//emissive(0,26,51); | |
//shininess(10.0);//オブジェクトの光沢を設定 | |
//lightSpecular(255, 255, 255); | |
pushMatrix(); | |
translate(vt.x,vt.y,vt.z); | |
//sphere(20); | |
rotateY(-frameCount * 0.005);//Y軸周りに逆回転 | |
image(img,0,0,300,300);//画像を表示するメソッド ⇒x,y,width,height | |
popMatrix(); | |
} | |
stroke(125,125,125); | |
strokeWeight(1); | |
drawLine(); | |
} | |
} | |
/*アレイリストで座標値を格納するためのユーザー定義のクラス*/ | |
class Vertex{ | |
float x, y, z; | |
Vertex(float x, float y, float z){ //コンストラクタ | |
//Conversion(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float r1, float r2, float r3){ //コンストラクタ | |
this.x = x;//自インスタンスの参照 | |
this.y = y; | |
this.z = z; | |
} | |
} | |
/*光る球体の画像を生成するメソッド*/ | |
PImage createLight(float rPower,float gPower,float bPower){ | |
int side = 100;//1辺の大きさ | |
float center = side / 2.0;//中心 | |
PImage img = createImage(side,side,RGB);//画像のバッファを生成 | |
/*画像のピクセル一つ一つに色を設定*/ | |
for(int y = 0 ; y < side ; y++){ | |
for(int x = 0 ; x < side ; x++){ | |
float distance = (sq(center - x) + sq(center - y)) / 50.0;//画像中心からの距離 | |
int r = int( (255 * rPower) / distance ); | |
int g = int( (255 * gPower) / distance ); | |
int b = int( (255 * bPower) / distance ); | |
img.pixels[x + y * side] = color(r, g, b); | |
} | |
} | |
return img; | |
} |
0 件のコメント:
コメントを投稿