mittens_engine/engine/ecs/system/
clock_system.rs1use std::sync::Arc;
2use std::time::Instant;
3
4use crate::engine::ecs::ComponentId;
5use crate::engine::ecs::World;
6use crate::engine::ecs::component::ClockComponent;
7use crate::engine::ecs::system::System;
8use crate::engine::graphics::VisualWorld;
9use crate::engine::user_input::InputState;
10
11pub trait ClockDriver: Send + Sync {
12 fn name(&self) -> &'static str;
13 fn time_now_sec(&self) -> f64;
14}
15
16#[derive(Debug)]
17pub struct SystemClockDriver {
18 start: Instant,
19}
20
21impl SystemClockDriver {
22 pub fn new() -> Self {
23 Self {
24 start: Instant::now(),
25 }
26 }
27}
28
29impl ClockDriver for SystemClockDriver {
30 fn name(&self) -> &'static str {
31 "system"
32 }
33
34 fn time_now_sec(&self) -> f64 {
35 self.start.elapsed().as_secs_f64()
36 }
37}
38
39pub struct ClockSystem {
43 driver: Arc<dyn ClockDriver>,
44 bpm: f64,
45
46 tempo_component: Option<ComponentId>,
47
48 time_base_sec: f64,
50 beat_base: f64,
51 last_time_sec: f64,
52 last_beat: f64,
53}
54
55impl std::fmt::Debug for ClockSystem {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 f.debug_struct("ClockSystem")
58 .field("driver", &self.driver.name())
59 .field("bpm", &self.bpm)
60 .field("last_beat", &self.last_beat)
61 .finish()
62 }
63}
64
65impl Default for ClockSystem {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl ClockSystem {
72 pub fn new() -> Self {
73 let driver: Arc<dyn ClockDriver> = Arc::new(SystemClockDriver::new());
74 let t0 = driver.time_now_sec();
75 Self {
76 driver,
77 bpm: 120.0,
78 tempo_component: None,
79 time_base_sec: t0,
80 beat_base: 0.0,
81 last_time_sec: t0,
82 last_beat: 0.0,
83 }
84 }
85
86 pub fn driver_name(&self) -> &'static str {
87 self.driver.name()
88 }
89
90 pub fn bpm(&self) -> f64 {
91 self.bpm
92 }
93
94 pub fn beat_now(&self) -> f64 {
95 self.last_beat
96 }
97
98 pub fn register_clock_component(&mut self, component: ComponentId) {
99 self.tempo_component = Some(component);
100 }
101
102 pub fn set_bpm(&mut self, bpm: f64) {
103 if !bpm.is_finite() || bpm <= 0.0 {
104 return;
105 }
106
107 if (self.bpm - bpm).abs() < 1e-6 {
108 return;
109 }
110
111 let now = self.driver.time_now_sec();
113 self.beat_base = self.beat_at_time(now);
114 self.time_base_sec = now;
115 self.bpm = bpm;
116 }
117
118 pub fn set_driver(&mut self, driver: Arc<dyn ClockDriver>) {
119 if self.driver_name() != driver.name() {
120 println!(
121 "[ClockSystem] driver: {} -> {}",
122 self.driver_name(),
123 driver.name()
124 );
125 }
126
127 let last_beat = self.last_beat;
129 self.driver = driver;
130
131 let now = self.driver.time_now_sec();
132 self.time_base_sec = now;
133 self.beat_base = last_beat;
134 self.last_time_sec = now;
135 self.last_beat = last_beat;
136 }
137
138 fn beat_at_time(&self, time_now_sec: f64) -> f64 {
139 let dt = (time_now_sec - self.time_base_sec).max(0.0);
140 self.beat_base + dt * (self.bpm / 60.0)
141 }
142
143 pub fn sample(&mut self) {
144 let now = self.driver.time_now_sec();
145 self.last_time_sec = now;
146 self.last_beat = self.beat_at_time(now);
147 }
148}
149
150impl System for ClockSystem {
151 fn tick(
152 &mut self,
153 world: &mut World,
154 _visuals: &mut VisualWorld,
155 _input: &InputState,
156 _dt_sec: f32,
157 ) {
158 if let Some(cid) = self.tempo_component {
159 if let Some(clock) = world.get_component_by_id_as::<ClockComponent>(cid) {
160 self.set_bpm(clock.bpm);
161 }
162 }
163
164 self.sample();
166 }
167}