jagua_rs/collision_detection/quadtree/
qt_partial_hazard.rs

1use crate::collision_detection::quadtree::qt_traits::QTQueryable;
2use crate::geometry::geo_traits::CollidesWith;
3use crate::geometry::primitives::{Edge, Rect, SPolygon};
4
5/// Defines a set of edges from a hazard that is partially active in the [`QTNode`](crate::collision_detection::quadtree::QTNode).
6#[derive(Clone, Debug)]
7pub struct QTHazPartial {
8    /// The edges that are active in the quadtree-node.
9    pub edges: Vec<Edge>,
10    /// A bounding box that guarantees all edges are contained within it. (used for fail fast)
11    pub ff_bbox: Rect,
12}
13
14impl QTHazPartial {
15    pub fn from_entire_shape(shape: &SPolygon) -> Self {
16        let edges = shape.edge_iter().collect();
17        let ff_bbox = shape.bbox;
18        Self { edges, ff_bbox }
19    }
20    pub fn from_parent(parent: &QTHazPartial, restricted_edges: Vec<Edge>) -> Self {
21        debug_assert!(!restricted_edges.is_empty());
22        debug_assert!(restricted_edges.iter().all(|e| parent.edges.contains(e)));
23        let ff_bbox = {
24            //calculate a bounding box around the edges
25            if parent.edges.len() == restricted_edges.len() {
26                // If the edges cover the entire shape, use the shape's bounding box
27                parent.ff_bbox
28            } else {
29                // Otherwise, calculate the bounding box from the edges
30                let (mut x_min, mut y_min, mut x_max, mut y_max) = (
31                    f32::INFINITY,
32                    f32::INFINITY,
33                    f32::NEG_INFINITY,
34                    f32::NEG_INFINITY,
35                );
36                for edge in &restricted_edges {
37                    x_min = x_min.min(edge.start.x()).min(edge.end.x());
38                    y_min = y_min.min(edge.start.y()).min(edge.end.y());
39                    x_max = x_max.max(edge.start.x()).max(edge.end.x());
40                    y_max = y_max.max(edge.start.y()).max(edge.end.y());
41                }
42                if x_min < x_max && y_min < y_max {
43                    Rect {
44                        x_min,
45                        y_min,
46                        x_max,
47                        y_max,
48                    }
49                } else {
50                    // If the edges are all aligned to an axis, use the shape's bounding box
51                    parent.ff_bbox
52                }
53            }
54        };
55
56        Self {
57            edges: restricted_edges,
58            ff_bbox,
59        }
60    }
61
62    pub fn n_edges(&self) -> usize {
63        self.edges.len()
64    }
65}
66
67impl<T: QTQueryable> CollidesWith<T> for QTHazPartial {
68    fn collides_with(&self, entity: &T) -> bool {
69        // If the entity does not collide with the bounding box of the hazard, it cannot collide with the hazard
70        entity.collides_with(&self.ff_bbox) && self.edges.iter().any(|e| entity.collides_with(e))
71    }
72}