1use num_bigint::BigInt;
15use num_rational::BigRational;
16use num_traits::{Signed, Zero};
17
18use super::rational::{R2, R3};
19use super::Sign;
20
21#[inline]
33fn homog2(p: &R2) -> (BigInt, BigInt, BigInt) {
34 let (xn, xd) = (p.x.numer(), p.x.denom());
35 let (yn, yd) = (p.y.numer(), p.y.denom());
36 (xn * yd, yn * xd, xd * yd)
37}
38
39#[inline]
41fn homog3(p: &R3) -> (BigInt, BigInt, BigInt, BigInt) {
42 let (xn, xd) = (p.x.numer(), p.x.denom());
43 let (yn, yd) = (p.y.numer(), p.y.denom());
44 let (zn, zd) = (p.z.numer(), p.z.denom());
45 let yz = yd * zd;
46 (xn * &yz, yn * (xd * zd), zn * (xd * yd), xd * yz)
47}
48
49#[derive(Clone, Debug)]
53pub struct Homog2(pub BigInt, pub BigInt, pub BigInt);
54
55pub fn homog2_of(p: &R2) -> Homog2 {
56 let (x, y, w) = homog2(p);
57 Homog2(x, y, w)
58}
59
60pub fn orient2d_h(a: &Homog2, b: &Homog2, c: &Homog2) -> Sign {
63 let ux = &b.0 * &a.2 - &a.0 * &b.2;
64 let uy = &b.1 * &a.2 - &a.1 * &b.2;
65 let vx = &c.0 * &a.2 - &a.0 * &c.2;
66 let vy = &c.1 * &a.2 - &a.1 * &c.2;
67 sign_of_int(&(ux * vy - uy * vx))
68}
69
70pub fn incircle_h(a: &Homog2, b: &Homog2, c: &Homog2, d: &Homog2) -> Sign {
73 let row = |p: &Homog2| -> (BigInt, BigInt, BigInt) {
74 let nx = &p.0 * &d.2 - &d.0 * &p.2;
75 let ny = &p.1 * &d.2 - &d.1 * &p.2;
76 let s = &p.2 * &d.2;
77 let lift = &nx * &nx + &ny * &ny;
78 (nx * &s, ny * &s, lift)
79 };
80 let (ux, uy, ul) = row(a);
81 let (vx, vy, vl) = row(b);
82 let (wx, wy, wl) = row(c);
83 let det = ul * (&vx * &wy - &vy * &wx)
84 + vl * (&wx * &uy - &wy * &ux)
85 + wl * (&ux * &vy - &uy * &vx);
86 sign_of_int(&det)
87}
88
89pub fn point_in_tri_2d_h(p: &Homog2, a: &Homog2, b: &Homog2, c: &Homog2) -> TriLoc {
91 let orient = orient2d_h(a, b, c);
92 if orient == Sign::Zero {
93 return TriLoc::Outside;
94 }
95 let normalize = |s: Sign| if orient == Sign::Pos { s } else { s.flip() };
96 let s0 = normalize(orient2d_h(a, b, p));
97 let s1 = normalize(orient2d_h(b, c, p));
98 let s2 = normalize(orient2d_h(c, a, p));
99 if s0 == Sign::Neg || s1 == Sign::Neg || s2 == Sign::Neg {
100 return TriLoc::Outside;
101 }
102 match (s0 == Sign::Zero, s1 == Sign::Zero, s2 == Sign::Zero) {
103 (false, false, false) => TriLoc::Inside,
104 (true, false, false) => TriLoc::OnEdge(0),
105 (false, true, false) => TriLoc::OnEdge(1),
106 (false, false, true) => TriLoc::OnEdge(2),
107 (true, false, true) => TriLoc::OnVertex(0),
108 (true, true, false) => TriLoc::OnVertex(1),
109 (false, true, true) => TriLoc::OnVertex(2),
110 (true, true, true) => TriLoc::Outside,
111 }
112}
113
114#[inline]
115fn sign_of_int(v: &BigInt) -> Sign {
116 if v.is_positive() {
117 Sign::Pos
118 } else if v.is_negative() {
119 Sign::Neg
120 } else {
121 Sign::Zero
122 }
123}
124
125pub fn orient2d_r(a: &R2, b: &R2, c: &R2) -> Sign {
127 let (ax, ay, aw) = homog2(a);
128 let (bx, by, bw) = homog2(b);
129 let (cx, cy, cw) = homog2(c);
130 let ux = &bx * &aw - &ax * &bw;
132 let uy = &by * &aw - &ay * &bw;
133 let vx = &cx * &aw - &ax * &cw;
134 let vy = &cy * &aw - &ay * &cw;
135 sign_of_int(&(ux * vy - uy * vx))
136}
137
138pub fn orient3d_r(a: &R3, b: &R3, c: &R3, d: &R3) -> Sign {
141 let (ax, ay, az, aw) = homog3(a);
142 let (bx, by, bz, bw) = homog3(b);
143 let (cx, cy, cz, cw) = homog3(c);
144 let (dx, dy, dz, dw) = homog3(d);
145 let ux = &bx * &aw - &ax * &bw;
148 let uy = &by * &aw - &ay * &bw;
149 let uz = &bz * &aw - &az * &bw;
150 let vx = &cx * &aw - &ax * &cw;
151 let vy = &cy * &aw - &ay * &cw;
152 let vz = &cz * &aw - &az * &cw;
153 let wx = &dx * &aw - &ax * &dw;
154 let wy = &dy * &aw - &ay * &dw;
155 let wz = &dz * &aw - &az * &dw;
156 let det = (&uy * &vz - &uz * &vy) * wx
157 + (&uz * &vx - &ux * &vz) * wy
158 + (&ux * &vy - &uy * &vx) * wz;
159 sign_of_int(&det)
160}
161
162pub fn incircle_r(a: &R2, b: &R2, c: &R2, d: &R2) -> Sign {
166 let (dx, dy, dw) = homog2(d);
167 let row = |p: &R2| -> (BigInt, BigInt, BigInt) {
172 let (px, py, pw) = homog2(p);
173 let nx = &px * &dw - &dx * &pw;
174 let ny = &py * &dw - &dy * &pw;
175 let s = pw * &dw;
176 let lift = &nx * &nx + &ny * &ny;
177 (nx * &s, ny * &s, lift)
178 };
179 let (ux, uy, ul) = row(a);
180 let (vx, vy, vl) = row(b);
181 let (wx, wy, wl) = row(c);
182 let det = ul * (&vx * &wy - &vy * &wx)
183 + vl * (&wx * &uy - &wy * &ux)
184 + wl * (&ux * &vy - &uy * &vx);
185 sign_of_int(&det)
186}
187
188pub fn point_on_segment_r(p: &R3, a: &R3, b: &R3) -> bool {
192 let (px, py, pz, pw) = homog3(p);
193 let (ax, ay, az, aw) = homog3(a);
194 let (bx, by, bz, bw) = homog3(b);
195 let apx = &px * &aw - &ax * &pw;
197 let apy = &py * &aw - &ay * &pw;
198 let apz = &pz * &aw - &az * &pw;
199 let dx = &bx * &aw - &ax * &bw;
200 let dy = &by * &aw - &ay * &bw;
201 let dz = &bz * &aw - &az * &bw;
202 if !(&apy * &dz - &apz * &dy).is_zero()
204 || !(&apz * &dx - &apx * &dz).is_zero()
205 || !(&apx * &dy - &apy * &dx).is_zero()
206 {
207 return false;
208 }
209 let s1 = &apx * &dx + &apy * &dy + &apz * &dz;
213 if s1.is_negative() {
214 return false;
215 }
216 let s2 = &dx * &dx + &dy * &dy + &dz * &dz;
217 s1 * (&aw * &bw) <= s2 * (pw * aw)
218}
219
220pub fn dot_diff_raw(a: &R3, o: &R3, u: &R3) -> (BigInt, BigInt) {
226 let (ax, ay, az, aw) = homog3(a);
227 let (ox, oy, oz, ow) = homog3(o);
228 let (ux, uy, uz, uw) = homog3(u);
229 let num = (&ax * &ow - &ox * &aw) * ux
231 + (&ay * &ow - &oy * &aw) * uy
232 + (&az * &ow - &oz * &aw) * uz;
233 (num, aw * ow * uw)
234}
235
236pub fn tri_normal_int(a: &R3, b: &R3, c: &R3) -> [BigInt; 3] {
240 let (ax, ay, az, aw) = homog3(a);
241 let (bx, by, bz, bw) = homog3(b);
242 let (cx, cy, cz, cw) = homog3(c);
243 let ux = &bx * &aw - &ax * &bw;
244 let uy = &by * &aw - &ay * &bw;
245 let uz = &bz * &aw - &az * &bw;
246 let vx = &cx * &aw - &ax * &cw;
247 let vy = &cy * &aw - &ay * &cw;
248 let vz = &cz * &aw - &az * &cw;
249 [
250 &uy * &vz - &uz * &vy,
251 &uz * &vx - &ux * &vz,
252 &ux * &vy - &uy * &vx,
253 ]
254}
255
256pub fn dot_point_raw(d: &[BigInt; 3], p: &R3) -> (BigInt, BigInt) {
261 let (px, py, pz, pw) = homog3(p);
262 (&d[0] * &px + &d[1] * &py + &d[2] * &pz, pw)
263}
264
265pub fn tri_normal_r(a: &R3, b: &R3, c: &R3) -> R3 {
268 b.sub(a).cross(&c.sub(a))
269}
270
271#[derive(Clone, Copy, Debug, PartialEq, Eq)]
273pub enum TriLoc {
274 Inside,
275 OnEdge(u8),
277 OnVertex(u8),
279 Outside,
280}
281
282pub fn point_in_tri_2d(p: &R2, a: &R2, b: &R2, c: &R2) -> TriLoc {
286 let orient = orient2d_r(a, b, c);
287 if orient == Sign::Zero {
288 return TriLoc::Outside;
289 }
290 let normalize = |s: Sign| if orient == Sign::Pos { s } else { s.flip() };
292 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 {
296 return TriLoc::Outside;
297 }
298 match (s0 == Sign::Zero, s1 == Sign::Zero, s2 == Sign::Zero) {
299 (false, false, false) => TriLoc::Inside,
300 (true, false, false) => TriLoc::OnEdge(0),
301 (false, true, false) => TriLoc::OnEdge(1),
302 (false, false, true) => TriLoc::OnEdge(2),
303 (true, false, true) => TriLoc::OnVertex(0), (true, true, false) => TriLoc::OnVertex(1), (false, true, true) => TriLoc::OnVertex(2), (true, true, true) => TriLoc::Outside, }
308}
309
310pub fn line_plane_intersect(p: &R3, q: &R3, a: &R3, b: &R3, c: &R3) -> Option<R3> {
319 let (px, py, pz, pw) = homog3(p);
325 let (qx, qy, qz, qw) = homog3(q);
326 let (ax, ay, az, aw) = homog3(a);
327 let (bx, by, bz, bw) = homog3(b);
328 let (cx, cy, cz, cw) = homog3(c);
329
330 let ux = &bx * &aw - &ax * &bw;
332 let uy = &by * &aw - &ay * &bw;
333 let uz = &bz * &aw - &az * &bw;
334 let vx = &cx * &aw - &ax * &cw;
335 let vy = &cy * &aw - &ay * &cw;
336 let vz = &cz * &aw - &az * &cw;
337 let nx = &uy * &vz - &uz * &vy;
338 let ny = &uz * &vx - &ux * &vz;
339 let nz = &ux * &vy - &uy * &vx;
340
341 let dx = &qx * &pw - &px * &qw;
343 let dy = &qy * &pw - &py * &qw;
344 let dz = &qz * &pw - &pz * &qw;
345 let n_dot_d = &nx * &dx + &ny * &dy + &nz * &dz;
346 if n_dot_d.is_zero() {
347 return None;
348 }
349 let ex = &ax * &pw - &px * &aw;
350 let ey = &ay * &pw - &py * &aw;
351 let ez = &az * &pw - &pz * &aw;
352 let n_dot_e = &nx * &ex + &ny * &ey + &nz * &ez;
353
354 let t_n = &n_dot_e * &qw;
356 let t_d = &aw * &n_dot_d;
357 let den = &pw * &qw * &t_d;
358 let coord = |pi: &BigInt, di: &BigInt| -> BigRational {
359 BigRational::new(pi * &qw * &t_d + di * &t_n, den.clone())
360 };
361 Some(R3::new(coord(&px, &dx), coord(&py, &dy), coord(&pz, &dz)))
362}
363
364pub fn line_line_intersect_2d(a: &R2, b: &R2, c: &R2, d: &R2) -> Option<R2> {
368 let (ax, ay, aw) = homog2(a);
374 let (bx, by, bw) = homog2(b);
375 let (cx, cy, cw) = homog2(c);
376 let (dx, dy, dw) = homog2(d);
377
378 let abx = &bx * &aw - &ax * &bw;
379 let aby = &by * &aw - &ay * &bw;
380 let cdx = &dx * &cw - &cx * &dw;
381 let cdy = &dy * &cw - &cy * &dw;
382 let dn = &abx * &cdy - &aby * &cdx;
383 if dn.is_zero() {
384 return None;
385 }
386 let cax = &cx * &aw - &ax * &cw;
387 let cay = &cy * &aw - &ay * &cw;
388 let n = &cax * &cdy - &cay * &cdx;
389
390 let den = &aw * &cw * &dn;
392 let dn_cw = &dn * &cw;
393 let x = BigRational::new(&ax * &dn_cw + &n * &abx, den.clone());
394 let y = BigRational::new(&ay * &dn_cw + &n * &aby, den);
395 Some(R2::new(x, y))
396}
397
398pub fn lift_to_plane(p: &R2, axis: usize, a: &R3, n: &R3) -> R3 {
404 let (nx, ny, nz, nw) = homog3(n);
405 let (ax, ay, az, aw) = homog3(a);
406 let (px, py, pw) = homog2(p);
407 let s = &nx * &ax + &ny * &ay + &nz * &az;
410 let rebuild = |ni: &BigInt, nj: &BigInt, nk: &BigInt| -> BigRational {
411 BigRational::new(
412 &s * &pw - &aw * (ni * &px + nj * &py),
413 &aw * &pw * nk,
414 )
415 };
416 let _ = nw; match axis {
418 0 => {
419 let x = rebuild(&ny, &nz, &nx);
420 R3::new(x, p.x.clone(), p.y.clone())
421 }
422 1 => {
423 let y = rebuild(&nz, &nx, &ny);
424 R3::new(p.y.clone(), y, p.x.clone())
425 }
426 2 => {
427 let z = rebuild(&nx, &ny, &nz);
428 R3::new(p.x.clone(), p.y.clone(), z)
429 }
430 _ => unreachable!("axis must be 0, 1, or 2"),
431 }
432}
433
434pub fn segment_param(p: &R3, q: &R3, x: &R3) -> BigRational {
438 let d = q.sub(p);
439 let (num, den) = if !d.x.is_zero() {
440 (&x.x - &p.x, d.x)
441 } else if !d.y.is_zero() {
442 (&x.y - &p.y, d.y)
443 } else {
444 (&x.z - &p.z, d.z)
445 };
446 debug_assert!(!den.is_zero(), "segment_param requires p != q");
447 num / den
448}