There are many resources avavilable online about Lindenmayer systems.
The syntax is per line. Versions <= Lparser 4 required that the lines be given in the specific order, Lparser 5 removed this restriction by introduced keywords.
lsuss supports both forms of syntax, though mixing the two styles in the same file is to be discouraged.
| <= Lparser 4 syntax | Lparser 5 syntax | Description | Effected symbols | Default values |
|---|---|---|---|---|
| # | # | rest of line is a comment | ||
| <float> | recursion <float> | recursion depth | 1 | |
| <float> | angle <float> | turning angle | + - < > & ^ | 45.0° |
| <float> | thickness <float> | trunk thickness | F Z | 100.0 |
| <production> | axiom <production> | starting axiom | must be supplied | |
| <id> '=' <production> | rule <id> '=' <production> | rules (multiple) | ||
| @ | @ | end of file | optional | |
| min_thickness <float> | minimum trunk thickness | F Z | 0.0 | |
| trope <float>* | droop due to gravity | t | 0.2 | |
| rand <float>* | randomness | ~ | 6.0 | |
| poly_limit <int> | maximum number of polygons | F Z { } | 500000 | |
| shape <int> | trunk shape (0=cubes, 1=connected cylinders) | F Z | 1 | |
| color <int> <int> <int> <int> | color (multiple) (color index 0..100, red/green/blue 0..255) | c | (see table below) |
* - not supported in Lparser 5, but is supported by lsuss.
| color index | default color |
|---|---|
| 1 | black |
| 2 | red |
| 3 | yellow |
| 4 | green |
| 5 | cyan |
| 6 | blue |
| 7 | magenta |
| 8 | dark green |
| 8 | dark cyan |
| 10 | dark blue |
| 11 | dark magenta |
| 12 | brown |
| 13 | dark green |
| 14 | grey |
| 15 | white |
| else | dark grey |
The symbols describe a basic 3D turtle graphics language. The current state is represented as a position, directional vectors, an angle, thickness, length, and color.
The directional vectors (forward, up, and left) represent a Frenet trihedron (i.e. with tangent, normal, and binormal vectors) - a nice visualisation/explanation can be found within Ridy Rucker's Kappatau Space Curves paper. The rotations used in lsuss are performed around these vectors, where each rotation is systematically named depending on what vector it rotates around, i.e. turn around up vector, pitch around left vector, roll around forward vector.
In the following table the symbols that are mentioned as having a default can take an optional float argument by providing it in brackets, i.e. +(30) would mean turn 30 degrees.
| Symbol | Description |
|---|---|
| + - | turn around up vector - default is current angle |
| & ^ | pitch around left vector - default is current angle |
| < > | roll around forward vector - default is current angle |
| % | roll 180° |
| | | turn 180° |
| ! ? | multiply thickness - default for ! ×0.7, ? ÷0.7 |
| : ; | multiply angle - default for : ×0.9, ; ÷0.9 |
| ' ” | multiply length - default for ' ×0.9, ” ÷0.9 |
| F | move forward and draw a trunk - default is length |
| Z | move forward and draw a trunk - default is half-length |
| [ | push state - position, vectors, angle, thickness, length, color |
| ] | pop state |
| g | move forward - default is length |
| c | change color - default is next color |
| ~ | turn, pitch & roll by random amount - default is rand |
| t | pitch due to gravity - default is trope |
| $ | roll until horizontal (do nothing if pointing straight up) |
| { | begin a polygon and use current position as the first point |
| } | end a polygon - only draws if at least three points |
| f | move forward and add to polygon points - default is length |
| z | move forward and add to polygon points - default is half-length |
| . | add current position to polygon points |
That would be giving away trade secrets… but here are some hints that I welcome feedback upon.
Traditionally lsystems are described as taking a string and replacing symbols in it with strings, and then repeating from the start of the string for each recursion level - thus building a massive string with a significant amount of data motion, and you still probably have to parse the numbers in the string later on. lsuss turns the rules into tokens (symbols/number) first and then uses a bounded depth first traversal of the cyclic graph represented by the rules, and thus it only needs a bounded stack, and a method to get the next symbol.
lsuss uses a Frenet trihedron, i.e. three unit vectors (f)oward, (l)eft, and (u)p all orthogonal to each other. A turn by angle° around the up vector (e.g. +(30)) is simply:
r = angle π/180.0 f' = f cos(r) + l sin(r) l' = -f sin(r) + l cos(r) u' = u
Where the rotations around the other vectors are variations of this. For rolling to horizontal (i.e. $) lsuss uses:
a = atan2(l.z, u.z) if(a > π/2) a = π - a else if(a < -π/2) a = -π - a roll = a 180.0/π -- and then on to roll around forward vector as usual
For troop (i.e. t) lsuss uses:
t = [-f.x, -f.y, 0.0]; a = troop (f·t) / |t| if(u.z < 0.0) a = -a -- ensure pitch downwards pitch = a 180.0/π -- and then on to pitch around left vector as usual
The maths seems sufficiently stable as to not need renormaliziation of the trihedron, if normalization is needed then it would be cricual to preserves the orthogonal nature, using of cross products, etc.
The sample Lparser files have been provided with the permission of Laurens Lapré. We encourage people to visit his Lparser homepage and suggest that Windows users download and try his Lparser software.