dcc_lsystem/
builder.rs

1use std::collections::HashMap;
2
3use crate::arena::{Arena, ArenaId};
4use crate::system::LSystem;
5use crate::token::Token;
6
7#[derive(Debug, Clone)]
8struct TransformationRule {
9    predecessor: ArenaId,
10    successor: Vec<ArenaId>,
11}
12
13impl TransformationRule {
14    pub fn new(predecessor: ArenaId, successor: Vec<ArenaId>) -> Self {
15        Self {
16            predecessor,
17            successor,
18        }
19    }
20}
21
22#[derive(Default, Clone)]
23pub struct LSystemBuilder {
24    arena: Arena<Token>,
25    axiom: Option<Vec<ArenaId>>,
26    rules: Vec<TransformationRule>,
27}
28
29impl LSystemBuilder {
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Register a new token.
35    ///
36    /// Returns a TokenId which can be used (in this LSystem) to refer to the registered token.
37    pub fn token<S: Into<String>>(&mut self, name: S) -> ArenaId {
38        self.arena.push(Token::new(name))
39    }
40
41    /// Register a new transformation rule in this LSystem.
42    ///
43    /// If any of the provided TokenId are invalid, this function will panic.
44    pub fn transformation_rule(&mut self, predecessor: ArenaId, successor: Vec<ArenaId>) {
45        // Verify that the TokenId corresponds to a token in this LSystem
46        if !self.arena.is_valid(predecessor) || !self.arena.is_valid_slice(&successor) {
47            panic!("Invalid token id provided to Lsystem::transformation_rule");
48        }
49
50        // Add the rule to this system
51        self.rules
52            .push(TransformationRule::new(predecessor, successor));
53    }
54
55    /// Set the axiom for this LSystem.
56    pub fn axiom(&mut self, axiom: Vec<ArenaId>) {
57        self.axiom = Some(axiom);
58    }
59
60    /// Consumes the builder, returning an LSystem instance.
61    ///
62    /// This function will panic if you have not set an axiom before proceeding.
63    pub fn finish(self) -> LSystem {
64        let axiom = self.axiom.expect("finish called before axiom set");
65
66        // Construct a HashMap associating each variable with its corresponding transformation rule
67        let mut rules_map = HashMap::new();
68
69        for rule in self.rules.into_iter() {
70            rules_map.insert(rule.predecessor, rule.successor);
71        }
72
73        // We also add constant production rules of the form P => P.
74        for (id, _token) in self.arena.enumerate() {
75            // no rule associated to this token, so its a constant token
76            rules_map.entry(id).or_insert_with(|| vec![id]);
77        }
78
79        // If we set our system up correctly, it should be that each token
80        // contributes exactly one rule, so we check for that here.
81        assert_eq!(self.arena.len(), rules_map.len());
82
83        LSystem::new(self.arena, axiom, rules_map)
84    }
85}
86
87fn build_rules_string(rules: &[TransformationRule], arena: &Arena<Token>) -> String {
88    let mut st = Vec::new();
89
90    for rule in rules {
91        st.push(format!(
92            "{} => {}",
93            arena.render(&[rule.predecessor]),
94            arena.render(&rule.successor)
95        ));
96    }
97
98    st.join(",")
99}
100
101impl std::fmt::Debug for LSystemBuilder {
102    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
103        f.debug_struct("LSystemBuilder")
104            .field("arena", &self.arena)
105            .field("axiom", &self.axiom)
106            .field("rules", &build_rules_string(&self.rules, &self.arena))
107            .finish()
108    }
109}