L-System Explorer: another experiment with procedural plants and gsub

(Updated) The inspiration for the code below is the book identified below and the Wikipedia articles on simulated growth of plants and L-systems that cite it as a source. An example of its output is below:


--
-- L-System Explorer
--
-- With acknowledgements to "The Algorithmic Beauty of Plants" by
-- Przemyslaw Prusinkiewicz and Aristid Lindenmayer
-- http://algorithmicbotany.org/papers/

supportedOrientations(LANDSCAPE_ANY)
function setup()
    -- Set up L-System
    l = L("X", {X = "F-[[X]+X]+F[+FX]-X", F = "FF"}, 5, 22.5)
    -- Other L-Systems:
    -- l = L("F", {F = "FF-[-F+F+F]+[+F-F-F]"}, 5, 22.5)
    -- l = L("X", {X = "F-[[X]+X]+F[+FX]-X", F = "FF"}, 5, 22.5)
    -- l = L("X", {X = "F[+X][-X]FX", F = "FF"}, 5, 25.7)
    -- l = L("X", {X = "F[+X]F[-X]+X", F = "FF"}, 5, 20)
    l.color = color(50, 140, 28)

    sTime = ElapsedTime
    step = 1
    count = 1
    maxCount = 7

    bm = mesh()
    bm.vertices = {vec2(1, 1), vec2(WIDTH, 1), vec2(1, HEIGHT),
    vec2(1, HEIGHT), vec2(WIDTH, 1), vec2(WIDTH, HEIGHT)}
    local c1 = color(199, 200, 159)
    local c2 = color(203, 214, 89)
    bm.colors = {c2, c2, c1, c1, c2, c1}
    textMode(CORNER)
    font("Inconsolata")
end

function draw()
    if ElapsedTime - sTime > step and count < maxCount then
        l:grow()
        sTime = ElapsedTime
        count = count + 1
    end
    background(0)
    bm:draw()
    fill(0)
    local h = HEIGHT - 30
    for k, v in pairs(l.rules) do
        local r = k.." -> "..v
        text(r, 10, h)
        h = h - 30
    end

    translate(WIDTH/2, 0)
    l:draw()
end

L = class()

function L:init(root, rules, length, angle)
    self.state = root
    self.rules = rules
    self.length = length
    self.angle = angle
    self.color = color(255)
    local pattern = "(["
    for k, v in pairs(rules) do
        pattern = pattern..k
    end
    self.pattern = pattern.."])"
end

function L:draw()
    local stack = {}
    pushMatrix()
    pushStyle()
    stroke(self.color)
    strokeWidth(1)
    noSmooth()
    local tx, ty, ta = 0, 0, 90
    local l = self.length
    local a = self.angle
    for i = 1, #self.state do
        local ntx, nty, nta
        local c =self.state:sub(i, i)
        if c == "F" then
            nta = ta
            local nar = math.rad(nta)
            ntx = tx + l * math.cos(nar)
            nty = ty + l * math.sin(nar)
            line(tx, ty, ntx, nty)
        elseif c == "-" then
            nta = (ta - a) % 360
            ntx, nty = tx, ty
        elseif c == "+" then
            nta = (ta + a) % 360
            ntx, nty = tx, ty
        elseif c == "[" then
            stack[#stack + 1] = {tx, ty, ta}
            ntx, nty, nta = tx, ty, ta
        elseif c == "]" then
            ntx, nty, nta = unpack(stack[#stack])
            table.remove(stack)
        else
            ntx, nty, nta = tx, ty, ta
        end            
        tx, ty, ta = ntx, nty, nta
    end
    popStyle()
    popMatrix()
end

function L:grow()
    local function f(...)
        return self.rules[arg[1]]
    end
    self.state = self.state:gsub(self.pattern, f)
end

Elsewhere on the Codea Forum, @Herwig and @Bri_G have also posted L-system based code, here and here. This code is shorter, because it makes use of Lua’s powerful string.gsub() library function.

The rules of the L-system are represented by a Lua table (referred to in the Codea class L by the by the field rules). For example, the two rules:


X -> F-[[X]+X]+F[+FX]-X
F -> FF

are represented by the table constructed as:


{
    X = "F-[[X]+X]+F[+FX]-X",
    F = "FF"
}

You are a code generating machine!

Thank you @Reefwing - I’ll take that as a compliment. I have updated my original post to include further references and explanations.

If anybody should follow the experiments that I post to this forum, they will see that they are only snippets. My current interest is to explore what short but, hopefully, intelligible code can produce.

So, I’d like to take this code and have it generate a mesh object…should be simple to do that…it’s leaves that I don’t know (yet) how to generate. I think the Bloom project will help with that…

It was a compliment @mpilgrem - I’m amazed at how much code you are able to produce on a variety of subjects.

@mpilgrem I’m getting curious as to your background. There’s quite a lot of mathematics behind some of these things you’re doing.