use crate::math::{MatExt, Matrix, Real, Vector};
use crate::shape::{Segment, Triangle};
use crate::utils;
use core::mem;
#[cfg(feature = "dim3")]
use crate::math::CrossMatrix;
#[cfg(all(feature = "dim2", not(feature = "std")))]
use crate::math::ComplexField;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
)]
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct Tetrahedron {
pub a: Vector,
pub b: Vector,
pub c: Vector,
pub d: Vector,
}
#[derive(Copy, Clone, Debug)]
pub enum TetrahedronPointLocation {
OnVertex(u32),
OnEdge(u32, [Real; 2]),
OnFace(u32, [Real; 3]),
OnSolid,
}
impl TetrahedronPointLocation {
pub fn barycentric_coordinates(&self) -> Option<[Real; 4]> {
let mut bcoords = [0.0; 4];
match self {
TetrahedronPointLocation::OnVertex(i) => bcoords[*i as usize] = 1.0,
TetrahedronPointLocation::OnEdge(i, uv) => {
let idx = Tetrahedron::edge_ids(*i);
bcoords[idx.0 as usize] = uv[0];
bcoords[idx.1 as usize] = uv[1];
}
TetrahedronPointLocation::OnFace(i, uvw) => {
let idx = Tetrahedron::face_ids(*i);
bcoords[idx.0 as usize] = uvw[0];
bcoords[idx.1 as usize] = uvw[1];
bcoords[idx.2 as usize] = uvw[2];
}
TetrahedronPointLocation::OnSolid => {
return None;
}
}
Some(bcoords)
}
pub fn same_feature_as(&self, other: &TetrahedronPointLocation) -> bool {
match (*self, *other) {
(TetrahedronPointLocation::OnVertex(i), TetrahedronPointLocation::OnVertex(j)) => {
i == j
}
(TetrahedronPointLocation::OnEdge(i, _), TetrahedronPointLocation::OnEdge(j, _)) => {
i == j
}
(TetrahedronPointLocation::OnFace(i, _), TetrahedronPointLocation::OnFace(j, _)) => {
i == j
}
(TetrahedronPointLocation::OnSolid, TetrahedronPointLocation::OnSolid) => true,
_ => false,
}
}
}
impl Tetrahedron {
#[inline]
pub fn new(a: Vector, b: Vector, c: Vector, d: Vector) -> Tetrahedron {
Tetrahedron { a, b, c, d }
}
pub fn from_array(arr: &[Vector; 4]) -> &Tetrahedron {
unsafe { mem::transmute(arr) }
}
pub fn face(&self, i: usize) -> Triangle {
match i {
0 => Triangle::new(self.a, self.b, self.c),
1 => Triangle::new(self.a, self.b, self.d),
2 => Triangle::new(self.a, self.c, self.d),
3 => Triangle::new(self.b, self.c, self.d),
_ => panic!("Tetrahedron face index out of bounds (must be < 4."),
}
}
pub fn face_ids(i: u32) -> (u32, u32, u32) {
match i {
0 => (0, 1, 2),
1 => (0, 1, 3),
2 => (0, 2, 3),
3 => (1, 2, 3),
_ => panic!("Tetrahedron face index out of bounds (must be < 4."),
}
}
pub fn edge(&self, i: u32) -> Segment {
match i {
0 => Segment::new(self.a, self.b),
1 => Segment::new(self.a, self.c),
2 => Segment::new(self.a, self.d),
3 => Segment::new(self.b, self.c),
4 => Segment::new(self.b, self.d),
5 => Segment::new(self.c, self.d),
_ => panic!("Tetrahedron edge index out of bounds (must be < 6)."),
}
}
pub fn edge_ids(i: u32) -> (u32, u32) {
match i {
0 => (0, 1),
1 => (0, 2),
2 => (0, 3),
3 => (1, 2),
4 => (1, 3),
5 => (2, 3),
_ => panic!("Tetrahedron edge index out of bounds (must be < 6)."),
}
}
pub fn barycentric_coordinates(&self, p: Vector) -> Option<[Real; 4]> {
let ab = self.b - self.a;
let ac = self.c - self.a;
let ad = self.d - self.a;
let m = Matrix::from_cols_array(&[ab.x, ab.y, ab.z, ac.x, ac.y, ac.z, ad.x, ad.y, ad.z]);
m.try_inverse().map(|im| {
let bcoords = im * (p - self.a);
[
1.0 - bcoords.x - bcoords.y - bcoords.z,
bcoords.x,
bcoords.y,
bcoords.z,
]
})
}
#[inline]
pub fn volume(&self) -> Real {
self.signed_volume().abs()
}
#[inline]
pub fn signed_volume(&self) -> Real {
let p1p2 = self.b - self.a;
let p1p3 = self.c - self.a;
let p1p4 = self.d - self.a;
let mat = CrossMatrix::from_cols(p1p2, p1p3, p1p4);
mat.determinant() / 6.0
}
#[inline]
pub fn center(&self) -> Vector {
utils::center(&[self.a, self.b, self.c, self.d])
}
}