use super::{DropAxis, ImplicitPoint, Lpi, Sign, Tpi};
use num_rational::BigRational;
use num_traits::Signed;
#[inline]
fn r(x: f64) -> BigRational {
BigRational::from_float(x).expect("kernel: non-finite coordinate reached the exact predicate")
}
#[inline]
fn sign_of(x: &BigRational) -> Sign {
if x.is_negative() {
Sign::Negative
} else if x.is_positive() {
Sign::Positive
} else {
Sign::Zero
}
}
type V3 = [BigRational; 3];
#[inline]
fn vec(p: [f64; 3]) -> V3 {
[r(p[0]), r(p[1]), r(p[2])]
}
#[inline]
fn sub3(a: &V3, b: &V3) -> V3 {
[&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2]]
}
fn det3(u: &V3, v: &V3, w: &V3) -> BigRational {
&u[0] * (&v[1] * &w[2] - &v[2] * &w[1])
+ &u[1] * (&v[2] * &w[0] - &v[0] * &w[2])
+ &u[2] * (&v[0] * &w[1] - &v[1] * &w[0])
}
#[inline]
fn cross(u: &V3, v: &V3) -> V3 {
[
&u[1] * &v[2] - &u[2] * &v[1],
&u[2] * &v[0] - &u[0] * &v[2],
&u[0] * &v[1] - &u[1] * &v[0],
]
}
pub fn orient3d_exact(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> Sign {
let (a, b, c, d) = (vec(a), vec(b), vec(c), vec(d));
let ad = sub3(&a, &d);
let bd = sub3(&b, &d);
let cd = sub3(&c, &d);
sign_of(&det3(&ad, &bd, &cd))
}
pub fn orient2d_exact(a: [f64; 3], b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
let (i, j) = match axis {
DropAxis::X => (1, 2),
DropAxis::Y => (0, 2),
DropAxis::Z => (0, 1),
};
let det = (r(a[i]) - r(c[i])) * (r(b[j]) - r(c[j])) - (r(a[j]) - r(c[j])) * (r(b[i]) - r(c[i]));
sign_of(&det)
}
pub fn lpi_lambda(l: &Lpi) -> (V3, BigRational) {
let p = vec(l.p);
let q = vec(l.q);
let rr = vec(l.r);
let s = vec(l.s);
let t = vec(l.t);
let qp = sub3(&q, &p);
let sr = sub3(&s, &rr);
let tr = sub3(&t, &rr);
let pr = sub3(&p, &rr);
let d = det3(&qp, &sr, &tr);
let n = det3(&pr, &sr, &tr);
let lx = &d * &p[0] - &n * &qp[0];
let ly = &d * &p[1] - &n * &qp[1];
let lz = &d * &p[2] - &n * &qp[2];
([lx, ly, lz], d)
}
pub fn lpi_point(l: &Lpi) -> V3 {
let (lambda, d) = lpi_lambda(l);
[&lambda[0] / &d, &lambda[1] / &d, &lambda[2] / &d]
}
fn indirect_orient3d(lambda: &V3, d: &BigRational, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
let p4r = vec(p4);
let row1 = [
&lambda[0] - d * &p4r[0],
&lambda[1] - d * &p4r[1],
&lambda[2] - d * &p4r[2],
];
let row2 = sub3(&vec(p2), &p4r);
let row3 = sub3(&vec(p3), &p4r);
super::assemble_sign(sign_of(&det3(&row1, &row2, &row3)), &[sign_of(d)])
}
pub fn lpi_orient3d(l: &Lpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
let (lambda, d) = lpi_lambda(l);
indirect_orient3d(&lambda, &d, p2, p3, p4)
}
pub fn tpi_lambda(t: &Tpi) -> (V3, BigRational) {
let plane = |pl: &[[f64; 3]; 3]| -> (V3, BigRational) {
let a = vec(pl[0]);
let ba = sub3(&vec(pl[1]), &a);
let ca = sub3(&vec(pl[2]), &a);
let n = cross(&ba, &ca);
let off = &n[0] * &a[0] + &n[1] * &a[1] + &n[2] * &a[2];
(n, off)
};
let (n1, c1) = plane(&t.planes[0]);
let (n2, c2) = plane(&t.planes[1]);
let (n3, c3) = plane(&t.planes[2]);
let d = det3(&n1, &n2, &n3);
let ns = [&n1, &n2, &n3];
let cs = [&c1, &c2, &c3];
let cramer = |k: usize| -> BigRational {
let mut rows: [V3; 3] = [ns[0].clone(), ns[1].clone(), ns[2].clone()];
for (row, ci) in rows.iter_mut().zip(cs.iter()) {
row[k] = (*ci).clone();
}
det3(&rows[0], &rows[1], &rows[2])
};
([cramer(0), cramer(1), cramer(2)], d)
}
pub fn tpi_orient3d(t: &Tpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
let (lambda, d) = tpi_lambda(t);
indirect_orient3d(&lambda, &d, p2, p3, p4)
}
pub fn tpi_point(t: &Tpi) -> V3 {
let (lambda, d) = tpi_lambda(t);
[&lambda[0] / &d, &lambda[1] / &d, &lambda[2] / &d]
}
pub fn orient3d_exact_pt(a: &V3, b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> Sign {
let (b, c, d) = (vec(b), vec(c), vec(d));
let ad = sub3(a, &d);
let bd = sub3(&b, &d);
let cd = sub3(&c, &d);
sign_of(&det3(&ad, &bd, &cd))
}
#[inline]
fn axis_idx(axis: DropAxis) -> (usize, usize) {
match axis {
DropAxis::X => (1, 2),
DropAxis::Y => (0, 2),
DropAxis::Z => (0, 1),
}
}
fn indirect_orient2d(lambda: &V3, d: &BigRational, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
let (i, j) = axis_idx(axis);
let br = vec(b);
let cr = vec(c);
let li = &lambda[i] - d * &cr[i];
let lj = &lambda[j] - d * &cr[j];
let lambda_det2 = &li * (&br[j] - &cr[j]) - &lj * (&br[i] - &cr[i]);
super::assemble_sign(sign_of(&lambda_det2), &[sign_of(d)])
}
pub fn lpi_orient2d(l: &Lpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
let (lambda, d) = lpi_lambda(l);
indirect_orient2d(&lambda, &d, b, c, axis)
}
pub fn tpi_orient2d(t: &Tpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
let (lambda, d) = tpi_lambda(t);
indirect_orient2d(&lambda, &d, b, c, axis)
}
pub fn orient2d_exact_pt(a: &V3, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
let (i, j) = axis_idx(axis);
let (br, cr) = (vec(b), vec(c));
let det = (&a[i] - &cr[i]) * (&br[j] - &cr[j]) - (&a[j] - &cr[j]) * (&br[i] - &cr[i]);
sign_of(&det)
}
pub fn lpi_compare_along(l1: &Lpi, l2: &Lpi, u: [f64; 3]) -> Sign {
let (lam1, d1) = lpi_lambda(l1);
let (lam2, d2) = lpi_lambda(l2);
let ur = vec(u);
let dot1 = &lam1[0] * &ur[0] + &lam1[1] * &ur[1] + &lam1[2] * &ur[2];
let dot2 = &lam2[0] * &ur[0] + &lam2[1] * &ur[1] + &lam2[2] * &ur[2];
let num = &dot1 * &d2 - &dot2 * &d1;
super::assemble_sign(sign_of(&num), &[sign_of(&d1), sign_of(&d2)])
}
pub(crate) fn lambda_of(p: &ImplicitPoint) -> (V3, BigRational) {
match p {
ImplicitPoint::Lpi(l) => lpi_lambda(l),
ImplicitPoint::Tpi(t) => tpi_lambda(t),
ImplicitPoint::Explicit(_) => unreachable!("lambda_of: Explicit point"),
}
}
fn lambda_or_explicit(p: &ImplicitPoint) -> (V3, BigRational) {
match p {
ImplicitPoint::Explicit(e) => (vec(*e), r(1.0)),
_ => lambda_of(p),
}
}
pub fn cmp_along(a: &ImplicitPoint, b: &ImplicitPoint, u: [f64; 3]) -> Sign {
let (la, da) = lambda_or_explicit(a);
let (lb, db) = lambda_or_explicit(b);
let ur = vec(u);
let dot_a = &la[0] * &ur[0] + &la[1] * &ur[1] + &la[2] * &ur[2];
let dot_b = &lb[0] * &ur[0] + &lb[1] * &ur[1] + &lb[2] * &ur[2];
let num = &dot_a * &db - &dot_b * &da;
super::assemble_sign(sign_of(&num), &[sign_of(&da), sign_of(&db)])
}
pub(crate) fn point_of(p: &ImplicitPoint) -> V3 {
match p {
ImplicitPoint::Lpi(l) => lpi_point(l),
ImplicitPoint::Tpi(t) => tpi_point(t),
ImplicitPoint::Explicit(e) => vec(*e),
}
}
pub(crate) fn orient2d_pts(a: &V3, b: &V3, c: &V3, axis: DropAxis) -> Sign {
sign_of(&tri_area2(a, b, c, axis))
}
pub(crate) fn tri_area2(a: &V3, b: &V3, c: &V3, axis: DropAxis) -> BigRational {
let (i, j) = axis_idx(axis);
(&a[i] - &c[i]) * (&b[j] - &c[j]) - (&a[j] - &c[j]) * (&b[i] - &c[i])
}
pub fn orient2d_2i(a: &ImplicitPoint, b: &ImplicitPoint, c: [f64; 3], axis: DropAxis) -> Sign {
let (i, j) = axis_idx(axis);
let (lam1, d1) = lambda_of(a);
let (lam2, d2) = lambda_of(b);
let cr = vec(c);
let a_i = &lam1[i] - &d1 * &cr[i];
let a_j = &lam1[j] - &d1 * &cr[j];
let b_i = &lam2[i] - &d2 * &cr[i];
let b_j = &lam2[j] - &d2 * &cr[j];
let det = &a_i * &b_j - &a_j * &b_i;
super::assemble_sign(sign_of(&det), &[sign_of(&d1), sign_of(&d2)])
}
pub fn orient2d_3i(a: &ImplicitPoint, b: &ImplicitPoint, c: &ImplicitPoint, axis: DropAxis) -> Sign {
let (i, j) = axis_idx(axis);
let (lam1, d1) = lambda_of(a);
let (lam2, d2) = lambda_of(b);
let (lam3, d3) = lambda_of(c);
let u_i = &d1 * &lam2[i] - &d2 * &lam1[i];
let u_j = &d1 * &lam2[j] - &d2 * &lam1[j];
let v_i = &d1 * &lam3[i] - &d3 * &lam1[i];
let v_j = &d1 * &lam3[j] - &d3 * &lam1[j];
let det = &u_i * &v_j - &u_j * &v_i;
super::assemble_sign(sign_of(&det), &[sign_of(&d2), sign_of(&d3)])
}
fn cmp_axis(a: &ImplicitPoint, b: &ImplicitPoint, k: usize) -> Sign {
use ImplicitPoint::Explicit;
match (a, b) {
(Explicit(ae), Explicit(be)) => sign_of(&(r(ae[k]) - r(be[k]))),
(_, Explicit(be)) => {
let (lam, d) = lambda_of(a);
let bk = r(be[k]);
super::assemble_sign(sign_of(&(&lam[k] - &d * &bk)), &[sign_of(&d)])
}
(Explicit(ae), _) => {
let (lam, d) = lambda_of(b);
let ak = r(ae[k]);
super::assemble_sign(sign_of(&(&ak * &d - &lam[k])), &[sign_of(&d)])
}
(_, _) => {
let (la, da) = lambda_of(a);
let (lb, db) = lambda_of(b);
super::assemble_sign(sign_of(&(&la[k] * &db - &lb[k] * &da)), &[sign_of(&da), sign_of(&db)])
}
}
}
pub fn cmp_lex(a: &ImplicitPoint, b: &ImplicitPoint) -> Sign {
for k in 0..3 {
let s = cmp_axis(a, b, k);
if s != Sign::Zero {
return s;
}
}
Sign::Zero
}