1use 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; #[cfg(feature = "rkyv")]
13use rkyv::{bytecheck, CheckBytes};
14
15#[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 pub a: Point<Real>,
28 pub b: Point<Real>,
30 pub c: Point<Real>,
32 pub d: Point<Real>,
34}
35
36#[derive(Copy, Clone, Debug)]
38pub enum TetrahedronPointLocation {
39 OnVertex(u32),
41 OnEdge(u32, [Real; 2]),
50 OnFace(u32, [Real; 3]),
57 OnSolid,
59}
60
61impl TetrahedronPointLocation {
62 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 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 #[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 pub fn from_array(arr: &[Point<Real>; 4]) -> &Tetrahedron {
116 unsafe { mem::transmute(arr) }
117 }
118
119 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 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 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 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 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 #[inline]
213 pub fn volume(&self) -> Real {
214 self.signed_volume().abs()
215 }
216
217 #[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 #[inline]
236 pub fn center(&self) -> Point<Real> {
237 utils::center(&[self.a, self.b, self.c, self.d])
238 }
239}