dcc_lsystem/
renderer.rs

1use std::collections::HashMap;
2
3use crate::turtle::TurtleContainer;
4use crate::{ArenaId, LSystem};
5
6#[cfg(feature = "image_renderer")]
7pub use crate::image_renderer::ImageRendererOptionsBuilder;
8
9#[cfg(feature = "image_renderer")]
10pub use crate::image_renderer::VideoRendererOptionsBuilder;
11
12pub trait Renderer<S> {
13    /// The output of the rendering operation
14    type Output;
15
16    /// Renders the system, consuming the renderer.
17    fn render(self, system: &LSystem, options: &S) -> Self::Output;
18}
19
20pub struct TurtleRenderer<Q: TurtleContainer> {
21    pub(crate) state: Q,
22    state_actions: HashMap<ArenaId, Box<dyn Fn(&mut Q)>>,
23    aliases: HashMap<ArenaId, ArenaId>,
24}
25
26impl<Q: TurtleContainer> TurtleRenderer<Q> {
27    pub fn new(state: Q) -> Self {
28        Self {
29            state,
30            state_actions: HashMap::new(),
31            aliases: HashMap::new(),
32        }
33    }
34
35    pub fn register<F: 'static + Fn(&mut Q)>(&mut self, arena_id: ArenaId, modifier: F) {
36        self.aliases.insert(arena_id, arena_id);
37        self.state_actions.insert(arena_id, Box::from(modifier));
38    }
39
40    pub fn register_multiple<F: 'static + Fn(&mut Q)>(
41        &mut self,
42        arena_ids: &[ArenaId],
43        modifier: F,
44    ) {
45        if let Some(id) = arena_ids.first() {
46            // Alias each ID in the slice to the first one
47            for aliased_id in arena_ids.iter() {
48                self.aliases.insert(*aliased_id, *id);
49            }
50
51            // Register the mutator for the first id
52            self.register(*id, modifier);
53        }
54    }
55
56    pub(crate) fn compute(&mut self, system_state: &[ArenaId]) {
57        for arena_id in system_state {
58            if self.aliases.contains_key(arena_id) {
59                // Find the arena id that the provided one points to
60                let alias = self.aliases[arena_id];
61
62                // If there is a function corresponding to the alias,
63                // apply it
64                if self.state_actions.contains_key(&alias) {
65                    self.state_actions[&alias](&mut self.state);
66                }
67            }
68        }
69    }
70}
71
72/// A version of ImageRendererOptions but intended for data only rendering (no image).
73/// For symmetry reasons and future proofing, it is implemented as an empty struct.
74#[derive(Default)]
75pub struct DataRendererOptions {}
76
77impl<Q: TurtleContainer> Renderer<DataRendererOptions> for TurtleRenderer<Q> {
78    type Output = Vec<(i32, i32, i32, i32)>;
79
80    fn render(mut self, system: &LSystem, _options: &DataRendererOptions) -> Self::Output {
81        // Setup our state machine based on the LSystem state
82        self.compute(system.get_state());
83
84        // TODO: find a way to move lines() instead of cloning it with to_vec()
85        self.state.inner().inner().lines().to_vec()
86    }
87}