TurtLSystems 0.1.7
Tool to draw L-systems via turtle graphics and save them as pngs and gifs.
License: MIT
Keywords: lindenmayer,system,lsystem,turtle,graphics,fractal,pattern,art,png,gif,animation,Sierpinski,Koch,Cantor,dragon

<!-- markdownlint-disable-next-line MD041 -->
[![Python version support](https://img.shields.io/pypi/pyversions/TurtLSystems)](https://www.python.org/downloads/)
<!-- [![Test Coverage](https://raw.githubusercontent.com/discretegames/TurtLSystems/main/coverage.svg)](https://github.com/discretegames/TurtLSystems/blob/main/coverage.txt) -->

<!-- markdownlint-disable MD033 -->
# **[TurtLSystems][pypi]**

[Intro][intro] | [Installation][install] | [Usage][usage] | [L-System Instructions][inst] | [Examples][ex] | [Links][links]

[TurtLSystems][pypi] is a Python 3 package for drawing [L-systems][wiki] via [turtle graphics][turtle]
with easy ways to output png images and gif animations of the patterns generated.
L-systems or Lindenmayer systems are a simple string rewriting systems where every character in an initial start
string is repeatedly overwritten with a predetermined replacement. This can lead to very elaborate patterns that
create fractals or mimic nature.

[![Sierpinski arrowhead curve example gif][example]][example]

[See more examples below.][ex]

## Quick Start

Do `pip install TurtLSystems` then run the following Python to draw a Sierpinski triangle:

```py
from TurtLSystems import *
draw()
wait()
```

<!-- markdownlint-disable-next-line MD025 -->
# Installation (supports Python 3.6+)

Make sure you have [Python][py] installed then run this command in a terminal:

```text
pip install TurtLSystems
```

If that doesn't work try one of the following lines:

```text
pip3 install TurtLSystems
python -m pip install TurtLSystems
python3 -m pip install TurtLSystems
py -m pip install TurtLSystems
```

For png and gif output the [Ghostscript][gs] conversion tool is required. It can be downloaded [here][gsd]. Ghostscript is
what takes the .eps files generated by turtle graphics and turns them into pngs which are then made into gifs.

The Python imaging library [Pillow][pillowdoc] (PIL) is also required for png and gif output but it will be installed
automatically when you install TurtLSystems.

[TurtLSystems on PyPI][pypi]  | [TurtLSystems on GitHub][gh] | [Ghostscript Download][gsd]

<!-- markdownlint-disable-next-line MD025 -->
# Usage

TurtLSystems has only four functions, [`init`][init], [`draw`][draw], [`wait`][wait], and [`lsystem`][lsystem],
and only the `draw` function is strictly necessary.

The code that generated [the blue gif][example] of the [Sierpinski arrowhead curve][arrowheadwiki] above is simply:

```py
from TurtLSystems import *
draw('A', 'A B-A-B B A+B+A,', 5, 60, 7, 2, (200, 220, 255), None, (36, 8, 107),
     heading=60, red_increment=2, gif='example.gif', max_frames=250, duration=30)
```

A typical program might look like:

```py
from TurtLSystems import init, draw, wait
init(...)  # fill in args
draw(...)  # fill in args
wait()
```

To ignore the errors that get thrown when exiting the window while the turtle is drawing use try/except with `Exit`:

```py
from TurtLSystems import init, draw, wait, Exit
try:
    init(...)  # fill in args
    draw(...)  # fill in args
    wait()
except Exit:
    pass
```

## `init` Function

Initializes global TurtLSystems properties.
Calling this is optional and only needed when customization is desired.
If used it should only be called once and placed before all calls to `draw` and `wait`.

Returns nothing.

### Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| window_size<br>`(0.75, 0.75)` | The size of the window. Use integers for pixel dimensions. Use floats for a percentage of the screen size.<br>`Tuple[int \| float, int \| float]`
| window_title<br>`"TurtLSystems"` | The title of the window.<br>`str`
| background_color<br>`(0, 0, 0)` | The background color of the window. A 0-255 rgb tuple. May be changed later by draw calls.<br>`Tuple[int, int, int]`
| background_image<br>`None` | The file path to a background image for the window.<br>`str \| None`
| window_position<br>`None` | The top and left screen coordinates of the window, or None for centered.<br>`Tuple[int \| None, int \| None] \| None`
| canvas_size<br>`None` | The size of the drawing canvas when an area larger than the window size is desired.<br>`Tuple[int \| None, int \| None] \| None`
| ghostscript<br>`None` | The path to or command name of ghostscript. When None, an educated guess of the path is made on Windows and 'gs' is used on Mac/Linux. [Ghostscript][gsd] is the image conversion tool required for png and gif output.<br>`str \| None`
| logo_mode<br>`False` | Whether the turtle graphics coordinates mode is 'standard' or 'logo'. Defaults to standard. In standard mode an angle of 0 points rightward and positive angles go counterclockwise. In logo mode an angle of 0 points upward and positive angles go clockwise.<br>`bool`
| delay<br>`None` | The turtle graphics animation delay in milliseconds. None for default value.<br>`int \| None`
| silent<br>`False` | Whether to silence all messages and warnings produced by TurtLSystems.<br>`bool`

If you have issues where png or gif output is cut off and doesn't match what is shown in the window, try increasing
the canvas size to much larger than needed like `init(canvas_size(2000, 2000))`.
I experienced this issue in Linux Mint and am not sure why it happened.

## `draw` Function

Opens a turtle graphics window and draws an L-system pattern based on the arguments provided.
When called multiple times all patterns are drawn to the same canvas.

All 54 arguments are optional but `start` and `rules` are the most important because they define the L-system,
and `level` defines how many expansion steps take place. On an expansion step, every character in `start` is
replaced with what it maps to in `rules` (or left unchanged if not present) resulting in a new `start` string.
The characters of `start` after the last expansion are the [instructions][inst] the turtle follows to draw a pattern.
See the [`lsystem`][lsystem] function documentation for specifics on what each character does as an instruction.

Call `draw()` by itself to see an example Sierpinski triangle pattern.

In the descriptions below, "on X" is short for "when the character X is encountered in the L-system string".

Returns a 2-tuple of the final L-system string and the turtle graphics Turtle object used to draw the pattern
(`Tuple[str, turtle.Turtle]`).

### Positional Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| start<br>`'F+G+G'` | The initial string or axiom of the L-system. Level 0.<br>`str`
| rules<br>`'F F+G-F-G+F G GG'` | Dictionary that maps characters to what they are replaced with in the L-system expansion step. May also be a string where whitespace separated pairs of substrings correspond to the character and its replacement. For example `{'A': 'AB', 'B': 'B+A'}` and `'A AB B B+A'` represent the same rules.<br>`Dict[str, str] \| str`
| level<br>`4` | The number of L-system expansion steps to take, i.e. how many times to apply `rules` to `start`.<br>`int`
| angle<br>`120` | The angle to turn by on `+` or `-`. In degrees by default but the `circle` arg can change that.<br>`float`
| length<br>`20` | The distance in pixels to move forward by on letters. The length step.<br>`float`
| thickness<br>`1` | The line width in pixels. May be any non-negative number.<br>`float`
| color<br>`(255, 255, 255)` | The line color. A 0-255 rgb tuple or None to hide all lines. Reselected on `0`.<br>`Tuple[int, int, int] \| None`
| fill_color<br>`(128, 128, 128)` | The fill color for `{}` polygons, `@` dots, and turtle shapes. A 0-255 rgb tuple or None to hide all fills. Reselected on `1`.<br>`Tuple[int, int, int] \| None`
| background_color<br>`None` | The background color of the window. A 0-255 rgb tuple or None to leave unchanged.<br>`Tuple[int, int, int] \| None`
| asap<br>`False` | When True the draw will happen as fast as possible, ignoring `speed` arg and the `delay` arg of `init`.<br>`bool`

### Customization Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| colors<br>`None` | When an iterable such as a list, `color` and `fill_color` are ignored and the first 10 colors of the list become the colors that are selected on `0` through `9`. Each may be a 0-255 rgb tuple or None for no color. The list of defaults below is used to fill out anything missing if less than 10 colors are given. When `colors` is None, `color` and `fill_color` are used replace slots 0 and 1 respectively.<br>`Iterable[Tuple[int, int, int] \| None] \| None`
| position<br>`(0, 0)` | The initial (x, y) position of the turtle.<br>`Tuple[float, float]`
| heading<br>`0` | The initial angle the turtle points in.<br>`float`
| scale<br>`1'` | A factor to scale the size of the pattern by. May be negative. Specifically, `length`, `position`, and `length_increment` are multiplied by `scale` and `thickness` and `thickness_increment` are multiplied by `abs(scale)`.<br>`float`
| prefix<br>`''` | An L-system string that does not undergo expansion prepended to the fully expanded `start` string.<br>`str`
| suffix<br>`''` | An L-system string that does not undergo expansion appended to the fully expanded `start` string<br>`str`
| max_chars<br>`None` | The maximum number of characters in the final L-system string (`prefix` + expanded `start` + `suffix`) to follow the instructions for, or None for no limit.<br>`int \| None`
| max_draws<br>`None` | The maximum number of "draw" operations to do or None for no limit. A "draw" operation is something that draws to the canvas, namely lines from uppercase letters, dots from `@`, and finished polygons from `}`.<br>`int \| None`

**Default Colors:**

- ![0](https://via.placeholder.com/10/ffffff.png?text=+) `0 = (255, 255, 255)` white
- ![1](https://via.placeholder.com/10/808080.png?text=+) `1 = (128, 128, 128)` gray
- ![2](https://via.placeholder.com/10/ff0000.png?text=+) `2 = (255, 0, 0)` red
- ![3](https://via.placeholder.com/10/ff8000.png?text=+) `3 = (255, 128, 0)` orange
- ![4](https://via.placeholder.com/10/ffff00.png?text=+) `4 = (255, 255, 0)` yellow
- ![5](https://via.placeholder.com/10/00ff00.png?text=+) `5 = (0, 255, 0)` green
- ![6](https://via.placeholder.com/10/00ffff.png?text=+) `6 = (0, 255, 255)` cyan
- ![7](https://via.placeholder.com/10/0000ff.png?text=+) `7 = (0, 0, 255)` blue
- ![8](https://via.placeholder.com/10/8000ff.png?text=+) `8 = (128, 0, 255)` purple
- ![9](https://via.placeholder.com/10/ff00ff.png?text=+) `9 = (255, 0, 255)` magenta

### Turtle Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| speed<br>`'fastest'` | The speed of the turtle. An integer from 1 to 10 for slowest to fastest or 0 for the fastest possible. Strings 'slowest', 'slow', 'normal', 'fast', and 'fastest' correspond to 1, 3, 6, 10, and 0 respectively.<br>`int \| str`
| show_turtle<br>`False` | Whether the turtle is shown or not.<br>`bool`
| turtle_shape<br>`'classic'` | The shape of the turtle. Can be 'classic', 'arrow', 'turtle', 'circle', 'square', or 'triangle'.<br>`str`
| circle<br>`360` | The number of degrees to consider a full circle as having. Use `2*math.pi` to work in radians.<br>`float`

### Increment Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| angle_increment<br>`15` | The amount to increment or decrement `angle` by on `)` or `(`.<br>`float`
| length_increment<br>`5` | The amount to increment or decrement `length` by on `^` or `%`.<br>`float`
| length_scalar<br>`2` | The amount to multiply or divide `length` by on `*` or `/`.<br>`float`
| thickness_increment<br>`1` | The amount to increment or decrement the `thickness` by on `>` or `<`. Thickness won't go below 0.<br>`float`
| red_increment<br>`1` | The amount to increment or decrement the red channel of the line or fill color by on `,` or `.`. Channel will stay in the range [0, 255]. This can be a float to allow for gradual changes.<br>`float`
| green_increment<br>`1` | The amount to increment or decrement the green channel of the line or fill color by on `;` or `:`. Channel will stay in the range [0, 255]. This can be a float to allow for gradual changes.<br>`float`
| blue_increment<br>`1` | The amount to increment or decrement the blue channel of the line or fill color by on `?` or `!`. Channel will stay in the range [0, 255]. This can be a float to allow for gradual changes.<br>`float`

### Text Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| text<br>`None` | A string of text to add to the canvas. Patters are drawn on top of it. None for no text.<br>`str \| None`
| text_color<br>`(255, 255, 255)` | The color of the text. A 0-255 rgb tuple or None to hide the text.<br>`Tuple[int, int, int] \| None`
| text_position<br>`(0, -200)` | The (x, y) position of the text.<br>`Tuple[int, int]`
| text_align<br>`'center'` | The alignment of the text. Either 'left', 'center', or 'right'.<br>`str`
| font<br>`'Arial` | The font name of the text.<br>`str`
| font_size<br>`16` | The font size of the text. Measured in points if positive or in pixels if negative.<br>`int`
| font_style<br>`'normal'` | The styling to apply to the font of the text. 'normal', 'bold', 'italic', 'underline' and 'overstrike' are all possibilities and can be combined like 'bold italic'.<br>`str`

### Png and Gif Frame Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| png<br>`None` | The file path of where to save the final drawing as a png image, or None for no png output. A file extension is not required.<br>`str \| None`
| padding<br>`10` | The amount of padding in pixels to frame the drawing with on all sides in png and gif output. Negative values are valid. When None, no padding happens and the entire canvas area is saved. Note that padding very large blank areas can be slow.<br>`int \| None`
| transparent<br>`False` | When True, the background of png and gif output is transparent rather that the window background color.<br>`bool`
| antialiasing<br>`4` | An integer 1, 2, or 4 that specifies how jagged pixel edges will be in png and gif output. 1 for the most jagged, 4 for the least jagged. Note that the window canvas does not respect this option.<br>`int`
| output_scale<br>`1` | A factor to scale png and gif dimensions by. Vector graphics are used so there is no quality loss from scaling up, though padding may take longer.<br>`float`

### Gif Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| gif<br>`None` | The file path of where to save the drawing as a gif animation, or None for no gif output. A file extension is not required.<br>`str \| None`
| frame_every<br>`1` | When an integer, this is the number of "draw" operations to wait for between recording of gif frames. A "draw" operation is something that draws to the canvas, namely lines from uppercase letters, dots from `@`, and finished polygons from `}`. When a collection such as a string, frames are recorded whenever L-system characters in the collections are encountered.<br>`int \| Collection[str]`
| max_frames<br>`100` | The maximum number of frames of the gif or None for no limit. Useful to prevent accidental creation of very long gifs.<br>`int \| None`
| duration<br>`20` | The duration in milliseconds of each gif frame. Should be 20 or above and divisible by 10.<br>`int`
| pause<br>`500` | The amount of additional time in milliseconds to pause on the last frame of the gif.<br>`int`
| defer<br>`0` | The amount of additional time in milliseconds to add to the first frame of the gif.<br>`int`
| loops<br>`None` | The number of times the gif loops or 0 or None for no limit.<br>`int \| None`
| reverse<br>`False` | Whether to reverse the frames of the gif.<br>`bool`
| alternate<br>`False` | When True, the central gif frames are copied and appended in reverse to the end of the gif, making it cycle forwards and backwards. For example, a sequence 01234 would become 01234321.<br>`bool`
| growth<br>`False` | When True, the gif consist of frames made from the final patterns of every expansion of the `start` string, from level 0 to `level` inclusive. `frame_every` and `max_frames` are ignored in this mode.<br>`bool`

### Advanced Arguments

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| tmpdir<br>`None` | The path to a directory to put all .eps and .png files in during the generation of png and gif output. Useful if you need the gif frames as pngs. When None these files are put in a temporary place and deleted.<br>`str \| None`
| callback<br>`None` | When not None, a function that is called for every character in the L-system string the turtle encounters. Two arguments are given, the current character and the Turtle object. If True is returned the turtle stops.<br>`Callable[[str, turtle.Turtle], bool \| None] \| None`
| skip_init<br>`False` | Whether to skip calling `init` when it hasn't been called already.<br>`bool`

## `wait` Function

Keeps window open. Calling this is optional.
If used it should only be called once and be placed after all calls to `draw`.

Returns nothing.

| Name<br>`Default` | Description<br>`Type` |
| --------------------------- | --------------------- |
| exit_on_click<br>`True` | Whether the window can be closed by clicking anywhere.<br>`bool`
| skip_init<br>`False` | For advanced use. Whether to skip calling `init` when it hasn't been called already.<br>`bool`

## `lsystem` Function

Expands the L-system string `start` according to `rules` `level` number of times, returning the resulting string.

The `draw` function calls this internally. You do you not need to call it unless you want to.

The types and descriptions of `start`, `rules`, and `level`
in this function match exactly what they are in the [`draw`][draw] function.

# L-System Instructions

Every non-whitespace printable ASCII character in an L-system string is an instruction as follows:

```plaintext
A-Z     Move forward by length step, drawing a line.
a-z     Move forward by length step, not drawing a line.
+       Turn positively by turning angle.
-       Turn negatively by turning angle.
|       Make a half turn (turn by 180°).
&       Swap meaning of + and -.
`       Swap meaning of uppercase and lowercase.
@       Draw a fill color dot with radius max(2*thickness, 4+thickness).
{       Start a polygon.
}       Finish a polygon, filling it with fill color.
[       Push current turtle state onto the stack (position, heading, colors, etc).
]       Pop current turtle state off the stack, if not empty.
$       Clear stack.
)       Increment turning angle by angle_increment.
(       Decrement turning angle by angle_increment.
~       Reset turning angle back to its initial value.
*       Multiply length step by length_scalar.
/       Divide length step by length_scalar.
^       Increment length step by length_increment.
%       Decrement length step by length_increment.
_       Reset length step back to its initial value.
>       Increment line thickness by thickness_increment.
<       Decrement line thickness by thickness_increment. Won't go below 0.
=       Reset line thickness back to its initial value.
'       Reset heading back to its initial value.
"       Reset position back to its initial value.
0-9     Change color to the color at this index of colors list.
,       Increment current color's 0-255 red channel by red_increment.
.       Decrement current color's 0-255 red channel by red_increment.
;       Increment current color's 0-255 green channel by green_increment.
;       Decrement current color's 0-255 green channel by green_increment.
?       Increment  current color's 0-255 blue channel by blue_increment.
!       Decrement  current color's 0-255 blue channel by blue_increment.
#       The next 0123456789.,:;!? apply to fill color rather than line color.
\       Stop executing all instructions immediately.
```

Any characters not mentioned are ignored and have no effect.
Many of the instructions are based on [Paul Bourke's 1991 *L-System User Notes*][paul].

# Examples

The `draw(...)` line of code in each example should be used as the middle line here:

```py
from TurtLSystems import draw, wait
draw(...)
wait()
```

## Sierpinski Triangle

```py
draw('F+G+G', 'F F+G-F-G+F G GG', 5, 120, 10, color=(255, 255, 0), asap=True, png='sierpinski')
```

[![Sierpinski triangle example][sierpinski]][sierpinski]

## Sierpinski Arrowhead Curve

```py
draw('A', 'A B-A-B B A+B+A', 5, 60, 10, color=(255, 0, 0), asap=True, heading=60, png='arrowhead')
```

[![Sierpinski arrowhead curve example][arrowhead]][arrowhead]

## Koch Snowflake

```py
draw('F--F--F', 'F F+F--F+F', 5, 60, 1.5, color=(0, 128, 255), asap=True, heading=60, png='koch')
```

[![Koch snowflake example][koch]][koch]

## Koch Anti-Snowflake

```py
draw('F++F++F', 'F F+F--F+F', 5, 60, 1.5, color=(0, 0, 255), asap=True, png='antikoch')
```

[![Koch anti-snowflake example][antikoch]][antikoch]

## Square Koch Curve

```py
draw('F', 'F F+F-F-F+F', 5, 90, 3, color=(0, 255, 255), asap=True, png='squarekoch')
```

[![Square Koch curve example][squarekoch]][squarekoch]

## Dragon Curve

```py
draw('F', 'F F+G G F-G', 12, 90, 5, color=(255, 0, 255), asap=True, png='dragon')
```

[![Dragon curve example][dragon]][dragon]

## Fractal Tree

```py
draw('A', 'A B[-A]+A B BB', 7, 45, 3, color=(255, 128, 0), asap=True, heading=90, png='tree')
```

[![Fractal tree example][tree]][tree]

# Fractal Plant

```py
draw('X', 'X F+[[X]-X]-F[-FX]+X F FF', 5, 20, 5, color=(0, 255, 0), asap=True, heading=88, png='plant')
```

[![Fractal plant example][plant]][plant]

---

Generalized versions of all the examples above and more are available in
[examples.py][expy] which can be imported and run with:

```py
from TurtLSystems.examples import *
sierpinski_triangle()
# sierpinski_arrowhead()
# square_koch_curve()
# koch_snowflake()
# koch_snowflake(anti=True)
# dragon_curve()
# cantor_set()
# tree()
# plant()
# gradient()
```

Simply uncomment the desired examples and run the file.
The exact examples present in this readme can be found all in one place [here][rexpy].

# Links

[TurtLSystems PyPI Package Page][pypi]

[TurtLSystems GitHub Repo][gh]

[TurtLSystems License][license]

[L-Systems Wikipedia][wiki]

[Ghostscript][gs]

[Pillow][pillow]

[pypi]: https://pypi.org/project/TurtLSystems/
[tpypi]: https://test.pypi.org/project/TurtLSystems/
[wiki]: https://en.wikipedia.org/wiki/L-system
[turtle]: https://docs.python.org/3/library/turtle.html
[gs]: https://ghostscript.com/
[gsd]: https://ghostscript.com/releases/gsdnld.html
[gh]: https://github.com/discretegames/TurtLSystems
[init]: https://github.com/discretegames/TurtLSystems#init-function
[draw]: https://github.com/discretegames/TurtLSystems#draw-function
[wait]: https://github.com/discretegames/TurtLSystems#wait-function
[lsystem]: https://github.com/discretegames/TurtLSystems#lsystem-function
[intro]: https://github.com/discretegames/TurtLSystems#turtlsystems
[install]: https://github.com/discretegames/TurtLSystems#installation-supports-python-36
[usage]: https://github.com/discretegames/TurtLSystems#usage
[links]: https://github.com/discretegames/TurtLSystems#links
[ex]: https://github.com/discretegames/TurtLSystems#examples
[inst]: https://github.com/discretegames/TurtLSystems#l-system-instructions
[py]: https://www.python.org/downloads/
[paul]: http://paulbourke.net/fractals/lsys
[license]: https://github.com/discretegames/TurtLSystems/blob/main/LICENSE.txt
[pillow]: https://pypi.org/project/Pillow/
[pillowdoc]: https://pillow.readthedocs.io/en/stable/
[arrowheadwiki]: https://en.wikipedia.org/wiki/Sierpi%C5%84ski_curve#Arrowhead_curve

[example]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/example.gif
[sierpinski]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/sierpinski.png
[arrowhead]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/arrowhead.png
[koch]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/koch.png
[antikoch]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/antikoch.png
[squarekoch]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/squarekoch.png
[dragon]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/dragon.png
[tree]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/tree.png
[plant]: https://raw.githubusercontent.com/discretegames/TurtLSystems/main/examples/plant.png
[expy]: https://github.com/discretegames/TurtLSystems/blob/main/src/TurtLSystems/examples.py
[rexpy]: https://github.com/discretegames/TurtLSystems/blob/main/examples/readme_examples.py