Log In  

Cart #42122 | 2017-07-01 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
13


I learned about L-systems the other day, so here's my first shot at making one. It generates a plant whenever you hit the main action button.

L-System Parameters:

axiom="a"

rules={}
rules["a"]={"b{c}b","c{b}c"}
rules["b"]={"a{a.}","a(bb.)"}
rules["c"]={"(ab.)","(b)"}

Constants are any characters that don't appear as a rule index - all rules for a given variable have an equal chance of being chosen. The generator runs five iterations for each plant.

A means "go forward," B and C mean "turn and go forward," {} and () mean "start a new branch and turn afterward," and . means "draw a circle."

All animation comes from the rendering portion - the L-system only runs to initially create the plant.

P#42123 2017-07-01 13:41 ( Edited 2017-07-03 22:15)

This is really really cool!! I love it. I'll have to dig deeper into the code when I have some time to sit down and look at it. I'd love to understand how this was done. :)

P#42130 2017-07-01 17:47 ( Edited 2017-07-01 21:47)

Looking through the code might help, but I wasn't writing it to be illustrative, so it's probably a little confusing if you try to stare at it on its own. Instead, here's an overly-lengthy explanation of what's going on:

You'll have to know the basics of L-Systems (they're pretty simple to jump into - honestly I just looked over some stuff on the wikipedia page) - the basic idea is that you have a string that represents the structure of some object, and then you have some rules for "growing" that string - each character in it is allowed to have a list of mutations, and when you grow a string, you find all the possible mutations in it and apply them, and then the cumulative result is the next version of the string.

So maybe you start with the string "A," and the character A is told that it's allowed to morph into the sequence of characters "BAC" - if we apply that rule to the first string (traditionally, the starting string in an L-system is called the "axiom"), we get "BAC" because "A" turns into that. If we run another tick, now using the string "BAC" as the input, then our new output is "BBACC" because the A in the middle has grown according to its mutation rule. "B" and "C" don't have any mutation rules in this simple example, so each time you grow, you'll just get more leading Bs and trailing Cs.

In the description of the demo above, you can see all the rules for the characters in this system. A, B, and C all have two possible mutations (with equal chance), and some of those mutations include extra punctuation symbols (which don't have any rules defined, which means they never morph/grow once they've been added).

Once the string has been generated, we use it to describe the layout of a plant. The routine is basically a "turtle" system - it's an imaginary car that can move forward or steer when it's told to, and it draws lines as it moves. The different symbols in the string tell the renderer to do a few different things:

A -- "Move forward and draw a line from your previous position to the new one"
B and C -- "Turn left/right, then move forward a small amount and draw a line"
{ and ( -- "Remember your current position+direction"
} and ) -- "Return to the position+direction where this group started, and then turn left/right"
. -- "Draw a pom-pom here"

Note that B and C are the same, except the turning direction, and {} and () are also the same, except the turning direction.

Here's an example of a simple output (two growth-iterations after the starting "A" string - a "young" plant):

(ab.){a(bb.)}(ab.)

An image of the above example after rendering:

If we look at the grouping symbols as if they were code groups (like html tags, curly-braces for code blocks, parentheses in text, etc), we can see that the growth scheme maintains correct grouping (since grouping symbols never mutate, according to our rules, and they always appear in matching pairs when introduced).

We have three root-groups:

(ab.)
{a(bb.)}
(ab.)

These are the plant's three root-branches that extend from the base (because once a branch-group is finished, we return to its starting point - and since there are no movement instructions in our string before the root-groups begin, all three of these branches extend from the ground-point where the plant grows from). You can also see that each root-group ends with a period, which means they all have a pom-pom at the end.

The animation is mostly introduced by altering the strength of each "turn" instruction - the animation is very similar to a vertex shader in 3D graphics, but instead of applying a change to each vertex in a mesh, we're applying a change to each branch-segment in the plant. Because of the "car" method of drawing, rotating a branch segment means that any of its child-segments will also be rotated (since all rotations/movements are relative to wherever the previous drawing task ended). This gives the plant a completely-stable fixed-length for all of its branch-segments during movements, which looks pretty nice, and then we can safely introduce a small amount of intentional rightward stretching to interfere with (and therefore obscure) the rest of the wind behavior.

Woof! That took a lot of explanation, but honestly L-systems don't seem particularly hard to implement. The whole cart is <200 lines!

Finally, just for example, here's a fully-grown plant and its layout string:

(b{c}ba(bb.).){b{c}b{b{c}b.}}(c{b}ca(bb.).)((ab.){a{a.}}(ab.)(b{c}b{b{c}b.}b{c}b{b{c}b.}.)a{a.}{(ab.)}a{a.}(c{b}c(a{a.}a{a.}.)b{c}b(a{a.}a{a.}.).).){(a{a.}{(b)}a(bb.)(c{b}c(a{a.}a(bb.).)c{b}c{b{c}b.}.))}c{b}c(a(bb.)a{a.}.){(b{c}ba{a.}.)}b{c}b{c{b}c.}{(b{c}ba(bb.).){b{c}b(a{a.}a(bb.).)}(b{c}ba(bb.).).}

I'm not gonna try and explain how that one ends up as the picture...but uh, it's all the same rules as the easier example.

P#42145 2017-07-02 14:04 ( Edited 2017-07-02 18:07)

Awesome! Thank you so much for taking the time to write this up :-D

P#42165 2017-07-03 18:15 ( Edited 2017-07-03 22:15)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-28 17:53:49 | 0.011s | Q:19