Illustratorでジェネラティブアートを作ってみる2

最終更新日

今回は定番のフラクタルツリーです。幹の部分から一定のルールで分岐を繰り返していくものです。子が親の形をそのまま継承した状態で繰り返していくフラクタルな描画の定番と言えるものです。
これも再帰処理を繰り返すことで描画が進む類のコードとなります。
まずは基本型から。

var doc = app.activeDocument;
var lyr = doc.layers.add();
lyr.name = "Fractal Tree";
var gy = new GrayColor();
gy.gray = 70;
drawFractalTree(lyr, 300, 500, 100, -Math.PI/2, 10);

function drawFractalTree(ly, x1, y1, length, angle, depth) 
{
  if (depth === 0) return; 
  var x2 = x1 + length * Math.cos(angle);
  var y2 = y1 + length * Math.sin(angle);
  var branch = ly.pathItems.add();
  branch.setEntirePath([[x1, -y1], [x2, -y2]]);
  branch.stroked = true;
  branch.strokeWidth = depth;
  branch.strokeColor = gy;
   
  var newLength = length * 0.7;
  var angleVariation = Math.PI / 6;
 
  drawFractalTree(ly, x2, y2, newLength, angle - angleVariation, depth-1);
  drawFractalTree(ly, x2, y2, newLength, angle + angleVariation, depth-1);
}

こ〜んなコードです。量的には少ない目で読み易くはあります。これを実行すると…

よく見るやつが出来上がりました。分岐の角度が30°長さが0.7倍、太さを-1ptのルールで10回繰り返した結果となります。
この基本形にPerlinノイズを利用して再帰時に長さと角度を変化させてみます。

var doc = app.activeDocument;
var layer = doc.layers.add();
layer.name = "Fractal Tree";
var gy = new GrayColor();
gy.gray = 70;
drawFractalTree(layer, 300, 500, 100, -Math.PI/2, 8);

function noise(x)
{
  return (Math.sin(x*0.1) + Math.sin(x*0.07) * 0.5 + Math.sin(x*0.03)*0.25) * 0.5 + 0.5;
}

function drawFractalTree(ly, x1, y1, length, angle, depth)
{
  if (depth===0) return;
  var x2 = x1 + length * Math.cos(angle);
  var y2 = y1 + length * Math.sin(angle);
  var branch = ly.pathItems.add();
  branch.setEntirePath([[x1, -y1], [x2, -y2]]);
  branch.stroked = true;
  branch.strokeWidth = depth;
  branch.strokeColor = gy;
  var newLength = length * (0.5 + noise(depth) * 0.3);
  var angleVariation = (Math.PI / 6) * (0.4 + noise(depth) * 0.3);
  drawFractalTree(ly, x2, y2, newLength, angle - angleVariation, depth-1);
  drawFractalTree(ly, x2, y2, newLength, angle + angleVariation, depth-1);
}

ノイズと言いつつもPerlinノイズは疑似乱数を利用したものでインプットに対する再現性は100%です。計算負荷が低いため2Dテクスチャの生成なんかで重宝するのですが、それはまたの機会に解説しましょう。今回は各段の角度と長さにノイズ成分を反映したものになっています。

擬似なので引数として与えられるシードが段ごとに設定されているので同じ段ではゆらぎは見られません。

最後に樹冠に相当する部分に葉を表現してみます。

var lyr = app.activeDocument.layers.add();
lyr.name = "Fractal Tree";
var gy = new GrayColor();
gy.gray = 50;
drawFractalTree(layer, 300, 500, 100, -Math.PI/2, 8);

function noise(x) 
{
  return (Math.sin(x*0.1) + Math.sin(x*0.07)*0.5 + Math.sin(x*0.03)*0.25)*0.5 + 0.5;
}

function drawFractalTree(ly, x1, y1, length, angle, depth) 
{
  if (depth === 0)
  {
  	drawLeaf(layer, x1, y1, length);
    return;
  }
  var x2 = x1 + length * Math.cos(angle);
  var y2 = y1 + length * Math.sin(angle);
  var branch = layer.pathItems.add();
  branch.setEntirePath([[x1, -y1], [x2, -y2]]);
  branch.stroked = true;
  branch.strokeWidth = depth;
  branch.strokeColor = gy;
    
  var newLength = length * (0.5 + noise(depth)*0.3); 
  var angleVariation = (Math.PI / 6) * (0.4+noise(depth)*0.3);
  drawFractalTree(layer, x2, y2, newLength, angle - angleVariation, depth-1);
  drawFractalTree(layer, x2, y2, newLength, angle + angleVariation, depth-1);
}

function drawLeaf(ly, x, y, size)
{
  var leafSize = size * (0.3 + Math.random() * 0.7); 
  var leaf = ly.pathItems.ellipse(-y-leafSize/2, x-leafSize/2, leafSize, leafSize);
  leaf.filled = true;
  var green = new RGBColor();
  green.red = Math.random() * 50;
  green.green = 100 + Math.random() * 155;
  green.blue = Math.random() * 50;
  leaf.fillColor = green;
  leaf.stroked = false;
}

これを実行すると…

こんなかんじでちょっと雰囲気が良くなりました。

ということなんですけど、この類のプログラムはforやwhileのような明示的な繰り返し機能は利用されません。関数として設定されたルーチンを内部から自分自身を呼び出すことで繰り返しを行うので再帰処理と呼ばれます。
再帰処理は段が深くなると反復する処理がべき乗で増加するため段数が深くなると計算負荷が非常に高くなります。

上記の作例では深さが8に設定されていますので末端のノードは128となり一番細い枝が128本あることになります。もう1段深くなると256、10段では512という事になり段数毎に2倍の負荷となっています。これが前日のシェルペンスキーの三角形だと1段あたり3個の子を持つので3・9・27・81・243・729・2187…と7段で2000を超えてきます。まあ、内容的には手続き型の軽い処理なんで、繰り返しが多くても安定して動きますが、もう少し複雑な処理を再帰で回すとあっと言う間にメモリを食いつぶしたりリークしたりします。とい事なので限界点にチャレンジする場合はハング覚悟で実施することをお勧めします。

ちなみに、depthを16に設定して回した結果が以下のものです。なかなか開放されないんでハングしたかなって思ったけどやり遂げましたw

ten_a

Graphic Designer, Scripter and Coder. Adobe Community Professional.

シェアする