simetri.extensions.l_system

Lindenmayer system (L-system) module.

  1"""Lindenmayer system (L-system) module."""
  2
  3from math import ceil
  4
  5from ..graphics.batch import Batch
  6from ..graphics.shape import Shape
  7from .turtle_sg import Turtle
  8
  9
 10def l_system(
 11    rules: dict, axiom: str, angle: float, dist: float, n: int, d_actions: dict = None
 12):
 13    """Generate a Lindenmayer system (L-system) using the given rules.
 14
 15    An L-system is a parallel rewriting system that uses recursive rules to
 16    generate complex patterns. This function interprets the generated string
 17    as turtle graphics commands.
 18
 19    Args:
 20        rules: A dictionary with characters as keys and strings as values.
 21               Each character in the axiom or resulting string will be replaced
 22               by its corresponding rule in each iteration.
 23        axiom: The initial string to start the L-system.
 24        angle: The angle (in degrees) for turtle rotation commands.
 25        dist: The distance for turtle forward/backward movement.
 26        n: The number of iterations to apply the rules.
 27        d_actions: Optional dictionary mapping characters to turtle methods.
 28                  This allows extending the default command set.
 29
 30    Returns:
 31        Batch: A batch of shapes representing the L-system drawing.
 32
 33    Example:
 34        >>> rules = {'F': 'F+F-F-F+F'}  # Koch curve
 35        >>> batch = l_system(rules, 'F', 60, 10, 3)
 36    """
 37
 38    turtle = Turtle(in_degrees=True)
 39    turtle.def_angle = angle
 40    turtle.def_dist = dist
 41
 42    actions = {
 43        "F": turtle.forward,
 44        "B": turtle.backward,
 45        "G": turtle.go,
 46        "+": turtle.left,
 47        "-": turtle.right,
 48        "[": turtle.push,
 49        "]": turtle.pop,
 50        "|": turtle.turn_around,
 51    }
 52    if d_actions:
 53        for key, value in d_actions.items():
 54            method = getattr(turtle, value)
 55            actions[key] = method
 56
 57    def expand(axiom, rules):
 58        """Expand the axiom using the provided rules.
 59
 60        Args:
 61            axiom: The string to expand.
 62            rules: Dictionary of replacement rules.
 63
 64        Returns:
 65            str: The expanded string after applying the rules.
 66        """
 67        return "".join([rules.get(char, char) for char in axiom])
 68
 69    for _ in range(n):
 70        axiom = expand(axiom, rules)
 71
 72    for char in axiom:
 73        actions.get(char, lambda: None)()
 74
 75    # TikZ gives memory error if there are too many vertices in one shape
 76    shapes = Batch()
 77    # tot = len(turtle.current_list)
 78    # part = 200 # partition size
 79    # for i in range(ceil(tot/part)):
 80    #     shape = Shape(turtle.current_list[i*part:(i+1)*part])
 81    #     shapes.append(shape)
 82    if turtle.current_list:
 83        turtle.lists.append(turtle.current_list)
 84    for x in turtle.lists:
 85        shapes.append(Shape(x))
 86    return shapes
 87
 88
 89# rules = {}
 90# rules['F'] = '-F+F+G[+F+F]-'
 91# rules['G'] = 'GG'
 92
 93# axiom = 'F'
 94# angle = 60
 95# dist = 15
 96# n=4
 97
 98# l_system(rules, axiom, angle, dist, n)
 99
