layer_proc_gen/generic_layers/
reduced_points.rs

1use std::{ops::Range, sync::Arc};
2
3use arrayvec::ArrayVec;
4
5use crate::{
6    Chunk, Layer,
7    debug::{Debug, DebugContent},
8    rolling_grid::GridPoint,
9    vec2::{Bounds, Point2d},
10};
11
12use super::UniformPoint;
13
14/// Represents point like types that do not want to be close to other types.
15/// The larger of two objects is kept if they are too close to each other.
16/// If both objects have the same radius, the one with the higher X coordinate is kept (or higher Y if X is also the same).
17pub trait Reducible: From<Point2d> + PartialEq + Clone + Sized + 'static {
18    /// The range of radii that `radius` can return.
19    const RADIUS_RANGE: Range<i64>;
20
21    /// The radius around the thing to be kept free from other things.
22    fn radius(&self) -> i64;
23    /// Center position of the circle to keep free of other things.
24    fn position(&self) -> Point2d;
25    /// Debug representation. Usually contains just a single thing, the item itself,
26    /// but can be overriden to emit addition information.
27    fn debug(&self) -> Vec<DebugContent> {
28        vec![DebugContent::Circle {
29            center: self.position(),
30            radius: self.radius() as f32,
31        }]
32    }
33}
34
35#[derive(PartialEq, Debug, Clone)]
36/// Removes locations that are too close to others.
37pub struct ReducedUniformPoint<P, const SIZE: u8, const SALT: u64> {
38    /// The points remaining after removing ones that are too close to others.
39    pub points: ArrayVec<P, 7>,
40}
41
42impl<P, const SIZE: u8, const SALT: u64> Default for ReducedUniformPoint<P, SIZE, SALT> {
43    fn default() -> Self {
44        Self {
45            points: Default::default(),
46        }
47    }
48}
49
50impl<P: Reducible, const SIZE: u8, const SALT: u64> Chunk for ReducedUniformPoint<P, SIZE, SALT> {
51    type LayerStore<T> = Arc<T>;
52    type Dependencies = Layer<UniformPoint<P, SIZE, SALT>>;
53    const SIZE: Point2d<u8> = Point2d::splat(SIZE);
54
55    fn compute(raw_points: &Self::Dependencies, index: GridPoint<Self>) -> Self {
56        let mut points = ArrayVec::new();
57        'points: for p in raw_points
58            .get_or_compute(index.into_same_chunk_size())
59            .points
60        {
61            for other in raw_points.get_range(
62                Bounds::point(p.position()).pad(Point2d::splat(p.radius() + P::RADIUS_RANGE.end)),
63            ) {
64                for other in other.points {
65                    if other == p {
66                        continue;
67                    }
68
69                    // prefer to delete lower radius, then lower x, then lower y
70                    let lower_priority = p
71                        .radius()
72                        .cmp(&other.radius())
73                        .then_with(|| p.position().cmp(&other.position()))
74                        .is_lt();
75
76                    // skip current point if another point's center is within our radius and we have lower priority
77                    if other.position().manhattan_dist(p.position()) < p.radius() + other.radius()
78                        && lower_priority
79                    {
80                        continue 'points;
81                    }
82                }
83            }
84            points.push(p);
85        }
86        ReducedUniformPoint { points }
87    }
88
89    fn clear(raw_points: &Self::Dependencies, index: GridPoint<Self>) {
90        raw_points.clear(Self::bounds(index));
91    }
92}
93
94impl<P: Reducible, const SIZE: u8, const SALT: u64> Debug for ReducedUniformPoint<P, SIZE, SALT> {
95    fn debug(&self) -> Vec<DebugContent> {
96        self.points
97            .iter()
98            .flat_map(|p| {
99                let mut debug = p.debug();
100                for debug in &mut debug {
101                    // After reducing, the radius is irrelevant and it is nicer to represent it as a point.
102                    match debug {
103                        DebugContent::Line(..) => {}
104                        DebugContent::Circle { radius, .. } => *radius = 1.,
105                        DebugContent::Text { .. } => {}
106                    }
107                }
108                debug
109            })
110            .collect()
111    }
112}