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 pub fn token<S: Into<String>>(&mut self, name: S) -> ArenaId {
38 self.arena.push(Token::new(name))
39 }
40
41 pub fn transformation_rule(&mut self, predecessor: ArenaId, successor: Vec<ArenaId>) {
45 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 self.rules
52 .push(TransformationRule::new(predecessor, successor));
53 }
54
55 pub fn axiom(&mut self, axiom: Vec<ArenaId>) {
57 self.axiom = Some(axiom);
58 }
59
60 pub fn finish(self) -> LSystem {
64 let axiom = self.axiom.expect("finish called before axiom set");
65
66 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 for (id, _token) in self.arena.enumerate() {
75 rules_map.entry(id).or_insert_with(|| vec![id]);
77 }
78
79 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}