pub const EDGE2NODE: [[usize; 2]; 6] = [
[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3], ];
pub const FACE2IDX: [usize; 5] = [0, 3, 6, 9, 12];
pub const IDX2NODE: [usize; 12] = [1, 2, 3, 2, 0, 3, 0, 1, 3, 0, 2, 1];
pub fn volume<T>(v1: &[T; 3], v2: &[T; 3], v3: &[T; 3], v4: &[T; 3]) -> T
where
T: num_traits::Float,
{
let three = T::one() + T::one() + T::one();
let one_6th = T::one() / (three + three);
let a0 =
(v2[0] - v1[0]) * ((v3[1] - v1[1]) * (v4[2] - v1[2]) - (v4[1] - v1[1]) * (v3[2] - v1[2]));
let a1 =
-(v2[1] - v1[1]) * ((v3[0] - v1[0]) * (v4[2] - v1[2]) - (v4[0] - v1[0]) * (v3[2] - v1[2]));
let a2 =
(v2[2] - v1[2]) * ((v3[0] - v1[0]) * (v4[1] - v1[1]) - (v4[0] - v1[0]) * (v3[1] - v1[1]));
(a0 + a1 + a2) * one_6th
}
pub fn solid_angle<T>(r1: &[T; 3], r2: &[T; 3], r3: &[T; 3]) -> T
where
T: num_traits::Float,
{
use crate::vec3::Vec3;
let zero = T::zero();
let two = T::one() + T::one();
let n1 = r1.norm();
let n2 = r2.norm();
let n3 = r3.norm();
if n1 == zero || n2 == zero || n3 == zero {
return zero;
}
let triple = r1.dot(&crate::vec3::cross(r2, r3)); let denom = n1 * n2 * n3 + r1.dot(r2) * n3 + r2.dot(r3) * n1 + r3.dot(r1) * n2;
two * triple.atan2(denom)
}
pub fn gauss_linking_number_edge_edge<T>(a: &[T; 3], b: &[T; 3], c: &[T; 3], d: &[T; 3]) -> T
where
T: num_traits::Float,
{
use crate::vec3::Vec3;
let t1 = solid_angle(&c.sub(a), &d.sub(a), &b.sub(a));
let t2 = solid_angle(&c.sub(b), &d.sub(b), &a.sub(b));
t1 - t2
}
pub fn barycentric_coord_for_origin<Real>(
q0: &[Real; 3],
q1: &[Real; 3],
q2: &[Real; 3],
q3: &[Real; 3],
) -> Option<(Real, Real, Real)>
where
Real: num_traits::Float,
{
let total = volume(q0, q1, q2, q3);
let zero = Real::zero();
let eps = Real::epsilon();
if total.abs() < eps {
return None;
}
let origin = &[zero; 3];
let r0 = volume(origin, q1, q2, q3) / total;
let r1 = volume(q0, origin, q2, q3) / total;
let r2 = volume(q0, q1, origin, q3) / total;
Some((r0, r1, r2))
}
#[allow(unused_assignments)]
pub fn nearest_to_origin<Real>(
q0: &[Real; 3],
q1: &[Real; 3],
q2: &[Real; 3],
q3: &[Real; 3],
) -> ([Real; 3], Real, Real, Real)
where
Real: num_traits::Float + std::fmt::Debug,
{
let zero = Real::zero();
let one = Real::one();
let mut r0 = zero;
let mut r1 = zero;
let mut r2 = zero;
let mut r3 = zero;
let mut p_min = *q0;
if let Some((rr0, rr1, rr2)) = barycentric_coord_for_origin(q0, q1, q2, q3) {
r0 = rr0;
r1 = rr1;
r2 = rr2;
r3 = one - r0 - r1 - r2;
p_min = crate::vec3::add4(q0, r0, q1, r1, q2, r2, q3, r3);
if r0 > zero && r1 > zero && r2 > zero && r3 > zero {
return (p_min, r0, r1, r2);
}
}
{
let (p, a1, a2, a3) = crate::tri3::nearest_to_origin3(q1, q2, q3);
p_min = p;
r0 = zero;
r1 = a1;
r2 = a2;
r3 = a3;
if r1 > zero && r2 > zero && r3 > zero {
return (p_min, r0, r1, r2);
}
}
{
let (p, a2, a3, a0) = crate::tri3::nearest_to_origin3(q2, q3, q0);
p_min = p;
r0 = a0;
r1 = zero;
r2 = a2;
r3 = a3;
if r2 > zero && r3 > zero && r0 > zero {
return (p_min, r0, r1, r2);
}
}
{
let (p, a3, a0, a1) = crate::tri3::nearest_to_origin3(q3, q0, q1);
p_min = p;
r0 = a0;
r1 = a1;
r2 = zero;
r3 = a3;
if r3 > zero && r0 > zero && r1 > zero {
return (p_min, r0, r1, r2);
}
}
{
let (p, a0, a1, a2) = crate::tri3::nearest_to_origin3(q0, q1, q2);
p_min = p;
r0 = a0;
r1 = a1;
r2 = a2;
r3 = zero;
if r0 > zero && r1 > zero && r2 > zero {
return (p_min, r0, r1, r2);
}
}
let mut d_min = crate::vec3::norm(q0);
{
let (p01, s0, s1) = crate::edge3::nearest_to_origin3(q0, q1);
let d01 = crate::vec3::norm(&p01);
if d01 < d_min {
d_min = d01;
p_min = p01;
r0 = s0;
r1 = s1;
r2 = zero;
r3 = zero;
}
}
{
let (p02, s0, s2) = crate::edge3::nearest_to_origin3(q0, q2);
let d02 = crate::vec3::norm(&p02);
if d02 < d_min {
d_min = d02;
p_min = p02;
r0 = s0;
r1 = zero;
r2 = s2;
r3 = zero;
}
}
{
let (p03, s0, s3) = crate::edge3::nearest_to_origin3(q0, q3);
let d03 = crate::vec3::norm(&p03);
if d03 < d_min {
d_min = d03;
p_min = p03;
r0 = s0;
r1 = zero;
r2 = zero;
r3 = s3;
}
}
{
let (p12, s1, s2) = crate::edge3::nearest_to_origin3(q1, q2);
let d12 = crate::vec3::norm(&p12);
if d12 < d_min {
d_min = d12;
p_min = p12;
r0 = zero;
r1 = s1;
r2 = s2;
r3 = zero;
}
}
{
let (p13, s1, s3) = crate::edge3::nearest_to_origin3(q1, q3);
let d13 = crate::vec3::norm(&p13);
if d13 < d_min {
d_min = d13;
p_min = p13;
r0 = zero;
r1 = s1;
r2 = zero;
r3 = s3;
}
}
{
let (p23, s2, s3) = crate::edge3::nearest_to_origin3(q2, q3);
let d23 = crate::vec3::norm(&p23);
if d23 < d_min {
let _ = d_min;
p_min = p23;
r0 = zero;
r1 = zero;
r2 = s2;
r3 = s3;
}
}
(p_min, r0, r1, r2)
}
#[test]
fn test_nearest_to_origin() {
let q0 = [1.0f64, 1.0, -1.0];
let q1 = [1.0, -1.0, 1.0];
let q2 = [-1.0, 1.0, 1.0];
let q3 = [-1.0, -1.0, -1.0];
{
let (p, r0, r1, r2) = nearest_to_origin(&q0, &q1, &q2, &q3);
let r3 = 1.0 - r0 - r1 - r2;
assert!(
r0 > 0.0 && r1 > 0.0 && r2 > 0.0 && r3 > 0.0,
"bary coords should all be positive inside"
);
assert!(
crate::vec3::norm(&p) < 1.0e-10,
"nearest point should be origin"
);
let recon = crate::vec3::add4(&q0, r0, &q1, r1, &q2, r2, &q3, r3);
assert!(
crate::vec3::norm(&crate::vec3::sub(&recon, &p)) < 1.0e-10,
"reconstruction mismatch"
);
}
{
let q0 = [3.0f64, 0.0, 0.0];
let q1 = [4.0, 1.0, 0.0];
let q2 = [4.0, 0.0, 1.0];
let q3 = [4.0, 0.0, 0.0];
let (p, r0, r1, r2) = nearest_to_origin(&q0, &q1, &q2, &q3);
let r3 = 1.0 - r0 - r1 - r2;
assert!(
(r0 + r1 + r2 + r3 - 1.0).abs() < 1.0e-10,
"bary coords should sum to 1"
);
assert!(
r0 >= -1.0e-10 && r1 >= -1.0e-10 && r2 >= -1.0e-10 && r3 >= -1.0e-10,
"bary coords non-negative"
);
let d = crate::vec3::norm(&p);
assert!(
d <= crate::vec3::norm(&q0) + 1.0e-10,
"nearest point not closer than q0"
);
assert!(
d <= crate::vec3::norm(&q1) + 1.0e-10,
"nearest point not closer than q1"
);
assert!(
d <= crate::vec3::norm(&q2) + 1.0e-10,
"nearest point not closer than q2"
);
assert!(
d <= crate::vec3::norm(&q3) + 1.0e-10,
"nearest point not closer than q3"
);
let recon = crate::vec3::add4(&q0, r0, &q1, r1, &q2, r2, &q3, r3);
assert!(
crate::vec3::norm(&crate::vec3::sub(&recon, &p)) < 1.0e-10,
"reconstruction mismatch"
);
}
}
pub fn condition_number(p0: &[f64; 3], p1: &[f64; 3], p2: &[f64; 3], p3: &[f64; 3]) -> Option<f64> {
use crate::vec3::{dot, sub};
let e1 = sub(p1, p0);
let e2 = sub(p2, p0);
let e3 = sub(p3, p0);
let g00 = dot(&e1, &e1);
let g11 = dot(&e2, &e2);
let g22 = dot(&e3, &e3);
let g01 = dot(&e1, &e2);
let g12 = dot(&e2, &e3);
let g02 = dot(&e1, &e3);
let eig = crate::mat3_sym::eigen_values_analytic(&[g00, g11, g22, g12, g02, g01])?;
Some((eig[2] / eig[0]).sqrt())
}
pub fn subdivide<INDEX>(corner: &[INDEX; 4], edge: &[INDEX; 6]) -> [[INDEX; 4]; 8]
where
INDEX: num_traits::PrimInt,
{
[
[corner[0], edge[0], edge[1], edge[2]],
[edge[0], corner[1], edge[3], edge[4]],
[edge[1], edge[3], corner[2], edge[5]],
[edge[2], edge[4], edge[5], corner[3]],
[edge[0], edge[1], edge[2], edge[5]],
[edge[0], edge[3], edge[1], edge[5]], [edge[0], edge[4], edge[3], edge[5]], [edge[0], edge[2], edge[4], edge[5]],
]
}