use std::collections::BTreeMap;
use super::*;
unsafe impl<'e, K, C> Send for Tiles<'e, K, C> {}
unsafe impl<'e, K, C> Sync for Tiles<'e, K, C> {}
#[derive(Debug)]
pub struct Scheduler {
grid: Grid,
jobs: usize,
}
pub struct Tasks<'a, 'e, K, C> {
pub sync: Vec<Vec<&'a mut EntityTrait<'e, K, C>>>,
pub unsync: Vec<&'a mut EntityTrait<'e, K, C>>,
}
impl Scheduler {
pub fn new(dimension: impl Into<Dimension>, jobs: usize) -> Self {
debug_assert!(jobs > 0);
Self {
grid: Grid::new(dimension, jobs),
jobs,
}
}
pub fn get_tasks<'a, 'e, K, C>(
&self,
entities: impl IntoIterator<Item = &'a mut EntityTrait<'e, K, C>>,
) -> Tasks<'a, 'e, K, C> {
debug_assert!(self.jobs > 0);
if self.jobs == 1 {
return Tasks {
sync: Vec::default(),
unsync: entities.into_iter().collect(),
};
}
let mut sync = Vec::new();
sync.resize_with(
self.grid.dimension.len(),
Vec::<&mut EntityTrait<'e, K, C>>::default,
);
let mut unsync = Vec::new();
for e in entities {
if let Some(location) = e.location() {
let scope = e.scope().unwrap_or_else(Scope::empty);
let tile =
self.grid.get(location, scope).unwrap_or_else(|| {
panic!(
"Cannot assign Tile to Entity at {:?} with {:?}",
location, scope
)
});
match tile {
Tile::Sync { index } => sync[index].push(e),
Tile::Unsync => unsync.push(e),
};
} else {
unsync.push(e);
}
}
Tasks { sync, unsync }
}
}
type Coordinate = i32;
type TileIndex = usize;
type EdgesMap = BTreeMap<Coordinate, TileIndex>;
#[derive(Debug)]
enum Tile {
Sync { index: TileIndex },
Unsync,
}
#[derive(Debug)]
struct Grid {
dimension: Dimension,
vertical: EdgesMap,
horizontal: EdgesMap,
}
impl Grid {
fn new(dimension: impl Into<Dimension>, count: usize) -> Self {
let grid_dimension = Dimension::with_eq_rectangles(count);
debug_assert!(!grid_dimension.is_empty());
let dimension = dimension.into();
let tile_dimension = grid_dimension.scale(dimension);
let step = tile_dimension.x as usize;
let count = grid_dimension.x as usize + 1;
let vertical: EdgesMap = (0..)
.step_by(step)
.take(count)
.enumerate()
.map(|(i, x)| (if i == count - 1 { dimension.x } else { x }, i))
.collect();
let step = tile_dimension.y as usize;
let count = grid_dimension.y as usize + 1;
let horizontal: EdgesMap = (0..=dimension.y)
.step_by(step)
.take(count)
.enumerate()
.map(|(i, y)| (if i == count - 1 { dimension.y } else { y }, i))
.collect();
Self {
dimension: grid_dimension,
vertical,
horizontal,
}
}
fn get(&self, location: Location, scope: Scope) -> Option<Tile> {
let tile_edges = |edges: &EdgesMap, coordinate| {
use std::ops::Bound::*;
let mut range = edges.range((Unbounded, Included(coordinate)));
let (&low_bound, &index) = range.next_back()?;
let mut range = edges.range((Excluded(coordinate), Unbounded));
let (&up_bound, _) = range.next()?;
Some((index as i32, (low_bound, up_bound)))
};
let (x, (left, right)) = tile_edges(&self.vertical, location.x)?;
let (y, (top, bottom)) = tile_edges(&self.horizontal, location.y)?;
let index = Location { x, y }.one_dimensional(self.dimension);
let scope = scope.magnitude() as i32;
if left > location.x - scope || right < location.x + scope {
return Some(Tile::Unsync);
}
if top > location.y - scope || bottom < location.y + scope {
return Some(Tile::Unsync);
}
Some(Tile::Sync { index })
}
}