L-system 與海龜


在〈實作 L-system〉中談到,L-system 可以用來描述海龜繪圖,對 L-system 本身來說,文法規則越豐富,可表示的生態成長就越豐富,為了在使用 L-system 描述海龜時,行為上可以更豐富些,接下來要實作的 L-system 程式,將支援以下的指令:

  • F:前進並畫線
  • f:前進不畫線
  • +:左轉
  • -:右轉
  • |:轉向(轉 180 度(degree))
  • [:將目前狀態置入堆疊
  • ]:取出堆疊頂的狀態

先前文件中實作的 Turtle,已經能支援以上的指令了,不過 p5.js 中旋轉之類的函式,可以支援徑度與角度,端看你的 angleMode 是指定 DEGREESRADIANS,為了便於實現轉向,這邊直接在 Turtle 實作個 reverse

class Turtle {
     起始位置 (x, y) 與頭面向的角度
    constructor(x = 0, y = 0, angle = 0) {
        this.coordinateVector = createVector(x, y);
        this.headingVector = createVector(1, 0).rotate(angle);
        this.state = [];
    }

    ...略

     轉向
    reverse() {
        this.headingVector.mult(-1);
    }
}

採用向量計算的話,就不用理會目前 angleMode 是指定 DEGREESRADIANS 了,直接乘上 -1,向量就是轉向了。

接著,基於 Turtle 以及〈實作 L-system〉的 lsystem 寫個 lsystemTurtle,將海龜繪圖與 L-system 結合在一起:

// length 是每次前進的長度
// angle 是每次轉的角度
// forwardSymbols 指定了也用來代表 F 的符號
function lsystemTurtle(axiom, rules, length, angle, n, forwardSymbols = '') {
    let symbols = Array.from(lsystem(axiom, rules, n));
    if(forwardSymbols.length > 0) {
        symbols = symbols.map(symbol => forwardSymbols.includes(symbol) ? 'F' : symbol);
    }

    const t = new Turtle();
    const lines = [];
    for(let i = 0; i < symbols.length; i++) {
        switch(symbols[i]) {
            case 'F': //  前進並畫線
                lines.push(t.forward(length)); break;
            case 'f': //  前進不畫線
                t.forward(length); break;
            case '+': //  左轉
                t.turn(-angle); break;
            case '-': //  右轉
                t.turn(angle); break;
            case '|': //  轉向
                t.reverse(); break;
            case '[': // 將目前狀態置入堆疊
                t.push(); break;
            case ']': // 取出堆疊頂的狀態
                t.pop(); break;
        }
    }

    return lines;
}

那麼〈實作 L-system〉中談到的樹生長:

  • 變數:X
  • 常數:F[]+-
  • 公理:X
  • 規則:X → F[+X][-X]

就可以畫出來了:

如果是這個 L-system 的話:

  • 變數:X
  • 常數:F[]+-
  • 公理:X
  • 規則:X → F[+X][-X]F → FF

可以生成另一種風格的樹:

來點有趣的動畫吧!以下是厥葉曲線,然而以動畫示範了整個繪圖的過程:

還能有哪些有趣的 L-system 可以與海龜繪圖結合呢?lsystems.js 我收集了一些(這邊沒有使用 p5.js-widget,因為它載入太長的程式碼會有問題):

令人驚訝吧!目前海龜可接受的指令並不多,卻已經可以變化出這麼多的圖案,你可以嘗試自訂更多的指令,或者是加入一些隨機性,例如,在某個機率以上,才選擇某個生成規則進行生成,構成某種形式的隨機 L-system(Stochastic L-system),這就讓你自己來嘗試了。