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.