L-Systems
Lindenmayer or L-systems provide a very simple way of coding/drawing complex structures. For those who may be interested the team thought we should demonstrate this simplicity by showing the code used in the patterns available in pzl_jigsaw.
As implemented for the simple patterns used in pzl_jigsaw we have a few commands defined in the dictionary self.commands and a loop to expand the productions for the given number of iterations.
class Lindenmayer(object):
"""
Lindenmayer or L-Systems
We've just implemented a few simple examples using a limited
set of commands.
"""
def __init__(self, axiom, productions, iterations):
self.commands = {
'Draw': set(['F','G','1']),
'Move': 'f',
'Turn left': '+',
'Turn right': '-',
'Push': '[',
'Pop': ']'}
current = axiom
next_bit = ""
for i in range(iterations):
for symbol in range(len(current)):
found_it = False
for letter, production in productions.iteritems():
if (current[symbol] == letter):
next_bit = next_bit + production
found_it = True
break
if not found_it:
next_bit = next_bit + current[symbol]
current = next_bit
next_bit = ""
self.l_system = current
We have a little animal that can follow simple commands and leave behind its trail which will constitute the displayed drawing. With their customary modesty and wit the team have named this class "Hare".
class Hare(object):
"""
Turtle graphics!
current position is position
current direction is angle
current increment is step
angle increment is delta_angle
"""
def __init__(self, commands, step, delta):
self.commands = commands
self.stack = []
delta = math.radians(delta)
self.delta_angle = delta
self.position = complex(0, 0j)
self.angle = 270.0
self.angle = math.radians(self.angle)
self.step = complex(math.cos(self.angle),
math.sin(self.angle))
self.path = []
self.x_min = self.position.real
self.x_max = self.position.real
self.y_min = self.position.imag
self.y_max = self.position.imag
def get_path(self, l_system):
self.stack = []
segment = []
self.path = []
for command in l_system:
if command in self.commands['Draw']:
segment.append(self.position)
self.position += self.step
segment.append(self.position)
self.path.append(segment)
segment = []
elif command == self.commands['Move']:
self.position += self.step
elif command == self.commands['Turn left']:
self.angle += self.delta_angle
self.step = complex(math.cos(self.angle),
math.sin(self.angle))
elif command == self.commands['Turn right']:
self.angle -= self.delta_angle
self.step = complex(math.cos(self.angle),
math.sin(self.angle))
elif command == self.commands['Push']:
item = (self.position, self.angle)
self.stack.append(item)
elif command == self.commands['Pop']:
item = self.stack.pop()
self.position = item[0]
self.angle = item[1]
self.step = complex(math.cos(self.angle),
math.sin(self.angle))
else:
# unrecognised symbol so do nothing
pass
# get range
for segment in self.path:
self.x_min = int(min(self.x_min, segment[0].real, segment[1].real))
self.x_max = int(max(self.x_max, segment[0].real, segment[1].real))
self.y_min = int(min(self.y_min, segment[0].imag, segment[1].imag))
self.y_max = int(max(self.y_max, segment[0].imag, segment[1].imag))
And finally the actual axioms and productions which, when processed as above, create the patterns seen on the screen. Where they have been taken from The Algorithmic Beauty of Plants the page numbers are shown.
def start_game_l(self, name):
"""
Define the codes for various l-systems.
Mostly taken from Prusinkiewicz and Lindenmayer (page nos)
"""
iterations = None
if name == 'Koch a':
#p10 a
iterations = 4
delta = 90
axiom = "F-F-F-F"
productions = {'F': 'FF-F-F-F-F-F+F'}
side_ratio = 1.0
elif name == 'Koch b':
#p10 b
delta = 90
iterations = 4
axiom = "F-F-F-F"
productions = {'F': 'FF-F-F-F-FF'}
side_ratio = 1.0
elif name == 'Koch c':
#p10 c
delta = 90
iterations = 3
axiom = "F-F-F-F"
productions = {'F': 'FF-F+F-F-FF'}
side_ratio = 1.0
elif name == 'Koch d':
#p10 d
delta = 90
iterations = 4
axiom = "F-F-F-F"
productions = {'F': 'FF-F--F-F'}
side_ratio = 1.0
elif name == 'Koch e':
#p10 e
delta = 90
iterations = 5
axiom = "F-F-F-F"
productions = {'F': 'F-FF--F-F'}
side_ratio = 1.0
elif name == 'Koch f':
#p10 f
delta = 90
iterations = 4
axiom = "F-F-F-F"
productions = {'F': 'F-F+F-F-F'}
side_ratio = 1.0
elif name == 'Islands and Lakes':
#p9, 1.8
iterations = 2
delta = 90
axiom = "F+F+F+F"
productions = {'F': 'F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-FFF',
'f': 'ffffff'}
side_ratio = 1.0
elif name == 'Quadratic Koch island':
#p9 a
iterations = 2
delta = 90
axiom = 'F-F-F-F'
productions = {'F' : 'F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F'}
side_ratio = 1.0
elif name == 'Quadratic snowflake':
#p9, 1.7b
iterations = 4
delta = 90
axiom = "-F"
productions = {'F': 'F+F-F-F+F'}
side_ratio = 1.0
elif name == 'Dragon':
#p11 a
iterations = 10
delta = 90
axiom = "F"
productions = {'F': 'F+G+', 'G' : '-F-G'}
side_ratio = 1.0
elif name == 'Sierpinski gasket':
#p11 b
iterations = 8
delta = 60
axiom = "G"
productions = {'F': 'G+F+G', 'G' : 'F-G-F'}
side_ratio = 1.0
elif name == 'Plant 1':
#p25 a
iterations = 5
delta = 27.7
axiom = 'F'
productions = {'F': 'F[+F]F[-F]F'}
side_ratio = 0.4
elif name == 'Plant 2':
#p25 b
iterations = 5
delta = 20
axiom = 'F'
productions = {'F': 'F[+F]F[-F][F]'}
side_ratio = 0.6
elif name == 'Plant 3':
#p25 c
iterations = 4
delta = 22.5
axiom = 'F'
productions = {'F': 'FF-[-F+F+F]+[+F-F-F]'}
side_ratio = 0.7
elif name == 'Plant 4':
#p25 d
iterations = 6
delta = 20
axiom = 'X'
productions = {'X': 'F[+X]F[-X]+X',
'F': 'FF'}
side_ratio = 0.7
elif name == 'Plant 5':
#p25 e
iterations = 7
delta = 25.7
axiom = 'X'
productions = {'X': 'F[+X][-X]FX', 'F': 'FF'}
side_ratio = 0.5
elif name == 'Plant 6':
#p25 f
iterations = 5
delta = 22.5
axiom = 'X'
productions = {'X': 'F-[[X]+X]+F[+FX]-X',
'F': 'FF'}
side_ratio = 0.7
elif name == 'Sierpinski carpet':
iterations = 4
delta = 90.0
axiom = 'F'
productions = {'F': 'F+F-F-F-f+F+F+F-F',
'f': 'fff'}
side_ratio = 1.0
elif name == 'Penrose':
iterations = 5
delta = 36.0
axiom = '[7]++[7]++[7]++[7]++[7]'
productions = {'6': '81++91----71[-81----61]++',
'7': '+81--91[---61--71]+',
'8': '-61++71[+++81++91]-',
'9': '--81++++61[+91++++71]--71',
'1': ''}
side_ratio = 1.0
if iterations != None:
self.do_l_systems(iterations, delta, axiom, productions, side_ratio)
Examples
Below we show videos of various examples of L-Systems patterns being generated by jigsaw.