2025年5月25日(日) 03:47 JST

L-system

  • 2009年2月 9日(月) 12:34 JST
  • 投稿者:

前回コッホ曲線を「収縮型」で描いたので、今回は「膨張」型でコッホ曲線を描くプログラムを書いてみました。

建築設計の実際は、たくさんの制限(面積、高さ、建坪率、容積率)を満たしながら<解を求める>ことなわけですが、膨張型を採用するとそれらの制限の処理に頭を悩ませることになります。うちの4年生も卒業制作でこれらを何とかしようと必死で努力しています。

この記事は絵を描くだけが目的なのでそんなうるさいことには一切構いません(笑)。


(全部読むには 全文表示 をクリックしてください。)

さて、今回はL-systemという手法を使います。L-systemについても下手な説明はするだけ無駄ですのでWikipediaで確認してください。L-systemでは、初期値と変換規則を与えて、世代を一つ進める毎に規則を適用して値を書き換えていきます。たとえばこんな感じに。

初期値: A、変換ルール1: A → AB、変換ルール2: B → AAB
A
AB
ABAAB
ABAABABABAAB
・・・
まあ、さほど難しい話ではありませんね。

次に、L-systemが出力する値を描画ルーチンの入力にすると不思議な形状が出現します。描画手法としてはタートルグラフィックスがよく使われるようです。タートルグラフィックスには独立したWikipediaのページがありません*1が、L-systemのページで少し説明されていますのでそちらを参照してください。

*1日本語版にはエントリーはあるもののページはブランク、英語版には少しだけ説明があります。

以下のソースに、コッホ曲線とドラゴン曲線とシェルピンスキーの三角形の例を含めておきましたので試してください。ただし、適用する規則にもよりますが、世代を少し進めるだけで値(文字列)が爆発的に長くなるので、小さめの数字*2から始めて徐々に大きくするようにしてください。

*2具体的には2とか3で始めてください。

ちなみに、建築分野では地味に人気の高いあのペンローズタイルもL-systemで描けます。本サイトのバナーもこの方法で書いたペンローズタイルにしばらく前から変わっています。以下のプログラムでペンローズタイルを出力するには少々改造する必要があります。興味のある方は手を加えてみてください。Wikipediaには第6世代までの絵柄が紹介されていましたので、下図は第7世代まで根気よく「成長」させたものです。このあたりまでくると待ち時間がとても長くなるので、C言語で書きたくなりました。

L-systemによるペンローズタイル(7世代もの)

%
% L-System.pl
% HLAB(C) 2009
%
:- encoding(utf8).
:- new(@w, picture('L-system')), send(@w,open).

%%コッホ → 角度は60°
rule(a,[a,-,a,+,+,a,-,a]).

%%シェルピンスキー
%rule(a,[b,-,a,-,b]).
%rule(b,[a,+,b,+,a]).

%%ドラゴン → 回転角度は90°
%rule(a,[a,-,a,+,a,+,a,a,-,a,-,a,+,a]).

%%変換対象外の記号
rule(-,[-]).
rule(+,[+]).

%%線長さ → ディスプレイ解像度に合わせて
length(10).

%%回転角度 → ディグリーで指定
angle(60.0).

%%描画開始位置と進行方向の初期化
:- nb_setval(dir,v(1,0)),     %% 進行方向
   nb_setval(prev,p(150,150)), %% 描画開始座標
   nb_setval(lines,[]).

l_system(0,[a]) :- !.
l_system(N,R) :-
	N1 is N - 1,
	l_system(N1, R1),
	applyRules(R1, R).

applyRules([],[]).
applyRules([H|T], R) :-
	rule(H, L), 
	append(L, R1, R), 
	applyRules(T,R1).
go :-
	l_system(4,R),
	draw(R).

clear :-
	nb_getval(lines, LL),
	member(L,LL),
	send(L, free),
	fail.
clear :- 
	nb_setval(lines,[]).

draw([]).
draw([H|T]) :-
	( H \== +, H \== -), !,
	nb_getval(dir,v(DX,DY)),
	nb_getval(prev,p(X0,Y0)),
	length(Len),
	X1 is X0 + DX * Len,
	Y1 is Y0 + DY * Len,
	send(@w, display, new(Handle, line(X0,Y0,X1,Y1,none))),
	send(Handle, colour, colour(blue)),
	pushHandle(Handle),
	nb_setval(prev,p(X1,Y1)),
	send(@w, flush), % 動画にしたい場合はこの行を生かす
	%sleep(0.02),     % さらにゆっくり鑑賞したければここも生かす
	draw(T).
draw([H|T]) :-
	rotation(H),
	draw(T).

pushHandle(H) :-
	nb_getval(lines, L),
	nb_setval(lines,[H|L]).

rotation(+) :-
	angle(Deg),
        Theta is pi * Deg/180.0,
	rotation0(Theta).
rotation(-) :-
	angle(Deg),
	Theta is pi * Deg/180.0,
	rotation0(-Theta).
rotation0(Theta) :-
	nb_getval(dir,v(DX,DY)),
	DX1 is DX * cos(Theta) - DY * sin(Theta),
	DY1 is DX * sin(Theta) + DY * cos(Theta),
	nb_setval(dir,v(DX1,DY1)).

以下のコメントは、その投稿者が所有するものでサイト管理者はコメントに関する責任を負いません。