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
/*!
# dcc-lsystem

A crate for working with [Lindenmayer systems](https://en.wikipedia.org/wiki/L-system).

## Background

An L-System consists of an alphabet of symbols that can be used to make strings,
a collection of production rules that expand each symbol into a larger string of symbols,
an initial axiom string from which to begin construction, and a mechanism for transforming
the generated strings into geometric structures.

## Algae example
Lindenmayer's original L-System for modelling the growth of Algae had
variables `A` and `B`, axiom `A`, and production rules `A -> AB`, `B -> A`.  Iterating
this system produces the following output:

0. `A`
1. `AB`
2. `ABA`
3. `ABAAB`

## Basic usage

Put the following in your `Cargo.toml`:

```toml
dcc-lsystem = "0.6"
```

### [`LSystemBuilder`]

An L-system is represented by an instance of [`LSystem`].  To create a barebones [`LSystem`],
the [`LSystemBuilder`] struct is useful.  The following example shows an implementation of
Lindenmayer's Algae system.

```rust
use dcc_lsystem::LSystemBuilder;

let mut builder = LSystemBuilder::new();

// Set up the two tokens we use for our system.
let a = builder.token("A");
let b = builder.token("B");

// Set up our axiom (i.e. initial state)
builder.axiom(vec![a]);

// Set the transformation rules
builder.transformation_rule(a, vec![a,b]); // A -> AB
builder.transformation_rule(b, vec![a]);   // B -> A

// Build our LSystem, which should have initial state A
let mut system = builder.finish();
assert_eq!(system.render(), "A");

// system.step() applies our production rules a single time
system.step();
assert_eq!(system.render(), "AB");

system.step();
assert_eq!(system.render(), "ABA");

// system.step_by() applies our production rule a number of times
system.step_by(5);
assert_eq!(system.render(), "ABAABABAABAABABAABABAABAABABAABAAB");
```
## Rendering L-systems

It is possible to render an L-system into an image or gif.  Typically this is done using
a turtle - each token in the L-system's state is associated with some movement or rotation
(or perhaps something more complicated) of a turtle.  The [`TurtleLSystemBuilder`] struct offers
a convenient way of constructing such renderings.

### Images

The Koch curve can be generated using an L-system with 3 symbols: `F`, `+`, and `-`,
where `F` corresponds to moving forwards, `+` denotes a left rotation by 90°,
and `-` denotes a right rotation by 90°. The system has axiom `F` and transformation
rule `F => F+F-F-F+F`. This is implemented in the following example.

```rust,no_run
use image::Rgb;

use dcc_lsystem::turtle::{TurtleLSystemBuilder, TurtleAction};
use dcc_lsystem::renderer::{ImageRendererOptionsBuilder, Renderer};

let mut builder = TurtleLSystemBuilder::new();

builder
    .token("F", TurtleAction::Forward(30)) // F => go forward 30 units
    .token("+", TurtleAction::Rotate(90))  // + => rotate left 90°
    .token("-", TurtleAction::Rotate(-90)) // - => rotate right 90°
    .axiom("F")
    .rule("F => F + F - F - F + F");

let (mut system, renderer) = builder.finish();
system.step_by(5); // Iterate our L-system 5 times

let options = ImageRendererOptionsBuilder::new()
    .padding(10)
    .thickness(4.0)
    .fill_color(Rgb([255u8, 255u8, 255u8]))
    .line_color(Rgb([0u8, 0u8, 100u8]))
    .build();

renderer
    .render(&system, &options)
    .save("koch_curve.png")
    .expect("Failed to save koch_curve.png");
```

The resulting image is shown in the Examples section below.

### GIFs

It is also possible to render a GIF using an L-system.  The individual frames
of the GIF correspond to partial renderings of the L-system's state.

```rust,no_run
use image::Rgb;

use dcc_lsystem::renderer::{Renderer, VideoRendererOptionsBuilder};
use dcc_lsystem::turtle::{TurtleAction, TurtleLSystemBuilder};

let mut builder = TurtleLSystemBuilder::new();

builder
    .token("F", TurtleAction::Forward(30))
    .token("+", TurtleAction::Rotate(90))
    .token("-", TurtleAction::Rotate(-90))
    .axiom("F")
    .rule("F => F + F - F - F + F");

let (mut system, renderer) = builder.finish();
system.step_by(5);

let options = VideoRendererOptionsBuilder::new()
    .filename("koch_curve.gif")
    .fps(20)
    .skip_by(0)
    .padding(10)
    .thickness(4.0)
    .fill_color(Rgb([255u8, 255u8, 255u8]))
    .line_color(Rgb([0u8, 0u8, 100u8]))
    .progress_bar(true)
    .build();

renderer
    .render(&system, &options);
```

### Turtle actions

Currently the following actions are available:

| [`TurtleAction`]                             | Description                                                                             |
|--------------------------------------------|-----------------------------------------------------------------------------------------|
| `Nothing`                                  | The turtle does nothing.                                                                |
| `Rotate(i32)`                              | Rotate the turtle through an angle.                                                     |
| `Forward(i32)`                             | Move the turtle forwards.                                                               |
| `Push`                                     | Push the turtle's current heading and location onto the stack.                          |
| `Pop`                                      | Pop the turtle's heading and location off the stack.                                    |
| `StochasticRotate(Box<dyn Distribution>)`  | Rotate the turtle through an angle specified by some probability distribution.          |
| `StochasticForward(Box<dyn Distribution>)` | Move the turtle forwards through a distance specified by some probability distribution. |

The [`Distribution`] trait is given by:

```rust
pub trait Distribution: dyn_clone:: DynClone {
    fn sample(&self) -> i32;
}
```

The [`Uniform`] distribution (using the `rand` crate) is implemented as follows:

```rust
# pub trait Distribution: dyn_clone::DynClone {
#     fn sample(&self) -> i32;
# }
use rand::Rng;

#[derive(Clone)]
pub struct Uniform {
    lower: i32,
    upper: i32,
}

impl Uniform {
    pub fn new(lower: i32, upper: i32) -> Self {
        Self { lower, upper }
    }
}

impl Distribution for Uniform {
    fn sample(&self) -> i32 {
        let mut rng = rand::thread_rng();
        rng.gen_range(self.lower..=self.upper)
    }
}
```

## Examples

Examples are located in `dcc-lsystem/examples`.

#### Sierpinski Arrowhead

![Sierpinski Arrowhead](https://user-images.githubusercontent.com/266585/62997521-73583380-be1d-11e9-8451-5ebf32216550.png)

#### Koch curve

![Koch curve](https://user-images.githubusercontent.com/266585/62997274-90403700-be1c-11e9-9f80-80968e265a8f.png)

#### Dragon curve

![Dragon curve](https://user-images.githubusercontent.com/266585/62997357-d5646900-be1c-11e9-8c24-c7da5958ef48.png)

#### Fractal plant

![Fractal plant](https://user-images.githubusercontent.com/266585/62997436-21afa900-be1d-11e9-8222-dfdc2ef18b72.png)

### License

Licensed under either of

 * Apache License, Version 2.0
   ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license
   ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

[`Lsystem`]: system/struct.LSystem.html
[`LSystemBuilder`]: builder/struct.LSystemBuilder.html
[`TurtleLSystemBuilder`]: turtle/struct.TurtleLSystemBuilder.html
[`Distribution`]: turtle/struct.Distribution.html
[`Uniform`]: turtle/struct.Uniform.html
[`TurtleAction`]: turtle/enum.TurtleAction.html
*/

extern crate self as dcc_lsystem;

pub use arena::{Arena, ArenaId};
pub use builder::LSystemBuilder;
pub use system::LSystem;

pub mod arena;
pub mod builder;
#[cfg(feature = "image_renderer")]
pub mod image;
#[cfg(feature = "image_renderer")]
pub mod image_renderer;
pub mod renderer;
pub mod system;
pub mod token;
pub mod turtle;

#[cfg(test)]
mod tests;