Skip to main content

alice_physics/
sleeping.rs

1//! Sleeping and Island Management
2//!
3//! Puts low-energy bodies to sleep and groups connected bodies into islands.
4//!
5//! # Sleeping
6//!
7//! Bodies whose velocities remain below a threshold for a sustained period
8//! are put to sleep, skipping integration and constraint solving.
9//!
10//! # Islands
11//!
12//! Connected components of bodies (via joints/contacts) are grouped into islands.
13//! If all bodies in an island are sleeping, the entire island sleeps.
14
15use crate::math::Fix128;
16use crate::solver::RigidBody;
17
18#[cfg(not(feature = "std"))]
19use alloc::vec;
20#[cfg(not(feature = "std"))]
21use alloc::vec::Vec;
22
23/// Sleep state for a body
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum SleepState {
26    /// Body is fully active
27    Awake,
28    /// Body is sleeping (skipped in simulation)
29    Sleeping,
30}
31
32/// Per-body sleep tracking data
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34pub struct SleepData {
35    /// Current sleep state
36    pub state: SleepState,
37    /// Number of consecutive frames below velocity threshold
38    pub idle_frames: u32,
39}
40
41impl SleepData {
42    /// Create new awake sleep data
43    #[must_use]
44    pub const fn new() -> Self {
45        Self {
46            state: SleepState::Awake,
47            idle_frames: 0,
48        }
49    }
50
51    /// Check if this body is currently sleeping
52    #[inline]
53    #[must_use]
54    pub fn is_sleeping(&self) -> bool {
55        self.state == SleepState::Sleeping
56    }
57
58    /// Wake up this body
59    #[inline]
60    pub fn wake(&mut self) {
61        self.state = SleepState::Awake;
62        self.idle_frames = 0;
63    }
64}
65
66impl Default for SleepData {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72/// Configuration for the sleeping system
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74pub struct SleepConfig {
75    /// Linear velocity threshold below which a body is considered idle
76    pub linear_threshold: Fix128,
77    /// Angular velocity threshold below which a body is considered idle
78    pub angular_threshold: Fix128,
79    /// Number of consecutive idle frames before putting to sleep
80    pub frames_to_sleep: u32,
81}
82
83impl Default for SleepConfig {
84    fn default() -> Self {
85        Self {
86            linear_threshold: Fix128::from_ratio(1, 100), // 0.01 m/s
87            angular_threshold: Fix128::from_ratio(1, 100), // 0.01 rad/s
88            frames_to_sleep: 60,                          // 1 second at 60fps
89        }
90    }
91}
92
93/// Island: a group of connected bodies
94#[derive(Clone, Debug)]
95pub struct Island {
96    /// Body indices in this island
97    pub bodies: Vec<usize>,
98    /// Whether all bodies in the island are sleeping
99    pub all_sleeping: bool,
100}
101
102/// Island manager using Union-Find (disjoint set)
103pub struct IslandManager {
104    /// Parent array for union-find
105    parent: Vec<usize>,
106    /// Rank for union by rank
107    rank: Vec<u32>,
108    /// Sleep data per body
109    pub sleep_data: Vec<SleepData>,
110    /// Sleep configuration
111    pub config: SleepConfig,
112}
113
114impl IslandManager {
115    /// Create a new island manager for `num_bodies` bodies
116    #[must_use]
117    pub fn new(num_bodies: usize, config: SleepConfig) -> Self {
118        let mut parent = Vec::with_capacity(num_bodies);
119        let rank = vec![0u32; num_bodies];
120        let sleep_data = vec![SleepData::new(); num_bodies];
121        for i in 0..num_bodies {
122            parent.push(i);
123        }
124        Self {
125            parent,
126            rank,
127            sleep_data,
128            config,
129        }
130    }
131
132    /// Resize to accommodate more bodies
133    pub fn resize(&mut self, num_bodies: usize) {
134        while self.parent.len() < num_bodies {
135            let idx = self.parent.len();
136            self.parent.push(idx);
137            self.rank.push(0);
138            self.sleep_data.push(SleepData::new());
139        }
140    }
141
142    /// Reset all unions (call before rebuilding islands)
143    pub fn reset_unions(&mut self) {
144        for i in 0..self.parent.len() {
145            self.parent[i] = i;
146            self.rank[i] = 0;
147        }
148    }
149
150    /// Find root with path compression
151    pub fn find(&mut self, mut x: usize) -> usize {
152        while self.parent[x] != x {
153            self.parent[x] = self.parent[self.parent[x]]; // Path halving
154            x = self.parent[x];
155        }
156        x
157    }
158
159    /// Union two bodies into the same island
160    pub fn union(&mut self, a: usize, b: usize) {
161        let ra = self.find(a);
162        let rb = self.find(b);
163        if ra == rb {
164            return;
165        }
166        // Union by rank
167        match self.rank[ra].cmp(&self.rank[rb]) {
168            core::cmp::Ordering::Less => self.parent[ra] = rb,
169            core::cmp::Ordering::Greater => self.parent[rb] = ra,
170            core::cmp::Ordering::Equal => {
171                self.parent[rb] = ra;
172                self.rank[ra] += 1;
173            }
174        }
175    }
176
177    /// Build islands from current union-find state
178    pub fn build_islands(&mut self) -> Vec<Island> {
179        let n = self.parent.len();
180        let mut island_map: Vec<Option<usize>> = vec![None; n];
181        let mut islands: Vec<Island> = Vec::new();
182
183        for i in 0..n {
184            let root = self.find(i);
185            let island_idx = island_map[root].unwrap_or_else(|| {
186                let idx = islands.len();
187                island_map[root] = Some(idx);
188                islands.push(Island {
189                    bodies: Vec::new(),
190                    all_sleeping: true,
191                });
192                idx
193            });
194            islands[island_idx].bodies.push(i);
195            if !self.sleep_data[i].is_sleeping() {
196                islands[island_idx].all_sleeping = false;
197            }
198        }
199
200        islands
201    }
202
203    /// Update sleep states based on current velocities
204    pub fn update_sleep(&mut self, bodies: &[RigidBody]) {
205        for (i, body) in bodies.iter().enumerate() {
206            if i >= self.sleep_data.len() {
207                break;
208            }
209            self.update_sleep_body(i, body);
210        }
211    }
212
213    /// [`Self::update_sleep`] for one body (`i` must be in range).
214    pub(crate) fn update_sleep_body(&mut self, i: usize, body: &RigidBody) {
215        // Static bodies are always "sleeping"
216        if body.is_static() {
217            self.sleep_data[i].state = SleepState::Sleeping;
218            return;
219        }
220
221        let linear_speed = body.velocity.length();
222        let angular_speed = body.angular_velocity.length();
223
224        let is_idle = linear_speed < self.config.linear_threshold
225            && angular_speed < self.config.angular_threshold;
226
227        if is_idle {
228            self.mark_idle(i);
229        } else {
230            self.sleep_data[i].state = SleepState::Awake;
231            self.sleep_data[i].idle_frames = 0;
232        }
233    }
234
235    /// [`Self::update_sleep_body`] for a body already known to be idle (or
236    /// static when `is_static`), without looking at its velocity.
237    pub(crate) fn update_sleep_idle(&mut self, i: usize, is_static: bool) {
238        if is_static {
239            self.sleep_data[i].state = SleepState::Sleeping;
240        } else {
241            self.mark_idle(i);
242        }
243    }
244
245    #[inline]
246    fn mark_idle(&mut self, i: usize) {
247        self.sleep_data[i].idle_frames += 1;
248        if self.sleep_data[i].idle_frames >= self.config.frames_to_sleep {
249            self.sleep_data[i].state = SleepState::Sleeping;
250        }
251    }
252
253    /// Wake up a body and all bodies in its island
254    pub fn wake_island(&mut self, body_index: usize) {
255        let root = self.find(body_index);
256        for i in 0..self.parent.len() {
257            if self.find(i) == root {
258                self.sleep_data[i].wake();
259            }
260        }
261    }
262
263    /// Wake up a single body
264    #[inline]
265    pub fn wake_body(&mut self, body_index: usize) {
266        if body_index < self.sleep_data.len() {
267            self.sleep_data[body_index].wake();
268        }
269    }
270
271    /// Check if a body is sleeping
272    #[inline]
273    pub fn is_sleeping(&self, body_index: usize) -> bool {
274        self.sleep_data
275            .get(body_index)
276            .is_some_and(SleepData::is_sleeping)
277    }
278
279    /// Get the number of sleeping bodies
280    #[must_use]
281    pub fn sleeping_count(&self) -> usize {
282        self.sleep_data.iter().filter(|d| d.is_sleeping()).count()
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use super::*;
289    use crate::math::Vec3Fix;
290
291    #[test]
292    fn test_union_find() {
293        let config = SleepConfig::default();
294        let mut mgr = IslandManager::new(5, config);
295
296        mgr.union(0, 1);
297        mgr.union(2, 3);
298
299        assert_eq!(mgr.find(0), mgr.find(1));
300        assert_ne!(mgr.find(0), mgr.find(2));
301        assert_eq!(mgr.find(2), mgr.find(3));
302    }
303
304    #[test]
305    fn test_build_islands() {
306        let config = SleepConfig::default();
307        let mut mgr = IslandManager::new(5, config);
308
309        mgr.union(0, 1);
310        mgr.union(2, 3);
311        // Body 4 is alone
312
313        let islands = mgr.build_islands();
314        assert_eq!(islands.len(), 3, "Should have 3 islands");
315
316        let total_bodies: usize = islands.iter().map(|i| i.bodies.len()).sum();
317        assert_eq!(total_bodies, 5);
318    }
319
320    #[test]
321    fn test_sleep_after_threshold() {
322        let config = SleepConfig {
323            frames_to_sleep: 3,
324            ..Default::default()
325        };
326        let mut mgr = IslandManager::new(1, config);
327
328        let still_body = RigidBody::new_static(Vec3Fix::ZERO);
329        let bodies = [still_body];
330
331        // Should become sleeping after 3 frames
332        for _ in 0..3 {
333            mgr.update_sleep(&bodies);
334        }
335
336        assert!(mgr.is_sleeping(0));
337    }
338
339    #[test]
340    fn test_wake_on_motion() {
341        let config = SleepConfig {
342            linear_threshold: Fix128::from_ratio(1, 100),
343            frames_to_sleep: 2,
344            ..Default::default()
345        };
346        let mut mgr = IslandManager::new(1, config);
347
348        // Put to sleep
349        let still_body = RigidBody::new(Vec3Fix::ZERO, Fix128::ONE);
350        mgr.update_sleep(&[still_body]);
351        mgr.update_sleep(&[still_body]);
352        assert!(mgr.is_sleeping(0));
353
354        // Moving body should wake up
355        let mut moving_body = RigidBody::new(Vec3Fix::ZERO, Fix128::ONE);
356        moving_body.velocity = Vec3Fix::from_int(10, 0, 0);
357        mgr.update_sleep(&[moving_body]);
358        assert!(!mgr.is_sleeping(0));
359    }
360
361    #[test]
362    fn test_wake_island() {
363        let config = SleepConfig::default();
364        let mut mgr = IslandManager::new(3, config);
365
366        // Put all to sleep
367        for d in &mut mgr.sleep_data {
368            d.state = SleepState::Sleeping;
369        }
370
371        mgr.union(0, 1);
372
373        // Wake body 0 => should wake body 1 too (same island)
374        mgr.wake_island(0);
375
376        assert!(!mgr.is_sleeping(0));
377        assert!(!mgr.is_sleeping(1));
378        assert!(mgr.is_sleeping(2)); // Different island, still sleeping
379    }
380
381    #[test]
382    fn test_resize() {
383        let config = SleepConfig::default();
384        let mut mgr = IslandManager::new(2, config);
385        mgr.resize(5);
386
387        assert_eq!(mgr.parent.len(), 5);
388        assert_eq!(mgr.sleep_data.len(), 5);
389    }
390
391    #[test]
392    fn test_sleeping_count() {
393        let config = SleepConfig::default();
394        let mut mgr = IslandManager::new(5, config);
395
396        mgr.sleep_data[0].state = SleepState::Sleeping;
397        mgr.sleep_data[2].state = SleepState::Sleeping;
398        mgr.sleep_data[4].state = SleepState::Sleeping;
399
400        assert_eq!(mgr.sleeping_count(), 3);
401    }
402}