Interpret L-System strings as turtle commands. Default commandset: F,G - Step forward while drawing f,g - Step forward without drawing -,+ - Yaw around the normal axis v,^ - Pitch around the transverse axis <,> - Roll around the longitudinal axis
| 15 | |
| 16 | |
| 17 | class LSystemInterpeter: |
| 18 | """Interpret L-System strings as turtle commands. |
| 19 | |
| 20 | Default commandset: |
| 21 | |
| 22 | F,G - Step forward while drawing |
| 23 | f,g - Step forward without drawing |
| 24 | -,+ - Yaw around the normal axis |
| 25 | v,^ - Pitch around the transverse axis |
| 26 | <,> - Roll around the longitudinal axis |
| 27 | | - Flip orientation 180 degrees |
| 28 | d,D - Turn drawing off, on |
| 29 | [,] - Push, pop position and orientation onto a stack |
| 30 | """ |
| 31 | |
| 32 | commandsets = frozenset(["default"]) |
| 33 | |
| 34 | def __init__(self, commandset, stepsize, angle): |
| 35 | """Initialize an L-System interpreter with the given commandset and turtle config.""" |
| 36 | if commandset not in self.commandsets: |
| 37 | raise ValueError(f"{commandset=} not in {self.commandsets}.") |
| 38 | self.commandset = commandset |
| 39 | self.turtle = Turtle() |
| 40 | self.stepsize = stepsize |
| 41 | self.angle = angle |
| 42 | self.drawing = True |
| 43 | self.orientation_changed = False |
| 44 | self.active_line = [] |
| 45 | self.stack = [] |
| 46 | |
| 47 | def tokenize(self, commands: io.TextIOWrapper) -> Tokens: |
| 48 | """Tokenize the given input using the configured commandset.""" |
| 49 | if self.commandset == "default": |
| 50 | return self._tokenize_default(commands) |
| 51 | raise ValueError(f"tokenize not supported (yet) for '{self.commandset}'") |
| 52 | |
| 53 | @staticmethod |
| 54 | def _tokenize_default(commands: io.TextIOWrapper) -> Tokens: |
| 55 | # The default set of commands are each a single character. |
| 56 | # So tokenizing is really easy. Yay. |
| 57 | while True: |
| 58 | chunk = commands.read(256) |
| 59 | if not chunk: |
| 60 | break |
| 61 | for char in chunk: |
| 62 | yield char |
| 63 | |
| 64 | def interpret(self, tokens: Tokens) -> Lines: |
| 65 | """Interpret the given tokens as 3D Turtle commands.""" |
| 66 | for line in self._interpret(tokens): |
| 67 | if line is not None: |
| 68 | yield line |
| 69 | |
| 70 | def _interpret(self, tokens: Tokens) -> Lines: |
| 71 | if self.commandset == "default": |
| 72 | yield from self._interpret_default(tokens) |
| 73 | else: |
| 74 | raise ValueError(f"commandset '{self.commandset}' unsupported") |