Asymptoteで再帰曲線
和田先生のパラメトロン計算機: 再帰曲線を見て、Asymptoteでも再帰曲線を書きたくなった。
それぞれの曲線描画アルゴリズムは『Javaによるアルゴリズム事典』で勉強して既知なので、掲載されている何々曲線を適当に選んで、プログラミングした。
はてな記法では、Asymptoteの色付けには対応していないようなので、便宜上Cの色付けを使う。
hilbert曲線
平面を埋め尽くすように配置される曲線。
// hilbert curve int order = 7; int size = 512; size(size); real h = size / 2; pair xy = (0, 0); void rightUpLeft(int i); void downLeftUp(int i); void leftDownRight(int i); void upRightDown(int i); void lineTo(real dx, real dy) { draw((xy.x, xy.y)--(xy.x + dx, xy.y + dy)); xy = (xy.x + dx, xy.y + dy); } downLeftUp = new void(int i) { if(i <= 0) return; leftDownRight(i - 1); lineTo(0, -h); downLeftUp(i - 1); lineTo(-h, 0); downLeftUp(i - 1); lineTo(0, h); rightUpLeft(i - 1); }; leftDownRight = new void(int i) { if(i <= 0) return; downLeftUp(i - 1); lineTo(-h, 0); leftDownRight(i - 1); lineTo(0, -h); leftDownRight(i - 1); lineTo(h, 0); upRightDown(i - 1); }; upRightDown = new void(int i) { if(i <= 0) return; rightUpLeft(i - 1); lineTo(0, h); upRightDown(i - 1); lineTo(h, 0); upRightDown(i - 1); lineTo(0, -h); leftDownRight(i - 1); }; rightUpLeft = new void(int i) { if(i <= 0) return; upRightDown(i - 1); lineTo(h, 0); rightUpLeft(i - 1); lineTo(0, h); rightUpLeft(i - 1); lineTo(-h, 0); downLeftUp(i - 1); }; rightUpLeft(order);
以下、全ての描画にこのlineTo関数を用いているのだが、関数の中で線を引く度にペン位置を表すpair xyが更新されるので、破壊的。pathとペン位置をreturnすれば綺麗に書けるかもしれない。
dragon曲線
有限長のテープを折り曲げて出来る曲線。
// dragon curve int order = 7; pair xy = (0, 0); void lineTo(real dx, real dy) { draw((xy.x, xy.y)--(xy.x + dx, xy.y + dy), arrow=MidArrow); xy = (xy.x + dx, xy.y + dy); } void drawDragon(int i, real dx, real dy, int sign) { if(i == 0) { lineTo(dx, dy); return; } drawDragon(i - 1, (dx - sign * dy) / 2, (dy + sign * dx) / 2, 1); drawDragon(i - 1, (dx + sign * dy) / 2, (dy - sign * dx) / 2, -1); } drawDragon(order, 200, 0, 1);
hilbert曲線に比べれば短いコードだが、drawDragonで再帰している引数の変化について理解に多少時間がかかった。直線の向きを表現するために、矢印を加えている。
C曲線
Cの形をした曲線。
// c curve int order = 10; pair xy = (0, 0); void lineTo(real dx, real dy) { draw((xy.x, xy.y)--(xy.x + dx, xy.y + dy)); xy = (xy.x + dx, xy.y + dy); } void drawC(int i, real dx, real dy) { if(i == 0) { lineTo(dx, dy); return; } drawC(i - 1, (dx + dy) / 2, (dy - dx) / 2); drawC(i - 1, (dx - dy) / 2, (dy + dx) / 2); } drawC(order, 200, 0);
dragon曲線の折り目がちょっと違うだけといった感じで、コードも似ている。
Koch曲線
フラクタルについて教科書で学ぶと、例の2番目くらいに出てくる図。
// koch curve int order = 7; pair xy = (0, 0); real[] COS; real[] SIN; for(int j = 0; j < 6; ++j) { COS.push(cos(j * pi / 3)); SIN.push(sin(j * pi / 3)); } void lineTo(real dx, real dy) { draw((xy.x, xy.y)--(xy.x + dx, xy.y + dy)); xy = (xy.x + dx, xy.y + dy); } void drawKoch(int i, real d) { if(d <= order) { lineTo(d * COS[i % 6], d * SIN[i % 6]); return; } d = d / 3; drawKoch(i, d); ++i; drawKoch(i, d); i = i + 4; drawKoch(i, d); ++i; drawKoch(i, d); } drawKoch(0, 512);
SIN/COSはその都度計算させることも出来るが、『Javaによるアルゴリズム事典』でそうなっているように、予め表を作っておけば参照するだけなので楽になる。
Chaos
上に並べた曲線とは少々気色が違うが、再帰的な特徴が現れる。
// chaos int width = 512; int height = 256; real k_min = 1.5; real k_max = 3.0; real p_min = 0; real p_max = 1.5; for(int ik = 0; ik < width; ++ik) { real k = k_min + (k_max - k_min) * ik / width; real p = 0.3; for(int j = 0; j <= 50; ++j) p += k * p * (1 - p); for(int j = 51; j <= 100; ++j) { real ip = (p - p_max) / (p_min - p_max) * height + 0.5; dot((ik, ip)); p += k * p * (1 - p); } }
終わりに
Asymptoteについては、Asymptote - TeX Wiki辺りを見てもらえば、すぐに使えるようになるだろう。
ほとんど、TeXユーザのためのベクタグラフィックス記述環境だが、こうやってプログラムをして図を描画出来るというのは、実にありがたい。Processingで書いて保存という方法では手間が多い。
しかし、Ubuntu等でインストールされるデフォルトのpTeX環境がUTF-8に対応していないという現状は、宣伝する側には少し痛い。