Martin McBride, 2019-07-12

Tags generativepy generativepy tutorials trees barnsley ferns turtle recursion l systems

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

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 import drawing from generativepy.drawing import makeSvg 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(canvas): s = AXIOM for i in range(ITERATIONS): s = lsystem(s, RULES) turtle = Turtle(canvas) canvas.stroke(Color('darkblue')) canvas.strokeWeight(2) turtle.moveTo(SIZE/2, 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) makeSvg("lsystem-binary-tree.svg", draw, pixelSize=(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 import drawing from generativepy.drawing import makeSvg 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(canvas): s = AXIOM for i in range(ITERATIONS): s = lsystem(s, RULES) turtle = Turtle(canvas) canvas.stroke(Color('darkgreen')) canvas.strokeWeight(2) turtle.moveTo(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) makeSvg("lsystem-tree.svg", draw, pixelSize=(SIZE, SIZE))

Copyright (c) Martin McBride 2020