Parametric L-systems have rules with
parameters. Technically all L-systems
in the previous chapters are parametric because they have rules like
`forward(10)`

and `turn(30)`

, but all of those
rules were already predefined. This chapter explains how to define
custom rules with parameters and also how to associate code with a
rule that turtle can later execute.

(with-graphics (iteration-count 72) (set-axiom (apex)) (add-rule (apex -> F(8) apex))) |

This simple example contains `apex`

rule that does
no more than slowly grow the stem of a plant longer and longer.

(defconstant +fibonacci+ 137.50777) (with-graphics (iteration-count 72) (set-axiom (apex)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (branch -> [ pitch(90) F(30) ]))) |

Next, there are some side branches added at a straight angle from the stem. Each new side branch is rotated (rolled) around the stem axis by the Fibonacci angle (or the golden angle). Fibonacci angle is a full, 360 degree angle divided by the golden ratio squared. Fibonacci angle often appears throughout nature, most commonly in a phenomenon called phyllotaxis.

(defconstant +fibonacci+ 137.50777) (with-graphics (iteration-count 72) (set-axiom (apex)) (add-option bend (turtle-pitch x)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (bend(x) -> bend((+ x 1.0)))) (add-rule (branch -> [ bend(18) F(30) ]))) |

In this example, a simple parametric rule `bend`

is defined.
The `bend`

rule is similar to `pitch`

except that
in each iteration it is rewritten with the previous angle increased by
one degree. That is the reason why "new" side branches form a steeper
angle with the stem than the "old" side branches. The general syntax
of a parametric rule definition is the rule name followed by a list
of parameters (list meaning a lisp list: elements in braces separated
by whitespace), after which comes the arrow (`->`

) followed
by rule expansion. Rule parameters can be used in arbitrary lisp
s-expressions to form arguments for rules in expansion.

When the axiom is rewritten "iteration count" number of times,
turtle goes through one final production and calls the drawing code
associated with each rule. Drawing code for a custom rule can be added
with `add-option`

macro. This macro takes two arguments:
rule name and lisp code form. For example, in the program there is a
predefined lisp function that rotates turtle around its local X axis:

(defun turtle-pitch (angle) ...)

Now, if there wasn't a predefined rule called `pitch`

,
it could be implemented by the following lines:

(add-option pitch (turtle-pitch angle)) (add-rule (pitch(angle) -> pitch(angle))

(defconstant +fibonacci+ 137.50777) (defparameter *green* (create-color 0.0 1.0 0.0)) (with-graphics (iteration-count 72) (set-background-color (create-color 0.0 0.2 0.4)) (set-axiom (radius(2) color(*green*) apex)) (add-option bend (turtle-pitch x)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (bend(x) -> bend((+ x 1.0)))) (add-rule (branch -> [ bend(18) F(30) ]))) |

Just to add some decorations to make examples look more consistent with the ones in previous chapters.

(defconstant +fibonacci+ 137.50777) (defparameter *green* (create-color 0.0 1.0 0.0)) (with-graphics (iteration-count 72) (set-background-color (create-color 0.0 0.2 0.4)) (set-axiom (radius(2) color(*green*) apex)) (add-option bend (turtle-pitch x)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (bend(x) -> bend((+ x 1.0)))) (add-rule (branch -> [ bend(18) organ ])) (add-rule (organ -> F(30) obj("blossom" 7.0))) (read-wavefront "blossom.obj" "blossom.png")) |

This example adds some blossoms at the end of branches.

(defconstant +fibonacci+ 137.50777) (defparameter *green* (create-color 0.0 1.0 0.0)) (with-graphics (iteration-count 72) (set-background-color (create-color 0.0 0.2 0.4)) (set-axiom (radius(2) color(*green*) apex)) (add-option bend (turtle-pitch x)) (add-option blossom (turtle-object "blossom" 7.0)) (add-option leaf (turtle-object "leaf" 7.0 *green*)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (bend(x) -> bend((+ x 1.0)))) (add-rule (branch -> [ bend(18) organ ])) (add-rule (organ -> F(30) blossom(20))) (add-rule ((blossom(x) ? (> x 0) -> blossom((1- x))) (blossom(x) ? (= x 0) -> roll(-90) leaf))) (add-macro (leaf -> obj("leaf" 10.0 *green*))) (read-wavefront "blossom.obj" "blossom.png") (read-wavefront "leaf.obj")) |

One more feature that goes together with parametric rules is conditionals.
Instead of just writing rules in braces, one more level of parenthesis
can be added, thus making it possible to expand a single rule in multiple
ways. In that case each rule definition can contain `?`

sign
followed by test form placed after parameter list. This particular example
contains such a rule called `blossom`

. This rule draws a
blossom, but after twenty iterations turns itself into a leaf.

Parametric rules with conditionals are a convenient way to implement stochastic L-systems. Consider as an example that somebody gathered statistics on certain species of some trees. From all the buds that appear in the spring, 20% are eaten by bugs, 50% turn into leaves, and the remaining 30% turn into flowers. Well, such statistics can be expressed nicely with the following rule:

(add-macro (bud -> r-bud((random 100))) (add-rule ((r-bud(x) ? (< x 20) -> ) (r-bud(x) ? (< x 70) -> leaf) (r-bud(x) ? (< x 100) -> flower)))

(defconstant +fibonacci+ 137.50777) (defparameter *green* (create-color 0.0 1.0 0.0)) (with-graphics (iteration-count 72) (set-background-color (create-color 0.0 0.2 0.4)) (add-macro (right -> move((create-point 350.0 0.0 0.0)))) (set-axiom (radius(2) color(*green*) right X)) (add-rule (X -> [ apex ] turn(-90) jump(150) turn(90) delay(10))) (add-rule ((delay(x) ? (> x 0) -> delay((1- x))) (delay(x) ? (= x 0) -> X))) (add-option bend (turtle-pitch x)) (add-option blossom (turtle-object "blossom" 7.0)) (add-option leaf (turtle-object "leaf" 7.0 *green*)) (add-rule (apex -> F(8) roll(+fibonacci+) branch apex)) (add-rule (bend(x) -> bend((+ x 1.0)))) (add-rule (branch -> [ bend(18) organ ])) (add-rule (organ -> F(40) blossom(20))) (add-rule ((blossom(x) ? (> x 0) -> blossom((1- x))) (blossom(x) ? (= x 0) -> roll(-90) leaf))) (add-macro (leaf -> obj("leaf" 10.0 *green*))) (read-wavefront "blossom.obj" "blossom.png") (read-wavefront "leaf.obj")) |

To illustrate the development of this plant, "row of plants" rule
`X`

is used. It's just that this time it must be a little
bit more elaborate than in the previous chapter, because now the
L-system has 72 iterations. So
`delay`

, a parametric rule with conditionals, is introduced.
It is very similar to `blossom`

rule which in essence delays
creation of the leaf, while `delay`

delays creation of the
next plant.