LSystems (travelling salesman) in py5

Here is a sketch I recently converted from processing.py to py5 (also for me an exploration of NamedTuple) could easily be adapted with other LSystem rules.

import py5
from math import cos, pi, sin, sqrt, radians
from grammar2 import grammar2
from collections import namedtuple

"""
mpeano.py by Martin Prout
LSystem rules from The Euclidean Traveling Salesman Problem.... by MG Norman & P Moscato.
Features a scaling adjustment and turtle reversing, use trignometry rather than processing affine
transforms to calculate the line, uses a grammar module to create production string.
"""

w = 600
h = 600

LSystem = namedtuple('MPeano', 'start, rules, turn_angle_deg')

mpeano = LSystem(
        start = 'XFF2-AFF2-XFF2-AFF',
        rules = dict(
            F = '', 
            Y = 'FFY',
            X = '+!X!FF-BQFI-!X!FF+',
            A = 'BQFI',
            B = 'AFF'
            ),
        turn_angle_deg = 45
    )
    
def settings():
    py5.size(w, h)

def setup():
    sketch_title('MPeano')

    production = grammar2.generate(mpeano, 6)
    py5.background(0, 0, 255)
    py5.stroke(255, 255, 0)
    py5.stroke_weight(3)
    render(production)    

def render(production):
    """
    Render evaluates the production string and calls draw_line
    """
    delta = radians(mpeano.turn_angle_deg)
    distance = 15
    turtle = {'x': w / 10, 'y': h / 10, 'angle': -delta}
    repeat = 1
    for val in production:
        if val == "F":
            turtle = draw_line(turtle, distance)
        elif val == "+":
            turtle['angle'] += delta * repeat
            repeat = 1
        elif val == "-":
            turtle['angle']-= delta * repeat
            repeat = 1
        elif val == "I":
          distance *= 1/sqrt(2)
        elif val == "Q":
            distance *= sqrt(2)
        elif val == "!":
            delta = -delta
        elif (val == '2'):
            repeat = 2
        else:
            pass

def draw_line(turtle, length):
    """
    Draw line utility uses processing 'line' function to draw lines
    """
    turtlecopy = turtle.copy()
    turtlecopy['x'] = turtle['x'] + length * cos(turtle['angle'])
    turtlecopy['y'] = turtle['y'] - length * sin(turtle['angle'])
    py5.line(turtle['x'], turtle['y'], turtlecopy['x'], turtlecopy['y'])
    return turtlecopy

def sketch_title(title):
    py5.get_surface().set_title(title)

py5.run_sketch()

Here is my grammar2 module:-

def generate(lsys, total_iterations):
    lstring = lsys.start
    rules = lsys.rules
    
    for i in range(total_iterations):
        str_buf = []
        for symbol in lstring:          
            str_buf.append(rules.get(symbol, symbol))
        lstring = ''.join(str_buf)          
    return lstring 

6 Likes

I have since updated my py5 lsystem examples at github, but I will be returning to my ruby-processing projects for a while especially with new versions JRuby and jdk17 on the horizon…

Nice working and creative :+1::+1: