parry3d/shape/
tetrahedron.rs

1//! Definition of the tetrahedron shape.
2
3use crate::math::{Matrix, Point, Real};
4use crate::shape::{Segment, Triangle};
5use crate::utils;
6use core::mem;
7use na::Matrix3;
8
9#[cfg(all(feature = "dim2", not(feature = "std")))]
10use na::ComplexField; // for .abs()
11
12#[cfg(feature = "rkyv")]
13use rkyv::{bytecheck, CheckBytes};
14
15/// A tetrahedron with 4 vertices.
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
18#[cfg_attr(
19    feature = "rkyv",
20    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
21    archive(as = "Self")
22)]
23#[derive(Copy, Clone, Debug)]
24#[repr(C)]
25pub struct Tetrahedron {
26    /// The tetrahedron's first point.
27    pub a: Point<Real>,
28    /// The tetrahedron's second point.
29    pub b: Point<Real>,
30    /// The tetrahedron's third point.
31    pub c: Point<Real>,
32    /// The tetrahedron's fourth point.
33    pub d: Point<Real>,
34}
35
36/// Logical description of the location of a point on a triangle.
37#[derive(Copy, Clone, Debug)]
38pub enum TetrahedronPointLocation {
39    /// The point lies on a vertex.
40    OnVertex(u32),
41    /// The point lies on an edge.
42    ///
43    /// The 0-th edge is the segment AB.
44    /// The 1-st edge is the segment AC.
45    /// The 2-nd edge is the segment AD.
46    /// The 3-rd edge is the segment BC.
47    /// The 4-th edge is the segment BD.
48    /// The 5-th edge is the segment CD.
49    OnEdge(u32, [Real; 2]),
50    /// The point lies on a triangular face interior.
51    ///
52    /// The first face is the triangle ABC.
53    /// The second face is the triangle ABD.
54    /// The third face is the triangle ACD.
55    /// The fourth face is the triangle BDC.
56    OnFace(u32, [Real; 3]),
57    /// The point lies inside of the tetrahedron.
58    OnSolid,
59}
60
61impl TetrahedronPointLocation {
62    /// The barycentric coordinates corresponding to this point location.
63    ///
64    /// Returns `None` if the location is `TetrahedronPointLocation::OnSolid`.
65    pub fn barycentric_coordinates(&self) -> Option<[Real; 4]> {
66        let mut bcoords = [0.0; 4];
67
68        match self {
69            TetrahedronPointLocation::OnVertex(i) => bcoords[*i as usize] = 1.0,
70            TetrahedronPointLocation::OnEdge(i, uv) => {
71                let idx = Tetrahedron::edge_ids(*i);
72                bcoords[idx.0 as usize] = uv[0];
73                bcoords[idx.1 as usize] = uv[1];
74            }
75            TetrahedronPointLocation::OnFace(i, uvw) => {
76                let idx = Tetrahedron::face_ids(*i);
77                bcoords[idx.0 as usize] = uvw[0];
78                bcoords[idx.1 as usize] = uvw[1];
79                bcoords[idx.2 as usize] = uvw[2];
80            }
81            TetrahedronPointLocation::OnSolid => {
82                return None;
83            }
84        }
85
86        Some(bcoords)
87    }
88
89    /// Returns `true` if both `self` and `other` correspond to points on the same feature of a tetrahedron.
90    pub fn same_feature_as(&self, other: &TetrahedronPointLocation) -> bool {
91        match (*self, *other) {
92            (TetrahedronPointLocation::OnVertex(i), TetrahedronPointLocation::OnVertex(j)) => {
93                i == j
94            }
95            (TetrahedronPointLocation::OnEdge(i, _), TetrahedronPointLocation::OnEdge(j, _)) => {
96                i == j
97            }
98            (TetrahedronPointLocation::OnFace(i, _), TetrahedronPointLocation::OnFace(j, _)) => {
99                i == j
100            }
101            (TetrahedronPointLocation::OnSolid, TetrahedronPointLocation::OnSolid) => true,
102            _ => false,
103        }
104    }
105}
106
107impl Tetrahedron {
108    /// Creates a tetrahedron from four points.
109    #[inline]
110    pub fn new(a: Point<Real>, b: Point<Real>, c: Point<Real>, d: Point<Real>) -> Tetrahedron {
111        Tetrahedron { a, b, c, d }
112    }
113
114    /// Creates the reference to a tetrahedron from the reference to an array of four points.
115    pub fn from_array(arr: &[Point<Real>; 4]) -> &Tetrahedron {
116        unsafe { mem::transmute(arr) }
117    }
118
119    /// Returns the i-th face of this tetrahedron.
120    ///
121    /// The 0-th face is the triangle ABC.
122    /// The 1-st face is the triangle ABD.
123    /// The 2-nd face is the triangle ACD.
124    /// The 3-rd face is the triangle BCD.
125    pub fn face(&self, i: usize) -> Triangle {
126        match i {
127            0 => Triangle::new(self.a, self.b, self.c),
128            1 => Triangle::new(self.a, self.b, self.d),
129            2 => Triangle::new(self.a, self.c, self.d),
130            3 => Triangle::new(self.b, self.c, self.d),
131            _ => panic!("Tetrahedron face index out of bounds (must be < 4."),
132        }
133    }
134
135    /// Returns the i-th face of this tetrahedron.
136    ///
137    /// The 0-th face is the triangle ABC.
138    /// The 1-st face is the triangle ABD.
139    /// The 2-nd face is the triangle ACD.
140    /// The 3-rd face is the triangle BCD.
141    pub fn face_ids(i: u32) -> (u32, u32, u32) {
142        match i {
143            0 => (0, 1, 2),
144            1 => (0, 1, 3),
145            2 => (0, 2, 3),
146            3 => (1, 2, 3),
147            _ => panic!("Tetrahedron face index out of bounds (must be < 4."),
148        }
149    }
150
151    /// Returns the i-th edge of this tetrahedron.
152    ///
153    /// The 0-th edge is the segment AB.
154    /// The 1-st edge is the segment AC.
155    /// The 2-nd edge is the segment AD.
156    /// The 3-rd edge is the segment BC.
157    /// The 4-th edge is the segment BD.
158    /// The 5-th edge is the segment CD.
159    pub fn edge(&self, i: u32) -> Segment {
160        match i {
161            0 => Segment::new(self.a, self.b),
162            1 => Segment::new(self.a, self.c),
163            2 => Segment::new(self.a, self.d),
164            3 => Segment::new(self.b, self.c),
165            4 => Segment::new(self.b, self.d),
166            5 => Segment::new(self.c, self.d),
167            _ => panic!("Tetrahedron edge index out of bounds (must be < 6)."),
168        }
169    }
170
171    /// Returns the indices of the vertices of the i-th edge of this tetrahedron.
172    ///
173    /// The 0-th edge is the segment AB.
174    /// The 1-st edge is the segment AC.
175    /// The 2-nd edge is the segment AD.
176    /// The 3-rd edge is the segment BC.
177    /// The 4-th edge is the segment BD.
178    /// The 5-th edge is the segment CD.
179    pub fn edge_ids(i: u32) -> (u32, u32) {
180        match i {
181            0 => (0, 1),
182            1 => (0, 2),
183            2 => (0, 3),
184            3 => (1, 2),
185            4 => (1, 3),
186            5 => (2, 3),
187            _ => panic!("Tetrahedron edge index out of bounds (must be < 6)."),
188        }
189    }
190
191    /// Computes the barycentric coordinates of the given point in the coordinate system of this tetrahedron.
192    ///
193    /// Returns `None` if this tetrahedron is degenerate.
194    pub fn barycentric_coordinates(&self, p: &Point<Real>) -> Option<[Real; 4]> {
195        let ab = self.b - self.a;
196        let ac = self.c - self.a;
197        let ad = self.d - self.a;
198        let m = Matrix::new(ab.x, ac.x, ad.x, ab.y, ac.y, ad.y, ab.z, ac.z, ad.z);
199
200        m.try_inverse().map(|im| {
201            let bcoords = im * (p - self.a);
202            [
203                1.0 - bcoords.x - bcoords.y - bcoords.z,
204                bcoords.x,
205                bcoords.y,
206                bcoords.z,
207            ]
208        })
209    }
210
211    /// Computes the volume of this tetrahedron.
212    #[inline]
213    pub fn volume(&self) -> Real {
214        self.signed_volume().abs()
215    }
216
217    /// Computes the signed volume of this tetrahedron.
218    ///
219    /// If it is positive, `p4` is on the half-space pointed by the normal
220    /// of the oriented triangle `(p1, p2, p3)`.
221    #[inline]
222    pub fn signed_volume(&self) -> Real {
223        let p1p2 = self.b - self.a;
224        let p1p3 = self.c - self.a;
225        let p1p4 = self.d - self.a;
226
227        let mat = Matrix3::new(
228            p1p2[0], p1p3[0], p1p4[0], p1p2[1], p1p3[1], p1p4[1], p1p2[2], p1p3[2], p1p4[2],
229        );
230
231        mat.determinant() / na::convert::<f64, Real>(6.0f64)
232    }
233
234    /// Computes the center of this tetrahedron.
235    #[inline]
236    pub fn center(&self) -> Point<Real> {
237        utils::center(&[self.a, self.b, self.c, self.d])
238    }
239}