fenris_geometry/primitives/
quad.rs

1use crate::{
2    AxisAlignedBoundingBox2d, BoundedGeometry, ConvexPolygon3d, Distance, SimplePolygon2d, Triangle, Triangle2d,
3};
4use fenris_traits::Real;
5use itertools::izip;
6use nalgebra::{Point2, Point3, Scalar, U2};
7
8#[derive(Debug, Copy, Clone, PartialEq)]
9pub struct Quad3d<T: Scalar> {
10    vertices: [Point3<T>; 4],
11}
12
13impl<T: Scalar> Quad3d<T> {
14    pub fn from_vertices(vertices: [Point3<T>; 4]) -> Self {
15        Self { vertices }
16    }
17}
18
19impl<'a, T> ConvexPolygon3d<'a, T> for Quad3d<T>
20where
21    T: Scalar,
22{
23    fn num_vertices(&self) -> usize {
24        4
25    }
26
27    fn get_vertex(&self, index: usize) -> Option<Point3<T>> {
28        self.vertices.get(index).cloned()
29    }
30}
31
32impl<T> BoundedGeometry<T> for Quad2d<T>
33where
34    T: Real,
35{
36    type Dimension = U2;
37
38    fn bounding_box(&self) -> AxisAlignedBoundingBox2d<T> {
39        AxisAlignedBoundingBox2d::from_points(&self.0).expect("Triangle always has > 0 vertices")
40    }
41}
42
43#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44/// A quadrilateral consisting of four vertices, assumed to be specified in counter-clockwise
45/// winding order.
46pub struct Quad2d<T: Scalar>(pub [Point2<T>; 4]);
47
48impl<T> Quad2d<T>
49where
50    T: Real,
51{
52    /// Returns the index of a concave corner of the quadrilateral, if there is any.
53    pub fn concave_corner(&self) -> Option<usize> {
54        for i in 0..4 {
55            let x_next = self.0[(i + 2) % 4];
56            let x_curr = self.0[(i + 1) % 4];
57            let x_prev = self.0[(i + 1) % 4];
58
59            let a = x_next - x_curr;
60            let b = x_prev - x_curr;
61            // perp gives "2d cross product", which when negative means that the interior angle
62            // is creater than 180 degrees, and so the corner must be concave
63            if a.perp(&b) < T::zero() {
64                return Some(i + 1);
65            }
66        }
67
68        None
69    }
70
71    /// Splits the quad into two triangles represented by local indices { 0, 1, 2, 3 }
72    /// which correspond to the quad's vertices.
73    ///
74    /// While the quad may be concave, it is assumed that it has no self-intersections and that
75    /// all vertices are unique.
76    pub fn split_into_triangle_connectivities(&self) -> ([usize; 3], [usize; 3]) {
77        if let Some(concave_corner_index) = self.concave_corner() {
78            let i = concave_corner_index;
79            let triangle1 = [(i + 2) % 4, (i + 3) % 4, (i + 0) % 4];
80            let triangle2 = [(i + 2) % 4, (i + 0) % 4, (i + 1) % 4];
81            (triangle1, triangle2)
82        } else {
83            // Split arbitrarily, but in a regular fashion
84            let triangle1 = [0, 1, 2];
85            let triangle2 = [0, 2, 3];
86            (triangle1, triangle2)
87        }
88    }
89
90    pub fn split_into_triangles(&self) -> (Triangle2d<T>, Triangle2d<T>) {
91        let (conn1, conn2) = self.split_into_triangle_connectivities();
92        let mut vertices1 = [Point2::origin(); 3];
93        let mut vertices2 = [Point2::origin(); 3];
94
95        for (v, idx) in izip!(&mut vertices1, &conn1) {
96            *v = self.0[*idx];
97        }
98
99        for (v, idx) in izip!(&mut vertices2, &conn2) {
100            *v = self.0[*idx];
101        }
102
103        let tri1 = Triangle(vertices1);
104        let tri2 = Triangle(vertices2);
105
106        (tri1, tri2)
107    }
108
109    pub fn area(&self) -> T {
110        let (tri1, tri2) = self.split_into_triangles();
111        tri1.area() + tri2.area()
112    }
113}
114
115impl<T> Distance<T, Point2<T>> for Quad2d<T>
116where
117    T: Real,
118{
119    fn distance(&self, point: &Point2<T>) -> T {
120        // TODO: Avoid heap allocation
121        SimplePolygon2d::from_vertices(self.0.to_vec()).distance(point)
122    }
123}