Martin McBride, 2020-10-10

Tags tree barnsley fern turtle recursion l system

Categories generativepy generative art

In this post we will look further into L Systems and see how they can be used to tree and fern like structures using generativepy. We will make use of the simple turtle system developed in a previous article.

We will start by creating a simple binary tree like this:

This pattern is the 6th generation of a pattern that starts out as a simple Y shape. Unlike simpler L Systems, it exhibits branching. There are 64 "leaves" in the images. Starting from the first generation (a Y shape) which has 2 leaves, the number of leaves doubles on each generation (2 to the power 6 is 64).

To implement branching, we need to add two extra characters to our L System grammar:

`[`

doesn't draw anything, but it saves the current turtle position and heading`]`

also doesn't draw anything, but restores the previous turtle position and heading.

So the string `[ABC]XYZ`

saves the initial position then draws `ABC`

(whatever that might be). It then restores the previous position, and draws `XYZ`

starting from the original position and heading. Notice that `ABC`

and `XYZ`

just represent whatever real operations you might want to perform.

The save and restore operations use a stack, so you can save multiple positions, and they will be restored in the reverse order.

Here are the rules for the binary tree:

F becomes G[+F]-F G becomes GG + becomes + - becomes - [ becomes [ ] becomes ]

`F`

and `G`

both draw a line length 10. `+`

and `-`

turn left or right by 45 degrees (`pi/4`

).

It is worth looking at the first pass. The initial string `F`

becomes `G[+F]-F`

. Here is what this draws:

Imagine we start of at point A, with the turtle heading upwards.

`G`

moves 10 units upwards to point B`[`

saves the current position (B) and heading (upwards)`+F`

turns the turtle 45 degrees left and draws 10 units to point C`]`

restores the previous position. The turtle is placed back at point B, heading upwards, without drawing anything`-F`

turns the turtle 45 degrees right and draws 10 units to point D

Here is the binary tree Python code:

from generativepy.drawing import make_svg, setup from generativepy.color import Color from turtle import Turtle import math AXIOM = 'F' RULES = { 'F' : 'G[+F]-F', 'G' : 'GG', '[' : '[', ']' : ']', '+' : '+', '-' : '-' } ITERATIONS = 6 ANGLE = math.pi/4 LENGTH = 10 SIZE=800 def lsystem(start, rules): out = '' for c in start: s = rules[c] out += s return out def draw(ctx, width, height, frame_no, frame_count): setup(ctx, width, height, background=Color(1)) s = AXIOM for i in range(ITERATIONS): s = lsystem(s, RULES) turtle = Turtle(ctx) turtle.move_to(SIZE/4, SIZE-10) turtle.left(math.pi/2) for c in s: if c=='F': turtle.forward(LENGTH) if c=='G': turtle.forward(LENGTH) elif c=='[': turtle.push() elif c==']': turtle.pop() elif c=='+': turtle.left(ANGLE) elif c=='-': turtle.right(ANGLE) make_svg("lsystem-tree.svg", draw, SIZE, SIZE)

The main things to note here are:

- The turtle is moved to position
`(SIZE/2, SIZE-10)`

, the centre-bottom of the canvas, and turned to point upwards, just after the turtle is created. - We have added the
`[`

and`]`

cases using the existing`Turtle`

methods`push`

and`pop`

.

Here is the result:

The trees are all at the same scale, except the sixth iteration, which is scaled down very slightly (to 75% of its original size) to fit.

We will modify the previous code slightly to create a more realistic looking L System plant.

The first rule is chnaged to:

F becomes G+[[F]-F]-G[-GF]+F

and the angle is changed to 20 degrees. This gives the following basic shape (ie the shape after 1 iteration):

You can follwo this through, step by step, like we did for the binary tree, if you wish. The shape creates after 6 iterations is:

The final code is below. All that has really changed are the rules and angle, a bit of adjustment of the size and position, and all importantly making the plant green instead of blue.

from generativepy.drawing import make_svg, setup from generativepy.color import Color from turtle import Turtle import math AXIOM = 'F' RULES = { 'F' : 'G+[[F]-F]-G[-GF]+F', 'G' : 'GG', '[' : '[', ']' : ']', '+' : '+', '-' : '-' } ITERATIONS = 6 ANGLE = 20*math.pi/180 LENGTH = 5 SIZE=800 def lsystem(start, rules): out = '' for c in start: s = rules[c] out += s return out def draw(ctx, width, height, frame_no, frame_count): setup(ctx, width, height, background=Color(1)) s = AXIOM for i in range(ITERATIONS): s = lsystem(s, RULES) turtle = Turtle(ctx) turtle.move_to(SIZE/4, SIZE-10) turtle.left(75*math.pi/180) for c in s: if c=='F': turtle.forward(LENGTH) if c=='G': turtle.forward(LENGTH) elif c=='[': turtle.push() elif c==']': turtle.pop() elif c=='+': turtle.left(ANGLE) elif c=='-': turtle.right(ANGLE) make_svg("lsystem-fern.svg", draw, SIZE, SIZE)

Copyright (c) Axlesoft Ltd 2020