use std::{ops::Range, sync::Arc};
use arrayvec::ArrayVec;
use crate::{
Chunk, Layer,
debug::{Debug, DebugContent},
rolling_grid::GridPoint,
vec2::{Bounds, Point2d},
};
use super::UniformPoint;
pub trait Reducible: From<Point2d> + PartialEq + Clone + Sized + 'static {
const RADIUS_RANGE: Range<i64>;
fn radius(&self) -> i64;
fn position(&self) -> Point2d;
fn debug(&self) -> Vec<DebugContent> {
vec![DebugContent::Circle {
center: self.position(),
radius: self.radius() as f32,
}]
}
}
#[derive(PartialEq, Debug, Clone)]
pub struct ReducedUniformPoint<P, const SIZE: u8, const SALT: u64> {
pub points: ArrayVec<P, 7>,
}
impl<P, const SIZE: u8, const SALT: u64> Default for ReducedUniformPoint<P, SIZE, SALT> {
fn default() -> Self {
Self {
points: Default::default(),
}
}
}
impl<P: Reducible, const SIZE: u8, const SALT: u64> Chunk for ReducedUniformPoint<P, SIZE, SALT> {
type LayerStore<T> = Arc<T>;
type Dependencies = Layer<UniformPoint<P, SIZE, SALT>>;
const SIZE: Point2d<u8> = Point2d::splat(SIZE);
fn compute(raw_points: &Self::Dependencies, index: GridPoint<Self>) -> Self {
let mut points = ArrayVec::new();
'points: for p in raw_points
.get_or_compute(index.into_same_chunk_size())
.points
{
for other in raw_points.get_range(
Bounds::point(p.position()).pad(Point2d::splat(p.radius() + P::RADIUS_RANGE.end)),
) {
for other in other.points {
if other == p {
continue;
}
let lower_priority = p
.radius()
.cmp(&other.radius())
.then_with(|| p.position().cmp(&other.position()))
.is_lt();
if other.position().manhattan_dist(p.position()) < p.radius() + other.radius()
&& lower_priority
{
continue 'points;
}
}
}
points.push(p);
}
ReducedUniformPoint { points }
}
fn clear(raw_points: &Self::Dependencies, index: GridPoint<Self>) {
raw_points.clear(Self::bounds(index));
}
}
impl<P: Reducible, const SIZE: u8, const SALT: u64> Debug for ReducedUniformPoint<P, SIZE, SALT> {
fn debug(&self) -> Vec<DebugContent> {
self.points
.iter()
.flat_map(|p| {
let mut debug = p.debug();
for debug in &mut debug {
match debug {
DebugContent::Line(..) => {}
DebugContent::Circle { radius, .. } => *radius = 1.,
DebugContent::Text { .. } => {}
}
}
debug
})
.collect()
}
}