aoer_plotty_rs/l_system/mod.rs
1//! The l_system module provides a simple Lindenmeyer fractal generator for use with
2//! plotted line-art. Take a look at the [`crate::l_system::LSystem`] struct for
3//! more details, and examples.
4
5use std::collections::HashMap;
6use embed_doc_image::embed_doc_image;
7
8/// # LSystem
9///
10/// What it says on the box; a simple L-system implementation for use with plotter
11/// based fractal art.
12///
13/// # Example
14///
15/// ```rust
16/// use aoer_plotty_rs::turtle::{TurtleTrait, Turtle, degrees};
17/// use aoer_plotty_rs::l_system::LSystem;
18/// use aoer_plotty_rs::geo_types::nannou::NannouDrawer;
19/// use std::collections::HashMap;
20/// use nannou::lyon::tessellation::{LineCap, LineJoin};
21/// use nannou::Draw;
22///
23/// let draw = Draw::new();
24/// let gosper = LSystem{
25/// axiom: "A".to_string(),
26/// rules: HashMap::from([
27/// ('A', "A-B--B+A++AA+B-".to_string()),
28/// ('B', "+A-BB--B-A++A+B". to_string())])
29/// };
30///
31/// let tlines = Turtle::new()
32/// .pen_down()
33/// .walk_lpath(&gosper.expand(4), degrees(60.0), 8.0)
34/// .to_multiline();
35/// for line in tlines {
36/// draw.polyline()
37/// .stroke_weight(3.0)
38/// .caps(LineCap::Round)
39/// .join(LineJoin::Round)
40/// .polyline_from_linestring(&line)
41/// .color(nannou::color::NAVY);
42/// }
43/// ```
44/// ![gosper-4][gosper-4]
45#[embed_doc_image("gosper-4", "images/gosper-4.png")]
46
47#[derive(Clone, Debug)]
48pub struct LSystem{
49 pub axiom: String,
50 pub rules: HashMap<char, String>,
51}
52
53impl LSystem{
54
55 fn recur(&self, state: String, order: u32)->String{
56 let new_state = state.chars().map(|c|{
57 match self.rules.get(&c){
58 Some(replacement) => replacement.clone(),
59 None => String::from(c)
60 }
61 }).collect();
62 if order == 0{
63 state
64 }else{
65 self.recur(new_state, order-1)
66 }
67 }
68
69 /// #expand
70 ///
71 /// Expands the L-system by the requested "order" of iterations. Returns a string
72 /// representing the state of the L-system. Useful with
73 /// [`crate::turtle::TurtleTrait::walk_lpath`]
74 pub fn expand(&self, order: u32) -> String{
75 self.recur(self.axiom.clone(), order)
76 }
77
78}
79
80
81#[cfg(test)]
82mod test{
83 use super::*;
84
85 #[test]
86 fn test_expand_simple(){
87 let system = LSystem {
88 axiom: "A".to_string(),
89 rules: HashMap::from([
90 ('A', "AB".to_string()),
91 ('B', "A". to_string())]),
92 };
93 assert!(system.expand(2) == "ABA".to_string());
94 assert!(system.expand(5) == "ABAABABAABAAB".to_string());
95 }
96}
97