#![allow(missing_docs)]
#![allow(dead_code)]
use serde::{Deserialize, Serialize};
pub type IslandsByPriority = (Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>, Vec<u32>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Priority {
Sleeping = 0,
Low = 1,
Normal = 2,
High = 3,
Critical = 4,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IslandEntry {
pub id: u32,
pub priority: Priority,
pub body_count: usize,
pub velocity_magnitude: f64,
pub steps_since_active: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepAllocation {
pub island_id: u32,
pub substeps: u32,
pub dt_per_substep: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameSchedule {
pub frame_dt: f64,
pub allocations: Vec<StepAllocation>,
pub skipped_islands: Vec<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scheduler {
islands: Vec<IslandEntry>,
next_id: u32,
pub base_substeps: u32,
pub max_substeps: u32,
pub sleep_threshold: f64,
pub sleep_delay: u32,
}
impl Scheduler {
pub fn new(base_substeps: u32, max_substeps: u32) -> Self {
let base = base_substeps.max(1);
let max = max_substeps.max(base);
Self {
islands: Vec::new(),
next_id: 0,
base_substeps: base,
max_substeps: max,
sleep_threshold: 0.01,
sleep_delay: 60,
}
}
pub fn add_island(&mut self, priority: Priority, body_count: usize) -> u32 {
let id = self.next_id;
self.next_id += 1;
self.islands.push(IslandEntry {
id,
priority,
body_count,
velocity_magnitude: 0.0,
steps_since_active: 0,
});
id
}
pub fn remove_island(&mut self, id: u32) {
self.islands.retain(|e| e.id != id);
}
pub fn island(&self, id: u32) -> Option<&IslandEntry> {
self.islands.iter().find(|e| e.id == id)
}
pub fn island_mut(&mut self, id: u32) -> Option<&mut IslandEntry> {
self.islands.iter_mut().find(|e| e.id == id)
}
pub fn set_priority(&mut self, id: u32, priority: Priority) {
if let Some(e) = self.island_mut(id) {
e.priority = priority;
}
}
pub fn update_velocity(&mut self, id: u32, velocity: f64) {
if let Some(e) = self.island_mut(id) {
e.velocity_magnitude = velocity;
}
}
pub fn update_body_count(&mut self, id: u32, count: usize) {
if let Some(e) = self.island_mut(id) {
e.body_count = count;
}
}
pub fn wake_island(&mut self, id: u32) {
if let Some(e) = self.island_mut(id)
&& e.priority == Priority::Sleeping
{
e.priority = Priority::Normal;
e.steps_since_active = 0;
}
}
pub fn schedule(&self, dt: f64) -> FrameSchedule {
let mut allocations = Vec::new();
let mut skipped = Vec::new();
for island in &self.islands {
let substeps = match island.priority {
Priority::Sleeping => {
skipped.push(island.id);
continue;
}
Priority::Low => (self.base_substeps / 2).max(1),
Priority::Normal => self.base_substeps,
Priority::High | Priority::Critical => self.max_substeps,
};
allocations.push(StepAllocation {
island_id: island.id,
substeps,
dt_per_substep: dt / substeps as f64,
});
}
FrameSchedule {
frame_dt: dt,
allocations,
skipped_islands: skipped,
}
}
pub fn auto_sleep_step(&mut self) -> Vec<u32> {
let threshold = self.sleep_threshold;
let delay = self.sleep_delay;
let mut newly_sleeping = Vec::new();
for e in &mut self.islands {
if e.priority == Priority::Sleeping || e.priority == Priority::Critical {
continue;
}
if e.velocity_magnitude < threshold {
e.steps_since_active += 1;
if e.steps_since_active >= delay {
e.priority = Priority::Sleeping;
newly_sleeping.push(e.id);
}
} else {
e.steps_since_active = 0;
}
}
newly_sleeping
}
pub fn island_count(&self) -> usize {
self.islands.len()
}
pub fn active_count(&self) -> usize {
self.islands
.iter()
.filter(|e| e.priority != Priority::Sleeping)
.count()
}
pub fn islands(&self) -> impl Iterator<Item = &IslandEntry> {
self.islands.iter()
}
pub fn islands_by_priority(&self) -> IslandsByPriority {
let mut critical = Vec::new();
let mut high = Vec::new();
let mut normal = Vec::new();
let mut low = Vec::new();
let mut sleeping = Vec::new();
for e in &self.islands {
match e.priority {
Priority::Critical => critical.push(e.id),
Priority::High => high.push(e.id),
Priority::Normal => normal.push(e.id),
Priority::Low => low.push(e.id),
Priority::Sleeping => sleeping.push(e.id),
}
}
(critical, high, normal, low, sleeping)
}
}