use super::backend::{
denom, int_from_uint, mul_int_uint, mul_uint, numer, rat_is_zero, rat_new, Int, Rational,
Signed,
};
use super::rational::{R2, R3};
use super::Sign;
#[inline]
fn homog2(p: &R2) -> (Int, Int, Int) {
let (xn, xd) = (numer(&p.x), denom(&p.x));
let (yn, yd) = (numer(&p.y), denom(&p.y));
(
mul_int_uint(xn, yd),
mul_int_uint(yn, xd),
int_from_uint(mul_uint(xd, yd)),
)
}
#[inline]
fn homog3(p: &R3) -> (Int, Int, Int, Int) {
let (xn, xd) = (numer(&p.x), denom(&p.x));
let (yn, yd) = (numer(&p.y), denom(&p.y));
let (zn, zd) = (numer(&p.z), denom(&p.z));
let yz = mul_uint(yd, zd);
(
mul_int_uint(xn, &yz),
mul_int_uint(yn, &mul_uint(xd, zd)),
mul_int_uint(zn, &mul_uint(xd, yd)),
int_from_uint(mul_uint(xd, &yz)),
)
}
#[derive(Clone, Debug)]
pub struct Homog2(pub Int, pub Int, pub Int);
pub fn homog2_of(p: &R2) -> Homog2 {
let (x, y, w) = homog2(p);
Homog2(x, y, w)
}
pub fn orient2d_h(a: &Homog2, b: &Homog2, c: &Homog2) -> Sign {
let ux = &b.0 * &a.2 - &a.0 * &b.2;
let uy = &b.1 * &a.2 - &a.1 * &b.2;
let vx = &c.0 * &a.2 - &a.0 * &c.2;
let vy = &c.1 * &a.2 - &a.1 * &c.2;
sign_of_int(&(ux * vy - uy * vx))
}
pub fn incircle_h(a: &Homog2, b: &Homog2, c: &Homog2, d: &Homog2) -> Sign {
let row = |p: &Homog2| -> (Int, Int, Int) {
let nx = &p.0 * &d.2 - &d.0 * &p.2;
let ny = &p.1 * &d.2 - &d.1 * &p.2;
let s = &p.2 * &d.2;
let lift = &nx * &nx + &ny * &ny;
(nx * &s, ny * &s, lift)
};
let (ux, uy, ul) = row(a);
let (vx, vy, vl) = row(b);
let (wx, wy, wl) = row(c);
let det = ul * (&vx * &wy - &vy * &wx)
+ vl * (&wx * &uy - &wy * &ux)
+ wl * (&ux * &vy - &uy * &vx);
sign_of_int(&det)
}
pub fn point_in_tri_2d_h(p: &Homog2, a: &Homog2, b: &Homog2, c: &Homog2) -> TriLoc {
let orient = orient2d_h(a, b, c);
if orient == Sign::Zero {
return TriLoc::Outside;
}
let normalize = |s: Sign| if orient == Sign::Pos { s } else { s.flip() };
let s0 = normalize(orient2d_h(a, b, p));
let s1 = normalize(orient2d_h(b, c, p));
let s2 = normalize(orient2d_h(c, a, p));
if s0 == Sign::Neg || s1 == Sign::Neg || s2 == Sign::Neg {
return TriLoc::Outside;
}
match (s0 == Sign::Zero, s1 == Sign::Zero, s2 == Sign::Zero) {
(false, false, false) => TriLoc::Inside,
(true, false, false) => TriLoc::OnEdge(0),
(false, true, false) => TriLoc::OnEdge(1),
(false, false, true) => TriLoc::OnEdge(2),
(true, false, true) => TriLoc::OnVertex(0),
(true, true, false) => TriLoc::OnVertex(1),
(false, true, true) => TriLoc::OnVertex(2),
(true, true, true) => TriLoc::Outside,
}
}
#[inline]
fn sign_of_int(v: &Int) -> Sign {
if v.is_positive() {
Sign::Pos
} else if v.is_negative() {
Sign::Neg
} else {
Sign::Zero
}
}
pub fn orient2d_r(a: &R2, b: &R2, c: &R2) -> Sign {
let (ax, ay, aw) = homog2(a);
let (bx, by, bw) = homog2(b);
let (cx, cy, cw) = homog2(c);
let ux = &bx * &aw - &ax * &bw;
let uy = &by * &aw - &ay * &bw;
let vx = &cx * &aw - &ax * &cw;
let vy = &cy * &aw - &ay * &cw;
sign_of_int(&(ux * vy - uy * vx))
}
pub fn orient3d_r(a: &R3, b: &R3, c: &R3, d: &R3) -> Sign {
let (ax, ay, az, aw) = homog3(a);
let (bx, by, bz, bw) = homog3(b);
let (cx, cy, cz, cw) = homog3(c);
let (dx, dy, dz, dw) = homog3(d);
let ux = &bx * &aw - &ax * &bw;
let uy = &by * &aw - &ay * &bw;
let uz = &bz * &aw - &az * &bw;
let vx = &cx * &aw - &ax * &cw;
let vy = &cy * &aw - &ay * &cw;
let vz = &cz * &aw - &az * &cw;
let wx = &dx * &aw - &ax * &dw;
let wy = &dy * &aw - &ay * &dw;
let wz = &dz * &aw - &az * &dw;
let det = (&uy * &vz - &uz * &vy) * wx
+ (&uz * &vx - &ux * &vz) * wy
+ (&ux * &vy - &uy * &vx) * wz;
sign_of_int(&det)
}
pub fn incircle_r(a: &R2, b: &R2, c: &R2, d: &R2) -> Sign {
let (dx, dy, dw) = homog2(d);
let row = |p: &R2| -> (Int, Int, Int) {
let (px, py, pw) = homog2(p);
let nx = &px * &dw - &dx * &pw;
let ny = &py * &dw - &dy * &pw;
let s = pw * &dw;
let lift = &nx * &nx + &ny * &ny;
(nx * &s, ny * &s, lift)
};
let (ux, uy, ul) = row(a);
let (vx, vy, vl) = row(b);
let (wx, wy, wl) = row(c);
let det = ul * (&vx * &wy - &vy * &wx)
+ vl * (&wx * &uy - &wy * &ux)
+ wl * (&ux * &vy - &uy * &vx);
sign_of_int(&det)
}
pub fn point_on_segment_r(p: &R3, a: &R3, b: &R3) -> bool {
let (px, py, pz, pw) = homog3(p);
let (ax, ay, az, aw) = homog3(a);
let (bx, by, bz, bw) = homog3(b);
let apx = &px * &aw - &ax * &pw;
let apy = &py * &aw - &ay * &pw;
let apz = &pz * &aw - &az * &pw;
let dx = &bx * &aw - &ax * &bw;
let dy = &by * &aw - &ay * &bw;
let dz = &bz * &aw - &az * &bw;
if !(&apy * &dz - &apz * &dy).is_zero()
|| !(&apz * &dx - &apx * &dz).is_zero()
|| !(&apx * &dy - &apy * &dx).is_zero()
{
return false;
}
let s1 = &apx * &dx + &apy * &dy + &apz * &dz;
if s1.is_negative() {
return false;
}
let s2 = &dx * &dx + &dy * &dy + &dz * &dz;
s1 * (&aw * &bw) <= s2 * (pw * aw)
}
pub fn dot_diff_raw(a: &R3, o: &R3, u: &R3) -> (Int, Int) {
let (ax, ay, az, aw) = homog3(a);
let (ox, oy, oz, ow) = homog3(o);
let (ux, uy, uz, uw) = homog3(u);
let num = (&ax * &ow - &ox * &aw) * ux
+ (&ay * &ow - &oy * &aw) * uy
+ (&az * &ow - &oz * &aw) * uz;
(num, aw * ow * uw)
}
pub fn tri_normal_int(a: &R3, b: &R3, c: &R3) -> [Int; 3] {
let (ax, ay, az, aw) = homog3(a);
let (bx, by, bz, bw) = homog3(b);
let (cx, cy, cz, cw) = homog3(c);
let ux = &bx * &aw - &ax * &bw;
let uy = &by * &aw - &ay * &bw;
let uz = &bz * &aw - &az * &bw;
let vx = &cx * &aw - &ax * &cw;
let vy = &cy * &aw - &ay * &cw;
let vz = &cz * &aw - &az * &cw;
[
&uy * &vz - &uz * &vy,
&uz * &vx - &ux * &vz,
&ux * &vy - &uy * &vx,
]
}
pub fn dot_point_raw(d: &[Int; 3], p: &R3) -> (Int, Int) {
let (px, py, pz, pw) = homog3(p);
(&d[0] * &px + &d[1] * &py + &d[2] * &pz, pw)
}
pub fn tri_normal_r(a: &R3, b: &R3, c: &R3) -> R3 {
b.sub(a).cross(&c.sub(a))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TriLoc {
Inside,
OnEdge(u8),
OnVertex(u8),
Outside,
}
pub fn point_in_tri_2d(p: &R2, a: &R2, b: &R2, c: &R2) -> TriLoc {
let orient = orient2d_r(a, b, c);
if orient == Sign::Zero {
return TriLoc::Outside;
}
let normalize = |s: Sign| if orient == Sign::Pos { s } else { s.flip() };
let s0 = normalize(orient2d_r(a, b, p)); let s1 = normalize(orient2d_r(b, c, p)); let s2 = normalize(orient2d_r(c, a, p)); if s0 == Sign::Neg || s1 == Sign::Neg || s2 == Sign::Neg {
return TriLoc::Outside;
}
match (s0 == Sign::Zero, s1 == Sign::Zero, s2 == Sign::Zero) {
(false, false, false) => TriLoc::Inside,
(true, false, false) => TriLoc::OnEdge(0),
(false, true, false) => TriLoc::OnEdge(1),
(false, false, true) => TriLoc::OnEdge(2),
(true, false, true) => TriLoc::OnVertex(0), (true, true, false) => TriLoc::OnVertex(1), (false, true, true) => TriLoc::OnVertex(2), (true, true, true) => TriLoc::Outside, }
}
pub fn line_plane_intersect(p: &R3, q: &R3, a: &R3, b: &R3, c: &R3) -> Option<R3> {
let (px, py, pz, pw) = homog3(p);
let (qx, qy, qz, qw) = homog3(q);
let (ax, ay, az, aw) = homog3(a);
let (bx, by, bz, bw) = homog3(b);
let (cx, cy, cz, cw) = homog3(c);
let ux = &bx * &aw - &ax * &bw;
let uy = &by * &aw - &ay * &bw;
let uz = &bz * &aw - &az * &bw;
let vx = &cx * &aw - &ax * &cw;
let vy = &cy * &aw - &ay * &cw;
let vz = &cz * &aw - &az * &cw;
let nx = &uy * &vz - &uz * &vy;
let ny = &uz * &vx - &ux * &vz;
let nz = &ux * &vy - &uy * &vx;
let dx = &qx * &pw - &px * &qw;
let dy = &qy * &pw - &py * &qw;
let dz = &qz * &pw - &pz * &qw;
let n_dot_d = &nx * &dx + &ny * &dy + &nz * &dz;
if n_dot_d.is_zero() {
return None;
}
let ex = &ax * &pw - &px * &aw;
let ey = &ay * &pw - &py * &aw;
let ez = &az * &pw - &pz * &aw;
let n_dot_e = &nx * &ex + &ny * &ey + &nz * &ez;
let t_n = &n_dot_e * &qw;
let t_d = &aw * &n_dot_d;
let den = &pw * &qw * &t_d;
let coord = |pi: &Int, di: &Int| -> Rational {
rat_new(pi * &qw * &t_d + di * &t_n, den.clone())
};
Some(R3::new(coord(&px, &dx), coord(&py, &dy), coord(&pz, &dz)))
}
pub fn line_line_intersect_2d(a: &R2, b: &R2, c: &R2, d: &R2) -> Option<R2> {
let (ax, ay, aw) = homog2(a);
let (bx, by, bw) = homog2(b);
let (cx, cy, cw) = homog2(c);
let (dx, dy, dw) = homog2(d);
let abx = &bx * &aw - &ax * &bw;
let aby = &by * &aw - &ay * &bw;
let cdx = &dx * &cw - &cx * &dw;
let cdy = &dy * &cw - &cy * &dw;
let dn = &abx * &cdy - &aby * &cdx;
if dn.is_zero() {
return None;
}
let cax = &cx * &aw - &ax * &cw;
let cay = &cy * &aw - &ay * &cw;
let n = &cax * &cdy - &cay * &cdx;
let den = &aw * &cw * &dn;
let dn_cw = &dn * &cw;
let x = rat_new(&ax * &dn_cw + &n * &abx, den.clone());
let y = rat_new(&ay * &dn_cw + &n * &aby, den);
Some(R2::new(x, y))
}
pub fn lift_to_plane(p: &R2, axis: usize, a: &R3, n: &R3) -> R3 {
let (nx, ny, nz, nw) = homog3(n);
let (ax, ay, az, aw) = homog3(a);
let (px, py, pw) = homog2(p);
let s = &nx * &ax + &ny * &ay + &nz * &az;
let rebuild = |ni: &Int, nj: &Int, nk: &Int| -> Rational {
rat_new(
&s * &pw - &aw * (ni * &px + nj * &py),
&aw * &pw * nk,
)
};
let _ = nw; match axis {
0 => {
let x = rebuild(&ny, &nz, &nx);
R3::new(x, p.x.clone(), p.y.clone())
}
1 => {
let y = rebuild(&nz, &nx, &ny);
R3::new(p.y.clone(), y, p.x.clone())
}
2 => {
let z = rebuild(&nx, &ny, &nz);
R3::new(p.x.clone(), p.y.clone(), z)
}
_ => unreachable!("axis must be 0, 1, or 2"),
}
}
pub fn segment_param(p: &R3, q: &R3, x: &R3) -> Rational {
let d = q.sub(p);
let (num, den) = if !rat_is_zero(&d.x) {
(&x.x - &p.x, d.x)
} else if !rat_is_zero(&d.y) {
(&x.y - &p.y, d.y)
} else {
(&x.z - &p.z, d.z)
};
debug_assert!(!rat_is_zero(&den), "segment_param requires p != q");
num / den
}