100# Examples
101
102# rules = {}
103# rules['X'] = 'XF+F+XF-F-F-XF-F+F+F-F+F+F-X'
104# axiom = 'XF+F+XF+F+XF+F'
105# angle = 60
106# n=2
107
108
109# rules = {}
110# rules['X'] = 'F-[[X]+X]+F[+FX]-X'
111# rules['F'] = 'FF'
112# axiom = 'X'
113# angle = 25
114# n=6
115
116# rules = {}
117# rules['A'] = '+F-A-F+' # Sierpinsky
118# rules['F'] = '-A+F+A-'
119# axiom = 'A'
120# angle = 60
121# n = 7
122
123# rules = {}
124# rules['F'] = 'F+F-F-F+F' # Koch curve 1
125# axiom = 'F'
126# angle = 60
127# n = 6
128
129# rules = {}
130# rules['X'] = 'X+YF+'  # Dragon curve
131# rules['Y'] = '-FX-Y'
132# axiom = 'FX'
133# angle = 90
134# n=10
135
136
137# rules = {}
138# rules['X'] = 'F-[[X]+X]+F[+FX]-X'  # Wheat
139# rules['F'] = 'FF'
140# axiom = 'X'
141# angle = 25
142# n=6
143
144# rules = {}
145# axiom = 'F+F+F+F'
146# rules['F'] = 'FF+F-F+F+FF'
147# angle = 90
148# n=4
def l_system( rules: dict, axiom: str, angle: float, dist: float, n: int, d_actions: dict = None):
11def l_system(
12    rules: dict, axiom: str, angle: float, dist: float, n: int, d_actions: dict = None
13):
14    """Generate a Lindenmayer system (L-system) using the given rules.
15
16    An L-system is a parallel rewriting system that uses recursive rules to
17    generate complex patterns. This function interprets the generated string
18    as turtle graphics commands.
19
20    Args:
21        rules: A dictionary with characters as keys and strings as values.
22               Each character in the axiom or resulting string will be replaced
23               by its corresponding rule in each iteration.
24        axiom: The initial string to start the L-system.
25        angle: The angle (in degrees) for turtle rotation commands.
26        dist: The distance for turtle forward/backward movement.
27        n: The number of iterations to apply the rules.
28        d_actions: Optional dictionary mapping characters to turtle methods.
29                  This allows extending the default command set.
30
31    Returns:
32        Batch: A batch of shapes representing the L-system drawing.
33
34    Example:
35        >>> rules = {'F': 'F+F-F-F+F'}  # Koch curve
36        >>> batch = l_system(rules, 'F', 60, 10, 3)
37    """
38
39    turtle = Turtle(in_degrees=True)
40    turtle.def_angle = angle
41    turtle.def_dist = dist
42
43    actions = {
44        "F": turtle.forward,
45        "B": turtle.backward,
46        "G": turtle.go,
47        "+": turtle.left,
48        "-": turtle.right,
49        "[": turtle.push,
50        "]": turtle.pop,
51        "|": turtle.turn_around,
52    }
53    if d_actions:
54        for key, value in d_actions.items():
55            method = getattr(turtle, value)
56            actions[key] = method
57
58    def expand(axiom, rules):
59        """Expand the axiom using the provided rules.
60
61        Args:
62            axiom: The string to expand.
63            rules: Dictionary of replacement rules.
64
65        Returns:
66            str: The expanded string after applying the rules.
67        """
68        return "".join([rules.get(char, char) for char in axiom])
69
70    for _ in range(n):
71        axiom = expand(axiom, rules)
72
73    for char in axiom:
74        actions.get(char, lambda: None)()
75
76    # TikZ gives memory error if there are too many vertices in one shape
77    shapes = Batch()
78    # tot = len(turtle.current_list)
79    # part = 200 # partition size
80    # for i in range(ceil(tot/part)):
81    #     shape = Shape(turtle.current_list[i*part:(i+1)*part])
82    #     shapes.append(shape)
83    if turtle.current_list:
84        turtle.lists.append(turtle.current_list)
85    for x in turtle.lists:
86        shapes.append(Shape(x))
87    return shapes

Generate a Lindenmayer system (L-system) using the given rules.

An L-system is a parallel rewriting system that uses recursive rules to generate complex patterns. This function interprets the generated string as turtle graphics commands.

Arguments:
  • rules: A dictionary with characters as keys and strings as values. Each character in the axiom or resulting string will be replaced by its corresponding rule in each iteration.
  • axiom: The initial string to start the L-system.
  • angle: The angle (in degrees) for turtle rotation commands.
  • dist: The distance for turtle forward/backward movement.
  • n: The number of iterations to apply the rules.
  • d_actions: Optional dictionary mapping characters to turtle methods. This allows extending the default command set.
Returns:

Batch: A batch of shapes representing the L-system drawing.

Example:
>>> rules = {'F': 'F+F-F-F+F'}  # Koch curve
>>> batch = l_system(rules, 'F', 60, 10, 3)