From 44eae1157f12878a2b52381f4aa5dddd9ce6db5d Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Wed, 26 Apr 2017 21:10:46 -0400 Subject: [PATCH] lindenmayer systems --- axi/__init__.py | 1 + axi/lindenmayer.py | 53 +++++++++++++++++++++++++++++++++++++++++ examples/lindenmayer.py | 21 ++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 axi/lindenmayer.py create mode 100644 examples/lindenmayer.py diff --git a/axi/__init__.py b/axi/__init__.py index f4c1026..aca498d 100644 --- a/axi/__init__.py +++ b/axi/__init__.py @@ -1,5 +1,6 @@ from .device import Device from .drawing import Drawing +from .lindenmayer import LSystem from .paths import simplify_paths, sort_paths, join_paths, load_paths from .planner import Planner from .turtle import Turtle diff --git a/axi/lindenmayer.py b/axi/lindenmayer.py new file mode 100644 index 0000000..6c28a37 --- /dev/null +++ b/axi/lindenmayer.py @@ -0,0 +1,53 @@ +import random +import re + +from math import sin, cos, radians + +from .drawing import Drawing + +class LSystem(object): + def __init__(self, rules): + self.rules = rules + self.pattern = re.compile('|'.join('(%s)' % x for x in rules)) + + def step(self, value): + def func(match): + rule = self.rules[match.group(0)] + if isinstance(rule, basestring): + return rule + return random.choice(rule) + return self.pattern.sub(func, value) + + def steps(self, value, iterations): + for i in range(iterations): + value = self.step(value) + return value + + def run(self, start, iterations, angle=None): + program = self.steps(start, iterations) + angle = angle and radians(angle) + state = (0.0, 0.0, 0.0) + stack = [] + paths = [] + point = (0.0, 0.0) + for instruction in program: + x, y, a = state + if instruction == '-': + a -= angle + elif instruction == '+': + a += angle + elif instruction == '[': + stack.append(state) + elif instruction == ']': + x, y, a = stack.pop() + point = (x, y) + else: + x += cos(a) + y += sin(a) + if paths and point == paths[-1][-1]: + paths[-1].append((x, y)) + else: + paths.append([point, (x, y)]) + point = (x, y) + state = (x, y, a) + return Drawing(paths) diff --git a/examples/lindenmayer.py b/examples/lindenmayer.py new file mode 100644 index 0000000..e85034c --- /dev/null +++ b/examples/lindenmayer.py @@ -0,0 +1,21 @@ +import axi + +def main(): + system = axi.LSystem({ + 'A': 'A-B--B+A++AA+B-', + 'B': '+A-BB--B-A++A+B', + }) + d = system.run('A', 5, 60) + # system = axi.LSystem({ + # 'X': 'F-[[X]+X]+F[+FX]-X', + # 'F': 'FF', + # }) + # d = system.run('X', 6, 20) + d = d.rotate_and_scale_to_fit(12, 8.5, step=90) + # d = d.sort_paths() + # d = d.join_paths(0.015) + d.render().write_to_png('out.png') + axi.draw(d) + +if __name__ == '__main__': + main()