l-system-fractals 0.1.2

CLI utility to produce L-System fractals
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# JSON file format and command-line interface for this program

## Basic command-line usage

```console
$ l-system-fractals filename.json
```

## `examples` directory

The `examples` directory has a number of JSON files and the resulting outputs.

## Basic idea for plotting

This program plots using [turtle
graphics](https://en.wikipedia.org/wiki/Turtle_graphics). At every time, the
turtle has a position and is pointing at a certain angle. The program will
interpret a string such as

```
A[-DA+C+A+C]+C-C-C+A[-DDDA[-DA+C+A+C]+C-C-C+A+CCC+A[-DA+C+A+C]+C-C-C+A+CCC]+CCC
-CCC-CCC+A[-DA+C+A+C]+C-C-C+A+BACBA[-DA+C+A+C]+C-C-C+ACCCBACB+A[-DA+C+A+C]+C-C-
C+A[-DDDA[-DA+C+A+C]+C-C-C+A+CCC+A[-DA+C+A+C]+C-C-C+A+CCC]+CCC-CCC-CCC+A[-DA+C+
A+C]+C-C-C+A+BACBA[-DA+C+A+C]+C-C-C+ACCCBACB
```

and turn it into a plot such as:

<img src="example1.svg" alt="A rectangular shape with squares removed and square islands around it" width="344" height="740" />

In this example:

- `A` and `C` instruct the program to draw forward 1.0 units from the turtle's
  current location (moving the turtle)
- `B` instructs the program to draw forward 2.0 units from the turtle's current
  location (moving the turtle)
- `D` instructs the program to move the turtle forward 1.0 units without drawing
- `[` instructs the program to push the current location and angle of the turtle
  onto the stack
- `]` instructs the program to pop a location/angle pair of the stack and
  position the turtle in that location pointing in that angle
- '+' instructs the program to rotate the turtle's heading by &pi;/2 radians
  (90&deg;) clockwise
- '-' instructs the program to rotate the turtle's heading by &pi;/2 radians
  (90&deg;) counter-clockwise

The magic happens when we apply rewriting rules to a given string. For example,
say we start with `A+B+A+B`, which corresponds to this shape:

<img src="example2.svg" alt="A rectangle" width="390" height="740" />

Next, suppose we apply the following rewriting rules:

- `A` &#x21a6; `A[-DA+C+A+C]+C-C-C+A`
- `B` &#x21a6; `BACB`
- `C` &#x21a6; `CCC`
- `D` &#x21a6; `DDD`

Then applying the rewriting rules once to the string `A+B+A+B` will result in:

```
A[-DA+C+A+C]+C-C-C+A+BACB+A[-DA+C+A+C]+C-C-C+A+BACB
```

The corresponding plot is:

<img src="example3.svg" alt="The rectangle above, except with a smaller square taken out of the top and bottom, and moved out to float one unit away" width="250" height="740" />

Applying the rewriting rules again gives us the initial example above.

## Commands

The structure of the JSON file depends on the _command_ you want to run. There
are three available commands:

- `PlotOne`: Make a plot of a specified iteration of the fractal
- `PlotMany`: Make a plot of several specified iterations of the fractal
- `OutputStrings`: Run the replacement rules on a string and output the string
  to a file.

### `PlotOne` JSON structure

Use the `PlotOne` command to plot a single specified iteration of the fractal.

<img src="examples/koch.svg" alt="The 6th iteration of a Koch snowflake" width="707" height="800" />

A JSON file implementing the `PlotOne` command will have the following keys:

- `command`: the string `"PlotOne"` in this case
- `rules`: the rules given in the format described below
- `iteration`: an nonnegative integer, specifying how many times to iterate the
  rewriting rules
- `plot_param`: the plot parameters in the format described below
- `filename`: a string with the desired filename of the resulting SVG
- `comment` (_optional_): a string, ignored by the program

#### Example

```json
{
  "command": "PlotOne",
  "rules": [...],
  "iteration": 3,
  "plot_param": [...],
  "filename": "output_file.svg",
  "comment": "blah, blah, blah"
}
```

### `PlotMany` JSON structure

Use the `PlotMany` command to plot a multiple specified iterations of the
fractal. The plots will be placed in a grid.

<img src="examples/hilbert.svg" alt="The first 6 iterations of a space-filling Hilbert curve" width="600" height="400" />

A JSON file implementing the `PlotMany` command will have the following keys:

- `command`: the string `"PlotMany"` in this case
- `rules`: the rules given in the format described below
- `iteration`: an nonnegative integer, specifying how many times to iterate the
  rewriting rules
- `iterations`: a list of nonnegative integers, specifying how many times to
  iterate the rewriting rules
- `row_len`: a positive integer specifying how many iterations to plot in each
  row of the grid
- `plot_param`: the plot parameters in the format described below
- `filename`: a string with the desired filename of the resulting SVG
- `comment` (_optional_): a string, ignored by the program

#### Example

```json
{
  "command": "PlotMany",
  "rules": [...],
  "iterations": [1, 2, 3, 4],
  "row_len": 2,
  "plot_param": [...],
  "filename": "output_file.svg",
  "comment": "blah, blah, blah"
}
```

### `OutputStrings` JSON structure

Use the `OutputStrings` command to apply rewriting rules to a string and write
the resulting string to a file.

A JSON file implementing the `OutputStrings` command will have the following
keys:

- `command`: the string `"OutputStrings"` in this case
- `rules`: an object mapping one-character strings to replacement strings, e.g.
  `{"A": "AB", "B": "BAB"}`.
- `start`: the string on which to start iterating
- `iteration`: an nonnegative integer, specifying how many times to
  iterate the rewriting rules
- `filename`: a string with the desired filename of the resulting text
- `comment` (_optional_): a string, ignored by the program

#### Example

```console
$ cat output_strings_example.json
{
  "command": "OutputStrings",
  "rules": {"A": "AB", "B": "BAB"},
  "start": "A",
  "iteration": 3,
  "filename": "output.txt"
}
$ l-system-fractals output_strings_example.json
Output written to: output.txt
$ cat output.txt
ABBABBABABBAB
```

## `rules` formats

There are three formats for the `rules` field used by `PlotOne` and `PlotMany`,
increasing in complexity and flexibility: `Simple`, `Advanced`, and `Full`.

### `Simple` rules format

The `Simple` format provides the most basic specification syntax for rules. It
allows drawing line segments of one length (no moving without drawing),
specifying a single angle, and a single stack for location/angle pairs. The keys
appearing are:

- `type`: in this case, the string `"Simple"`
- `replacement`: an object mapping one-character strings to replacement strings, e.g.
  `{"A": "AB", "B": "BAB"}`.
- `angle_numerator`, `angle_denominator`: these are used to specify an angle,
  which will be &pi; &times; `angle_numerator` / `angle_denominator`. The field
  `angle_numerator` may be any integer, while `angle_denominator` must be a
  positive integer.
- `start`: the initial string upon which to apply the replacement rules

When plotting the resulting string, the program will do the following for each
character:

- `+`: rotate clockwise by the specified angle
- `-`: rotate counterclockwise by the specified angle
- '[': push the current location and angle onto the stack
- ']' pop a location and angle pair off the stack, and set that as the current
  location
- **all other characters** appearing as a key in `replacement`:
  draw a line segment 1.0 units long from the current position in the direction
  of the current angle

#### Example

This will implement a Koch snowflake:

```json
{ ...
"rules": {
    "type": "Simple",
    "replacement": {"A": "A+A--A+A"},
    "angle_numerator": 1,
    "angle_denominator": 3,
    "start": "+A--A--A"
},
... }
```

### `Advanced` rules format

The `Advanced` format provides substantially more flexibility than `Simple`,
while still providing an easy syntax to use. It allows drawing line segments of
multiple lengths, moving the turtle without drawing for multiple lengths,
specifying a single angle, and a single stack for location/angle pairs. The keys
appearing are:

- `type`: in this case, the string `"Advanced"`
- `replacement`: an object mapping one-character strings to replacement strings,
  e.g.

  ```json
  "replacement": {
      "A": "A[-DA+C+A+C]+C-C-C+A",
      "B": "BACB",
      "C": "CCC",
      "D": "DDD",
  },
  ```

- `draw_step_sizes`: an object mapping one-character strings to floating-point
  numbers, instructing the program to draw forward that many units, e.g.

  ```json
   "draw_step_sizes": { "A": 1.0, "B": 2.0, "C": 1.0 },
  ```

- `move_step_sizes`: an object mapping one-character strings to floating-point
  numbers, instructing the program to move forward that many units without
  drawing, e.g.

  ```json
   "move_step_sizes": { "D": 1.0, "E": 2.0 },
  ```

- `angle_numerator`, `angle_denominator`: these are used to specify an angle,
  which will be &pi; &times; `angle_numerator` / `angle_denominator`. The field
  `angle_numerator` may be any integer, while `angle_denominator` must be a
  positive integer.
- `start`: the initial string upon which to apply the replacement rules

#### Example

This example produces an ["islands" fractal](examples/islands.svg) found in
Benoit B. Mandelbrot (1983), _The Fractal Geometry of Nautre (Updated and
Agumented)_, New York: W.H. Freeman
and Company, p. 121

```json
{ ...
"rules": {
    "type": "Advanced",
    "replacement": {
      "A": "A+BA-AA-A-AA+B+AA-BA+AA+A+AA-B-AAA",
      "B": "BBBBBB"
    },
    "draw_step_sizes": { "A": 1.0 },
    "move_step_sizes": { "B": 1.0 },
    "angle_numerator": 1,
    "angle_denominator": 2,
    "start": "A-A-A-A-"
  },
... }
```

### `Full` rules format

The `Full` format provides the maximum-possible flexibility, but at the cost of
being the most verbose. The keys appearing are:

- `type`: in this case, the string `"Advanced"`
- `replacement`: an object mapping one-character strings to replacement strings,
  e.g.

  ```json
  "replacement": {
      "A": "A[A{B<AX",
      "B": "BBBB",
      "X": "}+A{>+A<]+A["
    },
  ```

- `assignment`: an object mapping one-character strings to draw actions,
  described below, e.g.

  ```json
  "assignment": {
    "A": { "type": "DrawForward", "dist": 1.0 },
    "B": { "type": "MoveForward", "dist": 1.0 },
    "X": { "type": "Null" },
    "[": { "type": "Push", "stack": 0 },
    "{": { "type": "Push", "stack": 1 },
    "<": { "type": "Push", "stack": 2 },
    "]": { "type": "Pop", "stack": 0 },
    "}": { "type": "Pop", "stack": 1 },
    ">": { "type": "Pop", "stack": 2 },
    "+": {
      "type": "RotateCW",
      "numerator": 2,
      "denominator": 9
    }
  },
  ```

- `start`: the initial string upon which to apply the replacement rules

#### Draw actions

A draw action specified as a JSON object, with the key 'type' used to specify
the type of draw action, and other keys depending on the action. The possible
actions are:

- `DrawForward`: Draw forward by the distance specified. The keys are:
  - `type`: in this case, the string `"DrawForward"`.
  - `dist`: a float
- `MoveForward`: Move forward by the distance specified, without drawing. The
  keys are:
  - `type`: in this case, the string `"MoveForward"`.
  - `dist`: a float
- `Push`: Push the current position and angle onto the specified stack. The keys
  are:
  - `type`: in this case, the string `"Push"`,
  - `stack`: a nonnegative integer, specifying the stack. (Using the `Full`
    syntax is the only way to have multiple stacks.)
- `Pop`: Pop position/angle pair off the specified stack, and adopt it as the
  current position and angle. The keys are:
  - `type`: in this case, the string `"Pop"`,
  - `stack`: a nonnegative integer, specifying the stack. (Using the `Full`
    syntax is the only way to have multiple stacks.)
- `RotateCW`: Rotate clockwise by the specified multiple of &pi; The keys are:

  - `type`: in this case, the string `"RotateCW"`
  - `numerator`: Any integer
  - `denominator`: Any positive integer

  The rotation will by clockwise by &pi; &times; `numerator` / `denominator`.

  There is no action for counter-clockwise rotation; simply use `RotateCW` with
  the negative of the desired value.

- `Null`: Do nothing. The object is `{"type": "Null"}`.

_Implementation note:_ The `Full` rules syntax reflects the internal
representation of any set of rules.

#### Example

```json

{...
"rules": {
    "type": "Full",
    "replacement": {
      "A": "A[A{B<AX",
      "B": "BBBB",
      "X": "}+A{>+A<]+A["
    },
    "assignment": {
      "A": { "type": "DrawForward", "dist": 1.0 },
      "B": { "type": "MoveForward", "dist": 1.0 },
      "X": { "type": "Null" },
      "[": { "type": "Push", "stack": 0 },
      "{": { "type": "Push", "stack": 1 },
      "<": { "type": "Push", "stack": 2 },
      "]": { "type": "Pop", "stack": 0 },
      "}": { "type": "Pop", "stack": 1 },
      ">": { "type": "Pop", "stack": 2 },
      "+": {
        "type": "RotateCW",
        "numerator": 2,
        "denominator": 9
      }
    },
    "start": "+A+A+A+A+A+A+A+A+A"
},
...}
```

## `plot_param` format

The `plot_param` field is used to specify parameters for the resulting plotted
output for a `PlotOne` or `PlotMany` command. This is an object with the
following keys:

- `width`: a float specifying the maximum width of the SVG output
- `height`: a float specifying the maximum height of the SVG output
- `border`: a float specifying the width of a border around the resulting
  fractal in the SVG
- `fill`: a string that will be written in the SVG as the CSS `fill` attribute
  in an SVG `path` element
- `stroke`: a string that will be written in the SVG as the CSS `stroke` attribute
  in an SVG `path` element
- `stroke_width`: a float that is written in the SVG as the CSS `stroke-width`
  attribute in an SVG `path` element
- `background` (_optional_): a string; if provided, there will be a rectangle
  with this string as its SVG `fill` attribute behind the image

### Example

```json
{...
  "plot_param": {
    "width": 600.0,
    "height": 400.0,
    "border": 5.0,
    "fill": "none",
    "stroke": "black",
    "stroke_width": 0.5,
    "background": "white"
  },
...}
```

## Examples

### Quadric Koch island

- Reference: Benoit B. Mandelbrot (1983),
  [_The Fractal Geometry of Nature (Updated and
  Agumented)_](https://en.wikipedia.org/wiki/The_Fractal_Geometry_of_Nature),
  New York: W.H. Freeman and Company, p. 50

```json
{
  "command": "PlotOne",
  "rules": {
    "type": "Simple",
    "replacement": { "A": "A+A-A-AA+A+A-A" },
    "angle_numerator": 1,
    "angle_denominator": 2,
    "start": "A+A+A+A"
  },
  "iteration": 4,
  "plot_param": {
    "width": 1040.0,
    "height": 530.0,
    "border": 10.0,
    "fill": "#6f6",
    "stroke": "#0f0",
    "stroke_width": 1.0,
    "background": "#006"
  },
  "filename": "quadric_koch_island.svg",
  "comments": "Benoit B. Mandelbrot (1983), _The Fractal Geometry of Nautre (Updated and Agumented)_, New York: W.H. Freeman and Company, p. 50"
}
```

<img src="examples/quadric_koch_island.svg" alt="A green radially-symmetric shape that looks sort of like a Koch snowflake but more complicated." width="550" height="550" />

### Hilbert space-filling curve

- Reference (for formulation as an L-system):
  [Wikipedia]https://en.wikipedia.org/w/index.php?title=Hilbert_curve&oldid=1194248627#Representation_as_Lindenmayer_system

```json
{
  "command": "PlotMany",
  "rules": {
    "type": "Advanced",
    "replacement": { "A": "+BF-AFA-FB+", "B": "-AF+BFB+FA-" },
    "draw_step_sizes": { "F": 1.0 },
    "move_step_sizes": {},
    "angle_numerator": 1,
    "angle_denominator": 2,
    "start": "A"
  },
  "iterations": [1, 2, 3, 4, 5, 6],
  "row_len": 3,
  "plot_param": {
    "width": 600.0,
    "height": 400.0,
    "border": 5.0,
    "fill": "none",
    "stroke": "black",
    "stroke_width": 0.5,
    "background": "white"
  },
  "filename": "hilbert.svg",
  "comment": "L-system formuation from Wikipedia: https://en.wikipedia.org/w/index.php?title=Hilbert_curve&oldid=1194248627#Representation_as_Lindenmayer_system"
}
```

<img src="examples/hilbert.svg" alt="The first 6 iterations of a space-filling Hilbert curve" width="600" height="400" />

### Nonagon snowflake

> Everybody turns just in time to see the pentagon arrive<br />
> Counting up the sides, it is clear the pentagon has five<br />
> Chatting in the kitchen we see<br />
> There is a triangle whose sides number three<br />
> And is talking to the shape that has nine<br />
> Who is known as Nonagon

&mdash;["Nonagon"](https://youtu.be/z5m8BWk5LoQ), They Might be Giants

```json
{
  "command": "PlotMany",
  "rules": {
    "type": "Full",
    "replacement": {
      "A": "A[A{B<AX",
      "B": "BBBB",
      "X": "}+A{>+A<]+A["
    },
    "assignment": {
      "A": { "type": "DrawForward", "dist": 1.0 },
      "B": { "type": "MoveForward", "dist": 1.0 },
      "X": { "type": "Null" },
      "[": { "type": "Push", "stack": 0 },
      "{": { "type": "Push", "stack": 1 },
      "<": { "type": "Push", "stack": 2 },
      "]": { "type": "Pop", "stack": 0 },
      "}": { "type": "Pop", "stack": 1 },
      ">": { "type": "Pop", "stack": 2 },
      "+": { "type": "RotateCW", "numerator": 2, "denominator": 9 }
    },
    "start": "+A+A+A+A+A+A+A+A+A"
  },
  "iterations": [0, 1, 2, 3, 4, 5],
  "row_len": 3,
  "plot_param": {
    "width": 700.0,
    "height": 500.0,
    "border": 20.0,
    "fill": "none",
    "stroke": "black",
    "stroke_width": 0.5,
    "background": "white"
  },
  "filename": "cphan2.svg"
}
```

<img src="examples/cphan2.svg" alt="Six iterations of some weird spirally snowfake thingy, which starts out as a regular nonagon, and gets more and more complicated" width="700" height="500" />