Skip to main content

loam_runtime/
phase.rs

1use std::fmt;
2
3use crate::bridge::{Drag, DragError, DragRelease};
4use crate::command::{CommandResult, Commands};
5use crate::domain::{ChartPoint, DomainError, Domains};
6use crate::input::Input;
7use crate::session::{Manipulation, PreparedGeometry};
8use crate::stores::Stores;
9use crate::view::{Pick, Views};
10
11/// Fixed order; dispatch runs while paused and is the only phase where capacity grows.
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub enum Phase {
14    Dispatch,
15    Simulation,
16    Publication,
17}
18
19impl Phase {
20    pub const ALL: [Phase; 3] = [Phase::Dispatch, Phase::Simulation, Phase::Publication];
21
22    fn index(self) -> usize {
23        self as usize
24    }
25}
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum Order {
29    Before(&'static str),
30    After(&'static str),
31}
32
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct Tick(pub u64);
35
36#[derive(Clone, Copy, Debug, PartialEq)]
37pub struct Step {
38    pub tick: Tick,
39    pub dt: f32,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct PhaseError {
44    pub phase: Phase,
45    pub system: Option<&'static str>,
46    pub cause: DomainError,
47}
48
49impl PhaseError {
50    pub(crate) fn system(phase: Phase, system: &'static str, cause: DomainError) -> Self {
51        Self {
52            phase,
53            system: Some(system),
54            cause,
55        }
56    }
57
58    pub(crate) fn unnamed(phase: Phase, cause: DomainError) -> Self {
59        Self {
60            phase,
61            system: None,
62            cause,
63        }
64    }
65}
66
67impl fmt::Display for PhaseError {
68    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self.system {
70            Some(system) => write!(
71                formatter,
72                "system `{system}` failed in {:?}: {}",
73                self.phase, self.cause
74            ),
75            None => write!(formatter, "{:?} phase failed: {}", self.phase, self.cause),
76        }
77    }
78}
79
80impl std::error::Error for PhaseError {}
81
82pub struct Ctx<'a, A> {
83    pub app: &'a mut A,
84    pub domains: &'a mut Domains,
85    pub views: &'a mut Views,
86    pub commands: &'a mut Commands<A>,
87    pub results: &'a [CommandResult],
88    pub input: &'a Input,
89    pub prepared: &'a [PreparedGeometry],
90    pub step: Step,
91    pub(crate) manipulation: &'a mut Manipulation,
92}
93
94impl<A: Stores> Ctx<'_, A> {
95    pub fn pick(&self, ndc: [f32; 2]) -> Option<Pick> {
96        self.domains.pick(self.views, self.prepared, ndc)
97    }
98
99    pub fn dragging(&self) -> Option<Drag> {
100        self.manipulation.dragging()
101    }
102
103    pub fn grab(&mut self, ndc: [f32; 2], time: f64) -> Result<Pick, DragError> {
104        self.manipulation.grab(
105            self.domains,
106            self.views,
107            self.prepared,
108            self.commands,
109            ndc,
110            time,
111        )
112    }
113
114    pub fn drag(&mut self, ndc: [f32; 2], time: f64) -> Result<ChartPoint, DragError> {
115        self.manipulation
116            .drag(self.domains, self.views, self.commands, ndc, time)
117    }
118
119    pub fn release(&mut self) -> Option<DragRelease> {
120        self.manipulation.release(self.commands)
121    }
122
123    pub fn release_at(&mut self, time: f64) -> Option<DragRelease> {
124        self.manipulation.release_at(self.commands, time)
125    }
126
127    pub fn cancel_drag(&mut self) -> Option<DragRelease> {
128        self.manipulation.cancel(self.commands)
129    }
130}
131
132pub trait System<A>: Send + 'static {
133    fn run(&mut self, ctx: Ctx<'_, A>) -> Result<(), DomainError>;
134}
135
136impl<A: 'static, F> System<A> for F
137where
138    F: FnMut(Ctx<'_, A>) -> Result<(), DomainError> + Send + 'static,
139{
140    fn run(&mut self, ctx: Ctx<'_, A>) -> Result<(), DomainError> {
141        self(ctx)
142    }
143}
144
145type Runner<A> = Box<dyn FnMut(Ctx<'_, A>) -> Result<(), DomainError> + Send>;
146
147pub struct SystemEntry<A> {
148    name: &'static str,
149    run: Runner<A>,
150}
151
152impl<A: 'static> SystemEntry<A> {
153    pub(crate) fn new(name: &'static str, system: impl System<A>) -> Self {
154        let mut system = system;
155        Self {
156            name,
157            run: Box::new(move |ctx| system.run(ctx)),
158        }
159    }
160}
161
162impl<A> SystemEntry<A> {
163    pub fn name(&self) -> &'static str {
164        self.name
165    }
166
167    pub(crate) fn run(&mut self, ctx: Ctx<'_, A>) -> Result<(), DomainError> {
168        (self.run)(ctx)
169    }
170}
171
172pub(crate) struct Phases<A> {
173    entries: [Vec<SystemEntry<A>>; 3],
174}
175
176impl<A> Phases<A> {
177    pub(crate) fn new() -> Self {
178        Self {
179            entries: Default::default(),
180        }
181    }
182
183    pub(crate) fn push(&mut self, phase: Phase, entry: SystemEntry<A>) {
184        self.entries[phase.index()].push(entry);
185    }
186
187    pub(crate) fn insert(
188        &mut self,
189        phase: Phase,
190        order: Order,
191        entry: SystemEntry<A>,
192    ) -> Option<()> {
193        let list = &mut self.entries[phase.index()];
194        let index = match order {
195            Order::Before(name) => list.iter().position(|entry| entry.name() == name)?,
196            Order::After(name) => list.iter().position(|entry| entry.name() == name)? + 1,
197        };
198        list.insert(index, entry);
199        Some(())
200    }
201
202    pub(crate) fn entries(&self, phase: Phase) -> &[SystemEntry<A>] {
203        &self.entries[phase.index()]
204    }
205
206    pub(crate) fn entries_mut(&mut self, phase: Phase) -> &mut [SystemEntry<A>] {
207        &mut self.entries[phase.index()]
208    }
209}