1use super::{DropAxis, ImplicitPoint, Lpi, Sign, Tpi};
10use num_rational::BigRational;
11use num_traits::{Signed, Zero};
12
13#[inline]
14fn r(x: f64) -> BigRational {
15 BigRational::from_float(x).expect("kernel: non-finite coordinate reached the exact predicate")
16}
17
18#[inline]
19fn sign_of(x: &BigRational) -> Sign {
20 if x.is_negative() {
21 Sign::Negative
22 } else if x.is_positive() {
23 Sign::Positive
24 } else {
25 Sign::Zero
26 }
27}
28
29type V3 = [BigRational; 3];
30
31#[inline]
32fn vec(p: [f64; 3]) -> V3 {
33 [r(p[0]), r(p[1]), r(p[2])]
34}
35
36fn average(pts: &[[f64; 3]]) -> V3 {
39 let n = BigRational::from_float(pts.len() as f64).unwrap();
40 let mut acc = [r(0.0), r(0.0), r(0.0)];
41 for p in pts {
42 let v = vec(*p);
43 acc = [&acc[0] + &v[0], &acc[1] + &v[1], &acc[2] + &v[2]];
44 }
45 [&acc[0] / &n, &acc[1] / &n, &acc[2] / &n]
46}
47
48#[inline]
49fn sub3(a: &V3, b: &V3) -> V3 {
50 [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2]]
51}
52
53fn det3(u: &V3, v: &V3, w: &V3) -> BigRational {
55 &u[0] * (&v[1] * &w[2] - &v[2] * &w[1])
56 + &u[1] * (&v[2] * &w[0] - &v[0] * &w[2])
57 + &u[2] * (&v[0] * &w[1] - &v[1] * &w[0])
58}
59
60#[inline]
61fn cross(u: &V3, v: &V3) -> V3 {
62 [
63 &u[1] * &v[2] - &u[2] * &v[1],
64 &u[2] * &v[0] - &u[0] * &v[2],
65 &u[0] * &v[1] - &u[1] * &v[0],
66 ]
67}
68
69pub fn orient3d_exact(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> Sign {
72 let (a, b, c, d) = (vec(a), vec(b), vec(c), vec(d));
73 let ad = sub3(&a, &d);
74 let bd = sub3(&b, &d);
75 let cd = sub3(&c, &d);
76 sign_of(&det3(&ad, &bd, &cd))
77}
78
79pub fn orient2d_exact(a: [f64; 3], b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
81 let (i, j) = match axis {
82 DropAxis::X => (1, 2),
83 DropAxis::Y => (0, 2),
84 DropAxis::Z => (0, 1),
85 };
86 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]));
87 sign_of(&det)
88}
89
90pub fn lpi_lambda(l: &Lpi) -> (V3, BigRational) {
97 let p = vec(l.p);
98 let q = vec(l.q);
99 let rr = vec(l.r);
100 let s = vec(l.s);
101 let t = vec(l.t);
102 let qp = sub3(&q, &p);
103 let sr = sub3(&s, &rr);
104 let tr = sub3(&t, &rr);
105 let pr = sub3(&p, &rr);
106 let d = det3(&qp, &sr, &tr);
107 let n = det3(&pr, &sr, &tr);
108 let lx = &d * &p[0] - &n * &qp[0];
109 let ly = &d * &p[1] - &n * &qp[1];
110 let lz = &d * &p[2] - &n * &qp[2];
111 ([lx, ly, lz], d)
112}
113
114pub fn lpi_point(l: &Lpi) -> V3 {
117 let (lambda, d) = lpi_lambda(l);
118 if d.is_zero() {
119 return average(&[l.p, l.q]);
123 }
124 [&lambda[0] / &d, &lambda[1] / &d, &lambda[2] / &d]
125}
126
127fn indirect_orient3d(lambda: &V3, d: &BigRational, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
134 let p4r = vec(p4);
135 let row1 = [
136 &lambda[0] - d * &p4r[0],
137 &lambda[1] - d * &p4r[1],
138 &lambda[2] - d * &p4r[2],
139 ];
140 let row2 = sub3(&vec(p2), &p4r);
141 let row3 = sub3(&vec(p3), &p4r);
142 super::assemble_sign(sign_of(&det3(&row1, &row2, &row3)), &[sign_of(d)])
143}
144
145pub fn lpi_orient3d(l: &Lpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
147 let (lambda, d) = lpi_lambda(l);
148 indirect_orient3d(&lambda, &d, p2, p3, p4)
149}
150
151pub fn tpi_lambda(t: &Tpi) -> (V3, BigRational) {
156 let plane = |pl: &[[f64; 3]; 3]| -> (V3, BigRational) {
157 let a = vec(pl[0]);
158 let ba = sub3(&vec(pl[1]), &a);
159 let ca = sub3(&vec(pl[2]), &a);
160 let n = cross(&ba, &ca);
161 let off = &n[0] * &a[0] + &n[1] * &a[1] + &n[2] * &a[2];
162 (n, off)
163 };
164 let (n1, c1) = plane(&t.planes[0]);
165 let (n2, c2) = plane(&t.planes[1]);
166 let (n3, c3) = plane(&t.planes[2]);
167 let d = det3(&n1, &n2, &n3);
168 let ns = [&n1, &n2, &n3];
169 let cs = [&c1, &c2, &c3];
170 let cramer = |k: usize| -> BigRational {
171 let mut rows: [V3; 3] = [ns[0].clone(), ns[1].clone(), ns[2].clone()];
172 for (row, ci) in rows.iter_mut().zip(cs.iter()) {
173 row[k] = (*ci).clone();
174 }
175 det3(&rows[0], &rows[1], &rows[2])
176 };
177 ([cramer(0), cramer(1), cramer(2)], d)
178}
179
180pub fn tpi_orient3d(t: &Tpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Sign {
182 let (lambda, d) = tpi_lambda(t);
183 indirect_orient3d(&lambda, &d, p2, p3, p4)
184}
185
186pub fn tpi_point(t: &Tpi) -> V3 {
188 let (lambda, d) = tpi_lambda(t);
189 if d.is_zero() {
190 return average(&t.planes[0]);
193 }
194 [&lambda[0] / &d, &lambda[1] / &d, &lambda[2] / &d]
195}
196
197pub fn orient3d_exact_pt(a: &V3, b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> Sign {
201 let (b, c, d) = (vec(b), vec(c), vec(d));
202 let ad = sub3(a, &d);
203 let bd = sub3(&b, &d);
204 let cd = sub3(&c, &d);
205 sign_of(&det3(&ad, &bd, &cd))
206}
207
208#[inline]
209fn axis_idx(axis: DropAxis) -> (usize, usize) {
210 match axis {
211 DropAxis::X => (1, 2),
212 DropAxis::Y => (0, 2),
213 DropAxis::Z => (0, 1),
214 }
215}
216
217fn indirect_orient2d(lambda: &V3, d: &BigRational, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
223 let (i, j) = axis_idx(axis);
224 let br = vec(b);
225 let cr = vec(c);
226 let li = &lambda[i] - d * &cr[i];
227 let lj = &lambda[j] - d * &cr[j];
228 let lambda_det2 = &li * (&br[j] - &cr[j]) - &lj * (&br[i] - &cr[i]);
229 super::assemble_sign(sign_of(&lambda_det2), &[sign_of(d)])
230}
231
232pub fn lpi_orient2d(l: &Lpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
234 let (lambda, d) = lpi_lambda(l);
235 indirect_orient2d(&lambda, &d, b, c, axis)
236}
237
238pub fn tpi_orient2d(t: &Tpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
240 let (lambda, d) = tpi_lambda(t);
241 indirect_orient2d(&lambda, &d, b, c, axis)
242}
243
244pub fn orient2d_exact_pt(a: &V3, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Sign {
246 let (i, j) = axis_idx(axis);
247 let (br, cr) = (vec(b), vec(c));
248 let det = (&a[i] - &cr[i]) * (&br[j] - &cr[j]) - (&a[j] - &cr[j]) * (&br[i] - &cr[i]);
249 sign_of(&det)
250}
251
252pub fn lpi_compare_along(l1: &Lpi, l2: &Lpi, u: [f64; 3]) -> Sign {
260 let (lam1, d1) = lpi_lambda(l1);
261 let (lam2, d2) = lpi_lambda(l2);
262 let ur = vec(u);
263 let dot1 = &lam1[0] * &ur[0] + &lam1[1] * &ur[1] + &lam1[2] * &ur[2];
264 let dot2 = &lam2[0] * &ur[0] + &lam2[1] * &ur[1] + &lam2[2] * &ur[2];
265 let num = &dot1 * &d2 - &dot2 * &d1;
266 super::assemble_sign(sign_of(&num), &[sign_of(&d1), sign_of(&d2)])
267}
268
269pub(crate) fn lambda_of(p: &ImplicitPoint) -> (V3, BigRational) {
271 match p {
272 ImplicitPoint::Lpi(l) => lpi_lambda(l),
273 ImplicitPoint::Tpi(t) => tpi_lambda(t),
274 ImplicitPoint::Explicit(_) => unreachable!("lambda_of: Explicit point"),
275 }
276}
277
278fn lambda_or_explicit(p: &ImplicitPoint) -> (V3, BigRational) {
281 match p {
282 ImplicitPoint::Explicit(e) => (vec(*e), r(1.0)),
283 _ => lambda_of(p),
284 }
285}
286
287pub fn cmp_along(a: &ImplicitPoint, b: &ImplicitPoint, u: [f64; 3]) -> Sign {
291 let (la, da) = lambda_or_explicit(a);
292 let (lb, db) = lambda_or_explicit(b);
293 let ur = vec(u);
294 let dot_a = &la[0] * &ur[0] + &la[1] * &ur[1] + &la[2] * &ur[2];
295 let dot_b = &lb[0] * &ur[0] + &lb[1] * &ur[1] + &lb[2] * &ur[2];
296 let num = &dot_a * &db - &dot_b * &da;
297 super::assemble_sign(sign_of(&num), &[sign_of(&da), sign_of(&db)])
298}
299
300pub(crate) fn point_of(p: &ImplicitPoint) -> V3 {
302 match p {
303 ImplicitPoint::Lpi(l) => lpi_point(l),
304 ImplicitPoint::Tpi(t) => tpi_point(t),
305 ImplicitPoint::Explicit(e) => vec(*e),
306 }
307}
308
309#[cfg_attr(not(test), allow(dead_code))]
312pub(crate) fn orient2d_pts(a: &V3, b: &V3, c: &V3, axis: DropAxis) -> Sign {
313 sign_of(&tri_area2(a, b, c, axis))
314}
315
316#[cfg_attr(not(test), allow(dead_code))]
319pub(crate) fn tri_area2(a: &V3, b: &V3, c: &V3, axis: DropAxis) -> BigRational {
320 let (i, j) = axis_idx(axis);
321 (&a[i] - &c[i]) * (&b[j] - &c[j]) - (&a[j] - &c[j]) * (&b[i] - &c[i])
322}
323
324pub fn orient2d_2i(a: &ImplicitPoint, b: &ImplicitPoint, c: [f64; 3], axis: DropAxis) -> Sign {
329 let (i, j) = axis_idx(axis);
330 let (lam1, d1) = lambda_of(a);
331 let (lam2, d2) = lambda_of(b);
332 let cr = vec(c);
333 let a_i = &lam1[i] - &d1 * &cr[i];
334 let a_j = &lam1[j] - &d1 * &cr[j];
335 let b_i = &lam2[i] - &d2 * &cr[i];
336 let b_j = &lam2[j] - &d2 * &cr[j];
337 let det = &a_i * &b_j - &a_j * &b_i;
338 super::assemble_sign(sign_of(&det), &[sign_of(&d1), sign_of(&d2)])
339}
340
341pub fn orient2d_3i(a: &ImplicitPoint, b: &ImplicitPoint, c: &ImplicitPoint, axis: DropAxis) -> Sign {
345 let (i, j) = axis_idx(axis);
346 let (lam1, d1) = lambda_of(a);
347 let (lam2, d2) = lambda_of(b);
348 let (lam3, d3) = lambda_of(c);
349 let u_i = &d1 * &lam2[i] - &d2 * &lam1[i];
350 let u_j = &d1 * &lam2[j] - &d2 * &lam1[j];
351 let v_i = &d1 * &lam3[i] - &d3 * &lam1[i];
352 let v_j = &d1 * &lam3[j] - &d3 * &lam1[j];
353 let det = &u_i * &v_j - &u_j * &v_i;
354 super::assemble_sign(sign_of(&det), &[sign_of(&d2), sign_of(&d3)])
355}
356
357fn cmp_axis(a: &ImplicitPoint, b: &ImplicitPoint, k: usize) -> Sign {
359 use ImplicitPoint::Explicit;
360 match (a, b) {
361 (Explicit(ae), Explicit(be)) => sign_of(&(r(ae[k]) - r(be[k]))),
362 (_, Explicit(be)) => {
363 let (lam, d) = lambda_of(a);
365 let bk = r(be[k]);
366 super::assemble_sign(sign_of(&(&lam[k] - &d * &bk)), &[sign_of(&d)])
367 }
368 (Explicit(ae), _) => {
369 let (lam, d) = lambda_of(b);
371 let ak = r(ae[k]);
372 super::assemble_sign(sign_of(&(&ak * &d - &lam[k])), &[sign_of(&d)])
373 }
374 (_, _) => {
375 let (la, da) = lambda_of(a);
377 let (lb, db) = lambda_of(b);
378 super::assemble_sign(sign_of(&(&la[k] * &db - &lb[k] * &da)), &[sign_of(&da), sign_of(&db)])
379 }
380 }
381}
382
383pub fn cmp_lex(a: &ImplicitPoint, b: &ImplicitPoint) -> Sign {
388 for k in 0..3 {
389 let s = cmp_axis(a, b, k);
390 if s != Sign::Zero {
391 return s;
392 }
393 }
394 Sign::Zero
395}
396
397#[cfg(test)]
398#[path = "rational_tests.rs"]
399mod rational_tests;