Skip to main content

jagua_rs/collision_detection/hazards/
hazard.rs

1use crate::collision_detection::narrow_phase::{EdgeBoxes, EdgeChains, EdgeSet};
2use crate::geometry::DTransformation;
3use crate::geometry::geo_enums::GeoPosition;
4use crate::geometry::primitives::{Edge, Rect, SPolygon};
5use slotmap::new_key_type;
6use std::fmt::Debug;
7use std::hash::Hash;
8use std::sync::Arc;
9
10new_key_type! {
11    /// Key to identify hazards inside the CDE.
12    pub struct HazKey;
13
14    /// Key of a placed item, identifying it in [`BasicHazardEntity::PlacedItem`].
15    pub struct PItemKey;
16}
17
18/// Any spatial constraint affecting the feasibility of a placement of an Item.
19/// The entity type `E` defines what can induce a hazard; see [`HazardEntity`].
20/// Cloning is cheap: the shape and the geometry derived from it are shared.
21#[derive(Clone, Debug)]
22pub struct Hazard<E = BasicHazardEntity> {
23    entity: E,
24    shape: Arc<SPolygon>,
25    geometry: HazardGeometry,
26}
27
28impl<E: HazardEntity> Hazard<E> {
29    #[must_use]
30    pub fn new(entity: E, shape: Arc<SPolygon>) -> Self {
31        let geometry = HazardGeometry::new(&shape, entity.scope());
32        Self {
33            entity,
34            shape,
35            geometry,
36        }
37    }
38}
39
40impl<E: Copy> Hazard<E> {
41    /// The entity inducing the hazard
42    #[must_use]
43    pub fn entity(&self) -> E {
44        self.entity
45    }
46
47    /// The shape of the hazard
48    #[must_use]
49    pub fn shape(&self) -> &Arc<SPolygon> {
50        &self.shape
51    }
52
53    pub(crate) fn geometry(&self) -> &HazardGeometry {
54        &self.geometry
55    }
56}
57
58impl<E> EdgeSet for Hazard<E> {
59    #[inline(always)]
60    #[allow(clippy::inline_always)] // part of the query loops
61    fn any_edge_overlapping(&self, r: Rect, f: impl FnMut(&Edge) -> bool) -> bool {
62        match &self.geometry {
63            HazardGeometry::Interior(boxes) => boxes.any_edge_overlapping(&self.shape, r, f),
64            HazardGeometry::Exterior { chains, .. } => chains.any_edge_overlapping(r, f),
65        }
66    }
67}
68
69/// Geometry derived from a hazard's shape to speed up collision queries.
70/// Cloning shares the derived data.
71#[derive(Clone, Debug)]
72pub(crate) enum HazardGeometry {
73    Interior(EdgeBoxes),
74    /// The container's outline
75    Exterior {
76        chains: Arc<EdgeChains>,
77        /// Whether the outline is its own bounding box
78        is_rect: bool,
79    },
80}
81
82impl HazardGeometry {
83    #[allow(clippy::float_cmp)] // the bounding box's sides are vertex coordinates
84    fn new(shape: &SPolygon, scope: GeoPosition) -> Self {
85        match scope {
86            GeoPosition::Interior => HazardGeometry::Interior(EdgeBoxes::new(shape)),
87            GeoPosition::Exterior => {
88                let bbox = shape.bbox;
89                let is_rect = shape.n_vertices() == 4
90                    && shape.vertices.iter().all(|p| {
91                        (p.0 == bbox.x_min || p.0 == bbox.x_max)
92                            && (p.1 == bbox.y_min || p.1 == bbox.y_max)
93                    });
94                HazardGeometry::Exterior {
95                    chains: Arc::new(EdgeChains::new(shape)),
96                    is_rect,
97                }
98            }
99        }
100    }
101}
102
103/// Entity inducing a [`Hazard`]: everything the collision engine needs to know about it.
104/// Each entity registered in a [`CDEngine`](crate::collision_detection::CDEngine) must be unique.
105///
106/// [`BasicHazardEntity`] covers the entities of this crate's layouts. Applications with other
107/// kinds of hazards can define their own.
108pub trait HazardEntity: Copy + Eq + Hash + Debug {
109    /// Whether the hazard covers the interior or the exterior of its shape.
110    /// An engine needs exactly one exterior hazard.
111    fn scope(&self) -> GeoPosition;
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
115/// Entity inducing a [`Hazard`] in this crate's layouts.
116/// All entities are uniquely identified.
117pub enum BasicHazardEntity {
118    /// An item placed in the layout, defined by its id, applied transformation and key
119    PlacedItem {
120        id: usize,
121        dt: DTransformation,
122        pk: PItemKey,
123    },
124    /// Represents all regions outside the container
125    Exterior,
126    /// Represents a hole in the container.
127    Hole { idx: usize },
128    /// Represents a zone in the container with a specific quality level that is inferior to the base quality.
129    InferiorQualityZone { quality: usize, idx: usize },
130}
131
132impl HazardEntity for BasicHazardEntity {
133    fn scope(&self) -> GeoPosition {
134        match self {
135            BasicHazardEntity::PlacedItem { .. }
136            | BasicHazardEntity::Hole { .. }
137            | BasicHazardEntity::InferiorQualityZone { .. } => GeoPosition::Interior,
138            BasicHazardEntity::Exterior => GeoPosition::Exterior,
139        }
140    }
141}