use crate::math::{Isometry, Point, Vector};
use crate::query::{PointProjection, PointQuery, PointQueryWithLocation};
use crate::shape::{FeatureId, Tetrahedron, TetrahedronPointLocation};
use na::{self, RealField};
impl<N: RealField> PointQuery<N> for Tetrahedron<N> {
#[inline]
fn project_point(&self, m: &Isometry<N>, pt: &Point<N>, solid: bool) -> PointProjection<N> {
let (projection, _) = self.project_point_with_location(m, pt, solid);
projection
}
#[inline]
fn project_point_with_feature(
&self,
m: &Isometry<N>,
pt: &Point<N>,
) -> (PointProjection<N>, FeatureId) {
let (proj, loc) = self.project_point_with_location(m, pt, false);
let feature = match loc {
TetrahedronPointLocation::OnVertex(i) => FeatureId::Vertex(i),
TetrahedronPointLocation::OnEdge(i, _) => FeatureId::Edge(i),
TetrahedronPointLocation::OnFace(i, _) => FeatureId::Face(i),
TetrahedronPointLocation::OnSolid => unreachable!(),
};
(proj, feature)
}
}
impl<N: RealField> PointQueryWithLocation<N> for Tetrahedron<N> {
type Location = TetrahedronPointLocation<N>;
#[inline]
fn project_point_with_location(
&self,
m: &Isometry<N>,
pt: &Point<N>,
solid: bool,
) -> (PointProjection<N>, Self::Location) {
let p = m.inverse_transform_point(pt);
let ab = *self.b() - *self.a();
let ac = *self.c() - *self.a();
let ad = *self.d() - *self.a();
let ap = p - *self.a();
let ap_ab = ap.dot(&ab);
let ap_ac = ap.dot(&ac);
let ap_ad = ap.dot(&ad);
let _0: N = na::zero();
if ap_ab <= _0 && ap_ac <= _0 && ap_ad <= _0 {
let proj = PointProjection::new(false, m * self.a());
return (proj, TetrahedronPointLocation::OnVertex(0));
}
let bc = *self.c() - *self.b();
let bd = *self.d() - *self.b();
let bp = p - *self.b();
let bp_bc = bp.dot(&bc);
let bp_bd = bp.dot(&bd);
let bp_ab = bp.dot(&ab);
if bp_bc <= _0 && bp_bd <= _0 && bp_ab >= _0 {
let proj = PointProjection::new(false, m * self.b());
return (proj, TetrahedronPointLocation::OnVertex(1));
}
let cd = *self.d() - *self.c();
let cp = p - *self.c();
let cp_ac = cp.dot(&ac);
let cp_bc = cp.dot(&bc);
let cp_cd = cp.dot(&cd);
if cp_cd <= _0 && cp_bc >= _0 && cp_ac >= _0 {
let proj = PointProjection::new(false, m * self.c());
return (proj, TetrahedronPointLocation::OnVertex(2));
}
let dp = p - *self.d();
let dp_cd = dp.dot(&cd);
let dp_bd = dp.dot(&bd);
let dp_ad = dp.dot(&ad);
if dp_ad >= _0 && dp_bd >= _0 && dp_cd >= _0 {
let proj = PointProjection::new(false, m * self.d());
return (proj, TetrahedronPointLocation::OnVertex(3));
}
#[inline(always)]
fn check_edge<N: RealField>(
i: usize,
m: &Isometry<N>,
a: &Point<N>,
_: &Point<N>,
nabc: &Vector<N>,
nabd: &Vector<N>,
ap: &Vector<N>,
ab: &Vector<N>,
ap_ab: N,
bp_ab: N,
) -> (
N,
N,
Option<(PointProjection<N>, TetrahedronPointLocation<N>)>,
) {
let _0: N = na::zero();
let _1: N = na::one();
let ab_ab = ap_ab - bp_ab;
let ap_x_ab = ap.cross(ab);
let dabc = ap_x_ab.dot(nabc);
let dabd = ap_x_ab.dot(nabd);
if ab_ab != _0 && dabc >= _0 && dabd >= _0 && ap_ab >= _0 && ap_ab <= ab_ab {
let u = ap_ab / ab_ab;
let bcoords = [_1 - u, u];
let res = a + ab * u;
let proj = PointProjection::new(false, m * res);
(
dabc,
dabd,
Some((proj, TetrahedronPointLocation::OnEdge(i, bcoords))),
)
} else {
(dabc, dabd, None)
}
}
let nabc = ab.cross(&ac);
let nabd = ab.cross(&ad);
let (dabc, dabd, res) = check_edge(
0,
m,
self.a(),
self.b(),
&nabc,
&nabd,
&ap,
&ab,
ap_ab,
bp_ab,
);
if let Some(res) = res {
return res;
}
let nacd = ac.cross(&ad);
let (dacd, dacb, res) = check_edge(
1,
m,
self.a(),
self.c(),
&nacd,
&-nabc,
&ap,
&ac,
ap_ac,
cp_ac,
);
if let Some(res) = res {
return res;
}
let (dadb, dadc, res) = check_edge(
2,
m,
self.a(),
self.d(),
&-nabd,
&-nacd,
&ap,
&ad,
ap_ad,
dp_ad,
);
if let Some(res) = res {
return res;
}
let nbcd = bc.cross(&bd);
let (dbca, dbcd, res) = check_edge(
3,
m,
self.b(),
self.c(),
&nabc,
&nbcd,
&bp,
&bc,
bp_bc,
cp_bc,
);
if let Some(res) = res {
return res;
}
let (dbdc, dbda, res) = check_edge(
4,
m,
self.b(),
self.d(),
&-nbcd,
&nabd,
&bp,
&bd,
bp_bd,
dp_bd,
);
if let Some(res) = res {
return res;
}
let (dcda, dcdb, res) = check_edge(
5,
m,
self.c(),
self.d(),
&nacd,
&nbcd,
&cp,
&cd,
cp_cd,
dp_cd,
);
if let Some(res) = res {
return res;
}
#[inline(always)]
fn check_face<N: RealField>(
i: usize,
a: &Point<N>,
b: &Point<N>,
c: &Point<N>,
m: &Isometry<N>,
ap: &Vector<N>,
bp: &Vector<N>,
cp: &Vector<N>,
ab: &Vector<N>,
ac: &Vector<N>,
ad: &Vector<N>,
dabc: N,
dbca: N,
dacb: N,
) -> Option<(PointProjection<N>, TetrahedronPointLocation<N>)> {
let _0: N = na::zero();
let _1: N = na::one();
if dabc < _0 && dbca < _0 && dacb < _0 {
let n = ab.cross(ac);
if n.dot(ad) * n.dot(ap) < _0 {
let normal = n.normalize();
let vc = normal.dot(&ap.cross(bp));
let va = normal.dot(&bp.cross(cp));
let vb = normal.dot(&cp.cross(ap));
let denom = va + vb + vc;
assert!(denom != _0);
let inv_denom = _1 / denom;
let bcoords = [va * inv_denom, vb * inv_denom, vc * inv_denom];
let res = a * bcoords[0] + b.coords * bcoords[1] + c.coords * bcoords[2];
let proj = PointProjection::new(false, m * res);
return Some((proj, TetrahedronPointLocation::OnFace(i, bcoords)));
}
}
return None;
}
if let Some(res) = check_face(
0,
self.a(),
self.b(),
self.c(),
m,
&ap,
&bp,
&cp,
&ab,
&ac,
&ad,
dabc,
dbca,
dacb,
) {
return res;
}
if let Some(res) = check_face(
1,
self.a(),
self.b(),
self.d(),
m,
&ap,
&bp,
&dp,
&ab,
&ad,
&ac,
dadb,
dabd,
dbda,
) {
return res;
}
if let Some(res) = check_face(
2,
self.a(),
self.c(),
self.d(),
m,
&ap,
&cp,
&dp,
&ac,
&ad,
&ab,
dacd,
dcda,
dadc,
) {
return res;
}
if let Some(res) = check_face(
3,
self.b(),
self.c(),
self.d(),
m,
&bp,
&cp,
&dp,
&bc,
&bd,
&-ab,
dbcd,
dcdb,
dbdc,
) {
return res;
}
if !solid {
unimplemented!("Non-solid ray-cast on a tetrahedron is not yet implemented.")
}
let proj = PointProjection::new(true, m * p);
return (proj, TetrahedronPointLocation::OnSolid);
}
}