- 追加された行はこの色です。
- 削除された行はこの色です。
#freeze
NetaTaneMenu >>>> [[羊歯]] >>>> L-system
*L-system
----
:L-system: 「Lindenmayer system」の略称。リンデンマイヤーさんが考えたらしい。ルールに従って成長するあらゆるモデルを扱うための文法体系みたいな感じです。画像は視覚化するため,あるいはこれを利用して造形するために行うだけでL-systemの本質ではありません。下のソースにも幾つか紹介したように,ルールによって数列など様々な物が定義・作成できます。
:L-system: 「Lindenmayer system」の略称。リンデンマイヤーさんが考えたらしい。ルールに従って成長するあらゆるモデルを扱うための文法体系みたいな感じです。画像は視覚化するため,あるいはこれを利用して造形するために行うだけでL-systemの本質ではありません。下のソースにも幾つか紹介したように,ルールによって数列など様々な物が定義・作成できます。
#ref(Lsystem3.png,center)
図は下の十進ベーシックのソースで描いたもの。
図は下の十進ベーシックのソースで描いたもの。
ここで見ているL-systemは
:使用文字: A,B,C,D,F
:定格文字: [.],{,}
:初期設定: A
:ルール: A→F[B]C,B→C,C→F{D}A,D→A
です。
最初に
A
という文字列が与えられます。この文字列を走査しながらルールを適用すれば,
F[B]C
となります。
再度得られた文字列を走査しながらルールを適用すれば,
FはF [も[ Bはルールが適用されてC Cもルールが適用されてF{D}A
となり,(変換のルールに無い文字,ここでは「F」や定格文字は変化しません)
F[C]F{D}A
となります。これを繰り返すわけです。
これを十数回?繰り返したものを「適当な数値を設定して図形的に解釈したら」
上のように見ることもできるっていうわけです。
あくまでもL-system自体は記号の変換体系というわけです。
! L-system program
! L-system structure G=(V,S,O,P)
! V:variables 所謂構成用文字。増やしてもらって結構です。
LET V$="ABCDEF"
! S:constants
! S:constants
LET S$="+-[]{}"
! O:initiator ルールに依存します。
LET O$="A"
! P:rules 幾つかのルールとその書式です。
!LET P$="(A->AB)(B->CA)(C->BA)"
!LET P$="(A->AB)(B->A)" ! Algae
!LET P$="(A->B)(B->AB)" ! Fibonacci
!LET P$="(A->ABA)(B->BBB)" ! Cantor Set
!
!LET O$="F" ! Koch
!LET P$="(F->F+F-F-F+F)" ! Koch
!LET P$="(A->B-A-B)(B->A+B+A)" ! Sierpinski triangle
!LET P$="(A->F[A]{A})(B->A)(C->F{D}A)(D->A)" ! 単純な二分木
LET P$="(A->F[B]C)(B->C)(C->F{D}A)(D->A)" ! 少々複雑な木
! main
PRINT O$ ! とりあえず最初の形を「テキスト出力」に出力
FOR i=1 TO 12 ! ルールを何回適用するか
LET L$=Lsystem$(V$,S$,O$,P$,i) ! ルールを適用する関数
PRINT L$ ! 「テキスト出力」に出力
NEXT i
! 以下はグラフィック出力用の設定と実行
SET WINDOW -4,4,-1,7
RANDOMIZE
! グラフィック出力のサブルーチン
!CALL GLsystem(l$,V$,S$,0,0,90,1,32,0.5,0,0.1,3,60,0.8,55,65)
CALL GLsystem(l$,V$,S$,0,0,90,1,32,0.5,0,0.1,3,60,0.75,1.1,0.9,55,45)
! EXTERNAL SUB glsystem(x$,v$,s$,x,y,d,l,w,cR,cG,cB,s,t,tr,tl,r,rr,rl)
! (x,y)は描き始めるポイント dは方向 lは長さ wは太さ
! (cR,cG,cB)はRGB色
! sはターンの角度
! tは(+-での)ターンの角度
! tr,tlは右枝,左枝の出る角度
! rは1つ深く入った時の長さの比率
! rrは右枝のrlは左枝の長さの比率
END
!
! 以上がメインプログラム。以下が外部関数及びルーチン
!==============
! L-system関数
!==============
EXTERNAL FUNCTION Lsystem$(v$,s$,o$,p$,n)
! L-system G=(v$,s$,o$,p$) を n 回変換します。再帰呼び出し?
IF n=0 THEN ! n=0 なら終了条件
LET Lsystem$=o$
ELSE
LET l=LEN(o$) ! 元の文字列の長さを測り
LET no$="" ! 空の文字列 no$を用意してここに答えを
FOR i=1 TO l ! 文字列の先頭から順に
LET ox$=o$(i:i) ! 一文字ずつ ox$に収めて
IF POS(v$,ox$)>0 THEN ! 変数なら
LET no$=no$&rule$(p$,ox$) ! ルールを適用したものを応えに付け加え
ELSEIF POS(s$,ox$)>0 THEN ! 定数なら
LET no$=no$&ox$ ! そのままを付け加え
ELSE ! ルールや変数指定の間違いがあれば
PRINT "<";ox$;"> ***** L-SYSTEM ERROR ***** " ! エラー表示
END IF ! という場合分けを
NEXT i ! 最後の一文字まで繰り返します。
LET Lsystem$=Lsystem$(v$,s$,no$,p$,n-1) ! で再帰します。
END IF
END IF
END FUNCTION
!==============
! L-system関数で呼ばれるルール適用関数
!==============
EXTERNAL FUNCTION rule$(r$,x$)
LET l=LEN(r$) ! パターンマッチングです。
LET p=POS(r$,"("&x$) ! "(X->Y・・・"の形で個別のルールが始まるので
IF p>0 THEN ! マッチするルールがあれ ば
IF p>0 THEN ! マッチするルールがあれ ば
LET rr$=r$(p+4:l) ! 適用部分の->以降のルール文字列を取り出し
LET p=POS(rr$,")") ! そのルールの終り")"の位置をみつけたら
LET p=POS(rr$,")") ! そのルールの終り")"の位置をみつけたら
!PRINT rr $
LET rule$=rr$(1:p-1) ! そこまでが,ルールを適用した結 果
LET rule$=rr$(1:p-1) ! そこまでが,ルールを適用した結 果
ELSE ! なのでそれを返すわけです。
LET rule$=x$ ! マッチしなければ変化無し
END IF
END FUNCTION
!==============
! グラフィックス用サブルーチン
!==============
EXTERNAL SUB glsystem(x$,v$,s$,xx,yy,dd,ll,ww,ccR,ccG,ccB,s,t,r,rr,rl,tr,tl)
! (x,y)は描き始めるポイント dは方向 lは長さ
! sは1つ深くなったときのターンの角度
! tは(+-での)ターンの角度
! rは1つ深く入った時の長さの比率
! tr,tlは[]{}で枝分かれした時の,枝分かれの角度
OPTION ANGLE DEGREES
LET x=xx
LET y=yy
LET d=dd
LET l=ll
LET cR=ccR
LET cG=ccG
LET cB=ccB
SET COLOR MIX(1) cR,cG,cB
LET w=ww
SET LINE WIDTH w
PLOT LINES : x,y;
LET xl=LEN(x$)
DO WHILE xl>0
LET ox$=x$(1:1)
IF POS(v$,ox$)>0 THEN
LET x=x+l*COS(d)
LET y=y+l*SIN(d)
SET LINE WIDTH w
PLOT LINES : x,y;
LET l=l*r
LET w=w*r
LET d=d+s
LET tr=tr-ABS(s)
LET tl=tl-ABS(s)
ASK COLOR MIX(1) cR,cG,cB
LET cR=cR*RND !cR/(1+RND)
LET cG=cG+(1-cG)/(5+RND*10)
LET cB=cB/(1+RND)
SET COLOR MIX(1) cR,cG,cB
ELSEIF POS("+-",ox$)>0 THEN
IF ox$="+" THEN LET d=d+t ELSE LET d=d-t
ELSEIF POS("[{",ox$)>0 THEN
LET hx$=head$(x$)
LET x$=" "&tail$(x$)
IF ox$="[" THEN
CALL glsystem(hx$,v$,s$,x,y,d+tl,l/r*rl,w,cR,cG,cB,-s,t,r,rr,rl,tr-ABS(s),tl-ABS(s))
ELSE
CALL glsystem(hx$,v$,s$,x,y,d-tr,l/r*rr,w,cR,cG,cB,s,t,r,rr,rl,tr-ABS (s),tl-ABS(s))
END IF
PLOT LINES
PLOT LINES : x,y;
SET COLOR MIX(1) cR,cG,cB
END IF
LET x$=x$(2:xl)
LET xl=LEN(x$)
IF xl=0 THEN
SET AREA COLOR 4
DRAW disk WITH SCALE(0.02)*SHIFT(x,y)
END IF
LOOP
END SUB
!==============
! glsystemで呼ばれる文字列の枝を返す関数
!==============
EXTERNAL FUNCTION head$(x$)
LET ox$=x$(1:1)
IF ox$="[" THEN
LET ck1$="["
LET ck2$="]"
ELSE
LET ck1$="{"
LET ck2$="}"
END IF
LET lx=LEN(x$)
LET ax$=x$(2:lx)
LET lx=lx-1
LET ans$=""
LET i=1
LET k=1
DO WHILE k>0
LET ox$=ax$(i:i)
IF ox$=ck1$ THEN
LET k=k+1
ELSEIF ox$=ck2$ THEN
LET k=k-1
END IF
IF k>0 THEN LET ans$=ans$&ox$
LET i=i+1
LOOP
!PRINT ans$
LET head$=ans$
END FUNCTION
END FUNCTION
!==============
! glsystemで呼ばれる文字列の本体の残りを返す関数
!==============
EXTERNAL FUNCTION tail$(x$)
LET ox$=x$(1:1)
IF ox$="[" THEN
LET ck1$="["
LET ck2$="]"
ELSE
LET ck1$="{"
LET ck2$="}"
END IF
LET lx=LEN(x$)
LET ax$=x$(2:lx)
LET lx=lx-1
LET i=1
LET k=1
DO WHILE k>0
LET ox$=ax$(i:i)
IF ox$=ck1$ THEN
LET k=k+1
ELSEIF ox$=ck2$ THEN
LET k=k-1
END IF
LET i=i+1
LOOP
LET ans$=ax$(i:lx)
!PRINT ans$
LET tail$=ans$
END FUNCTION
END FUNCTION
rubyやproccessindg(?でしたっけ)で描けばもっとインタラクティブだったり
コードが見易かったりするのだとは思うのですが,
現場ではまだまだベーシックなような気がするのとお手軽さで。
----
ルールの部分を次のように変えると
LET P$="(A->F[B[A]]C)(B->C)(C->F{D}A)(D->A)" ! 少々複雑な木
こんな風になります。
#ref(Lsystem31.png,center)
少しは自然に観えますか?
COLOR(red){なかなか理解は追いつかないのですけれど,本当に素晴らしい!!どのようにして生徒に生かそうかと少々思案しています。でも,ここまでよくぞ見事に描けるものなんですね・・・。唖然}