1use super::{DropAxis, ImplicitPoint, Lpi, Sign, Tpi};
19
20#[derive(Clone, Copy, Debug)]
21pub struct RnInterval {
22 pub lo: f64,
23 pub hi: f64,
24}
25
26#[inline]
28pub fn next_up(x: f64) -> f64 {
29 if x.is_nan() || x == f64::INFINITY {
30 return x;
31 }
32 if x == 0.0 {
33 return f64::from_bits(1); }
35 let b = x.to_bits();
36 f64::from_bits(if x > 0.0 { b + 1 } else { b - 1 })
37}
38
39#[inline]
41pub fn next_down(x: f64) -> f64 {
42 if x.is_nan() || x == f64::NEG_INFINITY {
43 return x;
44 }
45 if x == 0.0 {
46 return -f64::from_bits(1); }
48 let b = x.to_bits();
49 f64::from_bits(if x > 0.0 { b - 1 } else { b + 1 })
50}
51
52impl RnInterval {
53 #[inline]
54 pub fn point(x: f64) -> Self {
55 Self { lo: x, hi: x }
56 }
57 #[inline]
58 pub fn add(self, o: Self) -> Self {
59 Self { lo: next_down(self.lo + o.lo), hi: next_up(self.hi + o.hi) }
60 }
61 #[inline]
62 pub fn sub(self, o: Self) -> Self {
63 Self { lo: next_down(self.lo - o.hi), hi: next_up(self.hi - o.lo) }
64 }
65 #[inline]
66 pub fn mul(self, o: Self) -> Self {
67 let c = [self.lo * o.lo, self.lo * o.hi, self.hi * o.lo, self.hi * o.hi];
68 let mut mn = c[0];
69 let mut mx = c[0];
70 for &v in &c[1..] {
71 if v < mn {
72 mn = v;
73 }
74 if v > mx {
75 mx = v;
76 }
77 }
78 Self { lo: next_down(mn), hi: next_up(mx) }
79 }
80 #[inline]
82 pub fn sign(self) -> Option<Sign> {
83 if self.lo > 0.0 {
84 Some(Sign::Positive)
85 } else if self.hi < 0.0 {
86 Some(Sign::Negative)
87 } else if self.lo == 0.0 && self.hi == 0.0 {
88 Some(Sign::Zero)
89 } else {
90 None
91 }
92 }
93}
94
95type Iv3 = [RnInterval; 3];
96
97#[inline]
98fn ivec(p: [f64; 3]) -> Iv3 {
99 [RnInterval::point(p[0]), RnInterval::point(p[1]), RnInterval::point(p[2])]
100}
101#[inline]
102fn isub(a: &Iv3, b: &Iv3) -> Iv3 {
103 [a[0].sub(b[0]), a[1].sub(b[1]), a[2].sub(b[2])]
104}
105fn idet3(u: &Iv3, v: &Iv3, w: &Iv3) -> RnInterval {
106 u[0]
107 .mul(v[1].mul(w[2]).sub(v[2].mul(w[1])))
108 .add(u[1].mul(v[2].mul(w[0]).sub(v[0].mul(w[2]))))
109 .add(u[2].mul(v[0].mul(w[1]).sub(v[1].mul(w[0]))))
110}
111
112#[inline]
113fn icross(u: &Iv3, v: &Iv3) -> Iv3 {
114 [
115 u[1].mul(v[2]).sub(u[2].mul(v[1])),
116 u[2].mul(v[0]).sub(u[0].mul(v[2])),
117 u[0].mul(v[1]).sub(u[1].mul(v[0])),
118 ]
119}
120
121fn lpi_lambda(l: &Lpi) -> (Iv3, RnInterval) {
122 let p = ivec(l.p);
123 let q = ivec(l.q);
124 let rr = ivec(l.r);
125 let s = ivec(l.s);
126 let t = ivec(l.t);
127 let qp = isub(&q, &p);
128 let sr = isub(&s, &rr);
129 let tr = isub(&t, &rr);
130 let pr = isub(&p, &rr);
131 let d = idet3(&qp, &sr, &tr);
132 let n = idet3(&pr, &sr, &tr);
133 let lx = d.mul(p[0]).sub(n.mul(qp[0]));
135 let ly = d.mul(p[1]).sub(n.mul(qp[1]));
136 let lz = d.mul(p[2]).sub(n.mul(qp[2]));
137 ([lx, ly, lz], d)
138}
139
140fn tpi_lambda(t: &Tpi) -> (Iv3, RnInterval) {
141 let plane = |pl: &[[f64; 3]; 3]| -> (Iv3, RnInterval) {
142 let a = ivec(pl[0]);
143 let ba = isub(&ivec(pl[1]), &a);
144 let ca = isub(&ivec(pl[2]), &a);
145 let n = icross(&ba, &ca);
146 let off = n[0].mul(a[0]).add(n[1].mul(a[1])).add(n[2].mul(a[2]));
147 (n, off)
148 };
149 let (n1, c1) = plane(&t.planes[0]);
150 let (n2, c2) = plane(&t.planes[1]);
151 let (n3, c3) = plane(&t.planes[2]);
152 let d = idet3(&n1, &n2, &n3);
153 let ns = [n1, n2, n3];
154 let cs = [c1, c2, c3];
155 let cramer = |k: usize| -> RnInterval {
156 let mut rows = [ns[0], ns[1], ns[2]];
157 for (row, ci) in rows.iter_mut().zip(cs.iter()) {
158 row[k] = *ci;
159 }
160 idet3(&rows[0], &rows[1], &rows[2])
161 };
162 ([cramer(0), cramer(1), cramer(2)], d)
163}
164
165fn indirect_orient3d(
168 lambda: &Iv3,
169 d: RnInterval,
170 p2: [f64; 3],
171 p3: [f64; 3],
172 p4: [f64; 3],
173) -> Option<Sign> {
174 let p4i = ivec(p4);
175 let row1 = [
176 lambda[0].sub(d.mul(p4i[0])),
177 lambda[1].sub(d.mul(p4i[1])),
178 lambda[2].sub(d.mul(p4i[2])),
179 ];
180 let row2 = isub(&ivec(p2), &p4i);
181 let row3 = isub(&ivec(p3), &p4i);
182 let lambda_det = idet3(&row1, &row2, &row3);
183 let sd = d.sign()?;
184 let sld = lambda_det.sign()?;
185 Some(super::assemble_sign(sld, &[sd]))
186}
187
188pub fn lpi_orient3d(l: &Lpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Option<Sign> {
189 let (lambda, d) = lpi_lambda(l);
190 indirect_orient3d(&lambda, d, p2, p3, p4)
191}
192
193pub fn tpi_orient3d(t: &Tpi, p2: [f64; 3], p3: [f64; 3], p4: [f64; 3]) -> Option<Sign> {
194 let (lambda, d) = tpi_lambda(t);
195 indirect_orient3d(&lambda, d, p2, p3, p4)
196}
197
198#[inline]
199fn axis_idx(axis: DropAxis) -> (usize, usize) {
200 match axis {
201 DropAxis::X => (1, 2),
202 DropAxis::Y => (0, 2),
203 DropAxis::Z => (0, 1),
204 }
205}
206
207fn indirect_orient2d(
210 lambda: &Iv3,
211 d: RnInterval,
212 b: [f64; 3],
213 c: [f64; 3],
214 axis: DropAxis,
215) -> Option<Sign> {
216 let (i, j) = axis_idx(axis);
217 let br = ivec(b);
218 let cr = ivec(c);
219 let li = lambda[i].sub(d.mul(cr[i]));
220 let lj = lambda[j].sub(d.mul(cr[j]));
221 let lambda_det2 = li.mul(br[j].sub(cr[j])).sub(lj.mul(br[i].sub(cr[i])));
222 let sd = d.sign()?;
223 let sld = lambda_det2.sign()?;
224 Some(super::assemble_sign(sld, &[sd]))
225}
226
227pub fn lpi_orient2d(l: &Lpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Option<Sign> {
228 let (lambda, d) = lpi_lambda(l);
229 indirect_orient2d(&lambda, d, b, c, axis)
230}
231
232pub fn tpi_orient2d(t: &Tpi, b: [f64; 3], c: [f64; 3], axis: DropAxis) -> Option<Sign> {
233 let (lambda, d) = tpi_lambda(t);
234 indirect_orient2d(&lambda, d, b, c, axis)
235}
236
237fn ilambda_of(p: &ImplicitPoint) -> (Iv3, RnInterval) {
239 match p {
240 ImplicitPoint::Lpi(l) => lpi_lambda(l),
241 ImplicitPoint::Tpi(t) => tpi_lambda(t),
242 ImplicitPoint::Explicit(_) => unreachable!("ilambda_of: Explicit"),
243 }
244}
245
246pub(super) type IvLam = ([RnInterval; 3], RnInterval);
253
254#[inline]
255pub(super) fn ilambda_cached(p: &ImplicitPoint) -> IvLam {
256 match p {
257 ImplicitPoint::Lpi(l) => lpi_lambda(l),
258 ImplicitPoint::Tpi(t) => tpi_lambda(t),
259 ImplicitPoint::Explicit(e) => (ivec(*e), RnInterval::point(1.0)),
260 }
261}
262
263pub(super) fn orient2d_from_lam_iv(a: &IvLam, b: &IvLam, c: &IvLam, axis: DropAxis) -> Option<Sign> {
270 let (i, j) = axis_idx(axis);
271 let (l1, d1) = (&a.0, a.1);
272 let (l2, d2) = (&b.0, b.1);
273 let (l3, d3) = (&c.0, c.1);
274 let u_i = d1.mul(l2[i]).sub(d2.mul(l1[i]));
275 let u_j = d1.mul(l2[j]).sub(d2.mul(l1[j]));
276 let v_i = d1.mul(l3[i]).sub(d3.mul(l1[i]));
277 let v_j = d1.mul(l3[j]).sub(d3.mul(l1[j]));
278 let det = u_i.mul(v_j).sub(u_j.mul(v_i));
279 Some(super::assemble_sign(det.sign()?, &[d2.sign()?, d3.sign()?]))
280}
281
282pub fn cmp_along(a: &ImplicitPoint, b: &ImplicitPoint, u: [f64; 3]) -> Option<Sign> {
287 let (la, da) = ilambda_cached(a);
288 let (lb, db) = ilambda_cached(b);
289 let ur = ivec(u);
290 let dot_a = la[0].mul(ur[0]).add(la[1].mul(ur[1])).add(la[2].mul(ur[2]));
291 let dot_b = lb[0].mul(ur[0]).add(lb[1].mul(ur[1])).add(lb[2].mul(ur[2]));
292 let num = dot_a.mul(db).sub(dot_b.mul(da));
293 Some(super::assemble_sign(num.sign()?, &[da.sign()?, db.sign()?]))
294}
295
296pub(super) fn cmp_lex_from_lam_iv(a: &IvLam, b: &IvLam) -> Option<Sign> {
299 let (la, da) = (&a.0, a.1);
300 let (lb, db) = (&b.0, b.1);
301 for k in 0..3 {
302 let s = la[k].mul(db).sub(lb[k].mul(da));
303 let sg = super::assemble_sign(s.sign()?, &[da.sign()?, db.sign()?]);
304 if sg != Sign::Zero {
305 return Some(sg);
306 }
307 }
308 Some(Sign::Zero)
309}
310
311pub fn orient2d_2i(a: &ImplicitPoint, b: &ImplicitPoint, c: [f64; 3], axis: DropAxis) -> Option<Sign> {
313 let (i, j) = axis_idx(axis);
314 let (lam1, d1) = ilambda_of(a);
315 let (lam2, d2) = ilambda_of(b);
316 let cr = ivec(c);
317 let a_i = lam1[i].sub(d1.mul(cr[i]));
318 let a_j = lam1[j].sub(d1.mul(cr[j]));
319 let b_i = lam2[i].sub(d2.mul(cr[i]));
320 let b_j = lam2[j].sub(d2.mul(cr[j]));
321 let det = a_i.mul(b_j).sub(a_j.mul(b_i));
322 let sd1 = d1.sign()?;
323 let sd2 = d2.sign()?;
324 Some(super::assemble_sign(det.sign()?, &[sd1, sd2]))
325}
326
327pub fn orient2d_3i(a: &ImplicitPoint, b: &ImplicitPoint, c: &ImplicitPoint, axis: DropAxis) -> Option<Sign> {
329 let (i, j) = axis_idx(axis);
330 let (lam1, d1) = ilambda_of(a);
331 let (lam2, d2) = ilambda_of(b);
332 let (lam3, d3) = ilambda_of(c);
333 let u_i = d1.mul(lam2[i]).sub(d2.mul(lam1[i]));
334 let u_j = d1.mul(lam2[j]).sub(d2.mul(lam1[j]));
335 let v_i = d1.mul(lam3[i]).sub(d3.mul(lam1[i]));
336 let v_j = d1.mul(lam3[j]).sub(d3.mul(lam1[j]));
337 let det = u_i.mul(v_j).sub(u_j.mul(v_i));
338 let sd2 = d2.sign()?;
339 let sd3 = d3.sign()?;
340 Some(super::assemble_sign(det.sign()?, &[sd2, sd3]))
341}
342
343fn icmp_axis(a: &ImplicitPoint, b: &ImplicitPoint, k: usize) -> Option<Sign> {
345 use ImplicitPoint::Explicit;
346 match (a, b) {
347 (Explicit(ae), Explicit(be)) => Some(if ae[k] < be[k] {
348 Sign::Negative
349 } else if ae[k] > be[k] {
350 Sign::Positive
351 } else {
352 Sign::Zero
353 }),
354 (_, Explicit(be)) => {
355 let (lam, d) = ilambda_of(a);
356 let num = lam[k].sub(d.mul(RnInterval::point(be[k])));
357 Some(super::assemble_sign(num.sign()?, &[d.sign()?]))
358 }
359 (Explicit(ae), _) => {
360 let (lam, d) = ilambda_of(b);
361 let num = RnInterval::point(ae[k]).mul(d).sub(lam[k]);
362 Some(super::assemble_sign(num.sign()?, &[d.sign()?]))
363 }
364 (_, _) => {
365 let (la, da) = ilambda_of(a);
366 let (lb, db) = ilambda_of(b);
367 let num = la[k].mul(db).sub(lb[k].mul(da));
368 Some(super::assemble_sign(num.sign()?, &[da.sign()?, db.sign()?]))
369 }
370 }
371}
372
373pub fn cmp_lex(a: &ImplicitPoint, b: &ImplicitPoint) -> Option<Sign> {
376 for k in 0..3 {
377 match icmp_axis(a, b, k)? {
378 Sign::Zero => continue,
379 s => return Some(s),
380 }
381 }
382 Some(Sign::Zero)
383}