Skip to main content

i_overlay/split/
cross_solver.rs

1use crate::geom::x_segment::XSegment;
2use i_float::fix_vec::FixVec;
3use i_float::int::point::IntPoint;
4use i_float::triangle::Triangle;
5use i_float::u128::UInt128;
6
7pub(super) type CollinearMask = u8;
8
9pub(super) trait EndMask {
10    fn is_target_a(&self) -> bool;
11    fn is_target_b(&self) -> bool;
12    fn is_other_a(&self) -> bool;
13    fn is_other_b(&self) -> bool;
14
15    fn new(target_a: bool, target_b: bool, other_a: bool, other_b: bool) -> Self;
16}
17
18const TARGET_A: u8 = 0b0001;
19const TARGET_B: u8 = 0b0010;
20const OTHER_A: u8 = 0b0100;
21const OTHER_B: u8 = 0b1000;
22
23impl EndMask for CollinearMask {
24    #[inline(always)]
25    fn is_target_a(&self) -> bool {
26        self & TARGET_A == TARGET_A
27    }
28
29    #[inline(always)]
30    fn is_target_b(&self) -> bool {
31        self & TARGET_B == TARGET_B
32    }
33
34    #[inline(always)]
35    fn is_other_a(&self) -> bool {
36        self & OTHER_A == OTHER_A
37    }
38
39    #[inline(always)]
40    fn is_other_b(&self) -> bool {
41        self & OTHER_B == OTHER_B
42    }
43
44    #[inline(always)]
45    fn new(target_a: bool, target_b: bool, other_a: bool, other_b: bool) -> Self {
46        let a0 = target_a as u8;
47        let b0 = (target_b as u8) << 1;
48        let a1 = (other_a as u8) << 2;
49        let b1 = (other_b as u8) << 3;
50
51        a0 | b0 | a1 | b1
52    }
53}
54
55pub(super) struct CrossResult {
56    pub(super) point: IntPoint,
57    pub(super) cross_type: CrossType,
58    pub(super) is_round: bool,
59}
60
61pub(super) enum CrossType {
62    Pure,
63    TargetEnd,
64    OtherEnd,
65    Overlay,
66}
67
68pub(super) struct CrossSolver {}
69
70impl CrossSolver {
71    pub(super) fn cross(target: &XSegment, other: &XSegment, radius: i64) -> Option<CrossResult> {
72        let a0b0a1 = Triangle::clock_direction_point(target.a, target.b, other.a);
73        let a0b0b1 = Triangle::clock_direction_point(target.a, target.b, other.b);
74
75        let a1b1a0 = Triangle::clock_direction_point(other.a, other.b, target.a);
76        let a1b1b0 = Triangle::clock_direction_point(other.a, other.b, target.b);
77
78        let s = (1 & (a0b0a1 + 1)) + (1 & (a0b0b1 + 1)) + (1 & (a1b1a0 + 1)) + (1 & (a1b1b0 + 1));
79
80        if s == 4 {
81            return Some(CrossResult {
82                point: IntPoint::ZERO,
83                cross_type: CrossType::Overlay,
84                is_round: false,
85            });
86        }
87
88        let is_not_cross = a0b0a1 == a0b0b1 || a1b1a0 == a1b1b0;
89
90        if s > 1 || is_not_cross {
91            return None;
92        }
93
94        if s != 0 {
95            return if a0b0a1 == 0 {
96                Some(CrossResult {
97                    point: other.a,
98                    cross_type: CrossType::OtherEnd,
99                    is_round: false,
100                })
101            } else if a0b0b1 == 0 {
102                Some(CrossResult {
103                    point: other.b,
104                    cross_type: CrossType::OtherEnd,
105                    is_round: false,
106                })
107            } else if a1b1a0 == 0 {
108                Some(CrossResult {
109                    point: target.a,
110                    cross_type: CrossType::TargetEnd,
111                    is_round: false,
112                })
113            } else {
114                Some(CrossResult {
115                    point: target.b,
116                    cross_type: CrossType::TargetEnd,
117                    is_round: false,
118                })
119            };
120        }
121
122        Self::middle_cross(target, other, radius)
123    }
124
125    pub(super) fn collinear(target: &XSegment, other: &XSegment) -> CollinearMask {
126        let a0 = FixVec::new_point(target.a);
127        let b0 = FixVec::new_point(target.b);
128        let a1 = FixVec::new_point(other.a);
129        let b1 = FixVec::new_point(other.b);
130
131        let v1 = b1 - a1;
132
133        let aa0 = (a0 - a1).dot_product(v1).signum();
134        let ab0 = (a0 - b1).dot_product(v1).signum();
135        let ba0 = (b0 - a1).dot_product(v1).signum();
136        let bb0 = (b0 - b1).dot_product(v1).signum();
137
138        let aa1 = -aa0;
139        let ab1 = -ba0;
140        let ba1 = -ab0;
141        let bb1 = -bb0;
142
143        let is_target_a = aa0 == -ab0 && aa0 != 0;
144        let is_target_b = ba0 == -bb0 && ba0 != 0;
145
146        let is_other_a = aa1 == -ab1 && aa1 != 0;
147        let is_other_b = ba1 == -bb1 && ba1 != 0;
148
149        CollinearMask::new(is_target_a, is_target_b, is_other_a, is_other_b)
150    }
151
152    fn middle_cross(target: &XSegment, other: &XSegment, radius: i64) -> Option<CrossResult> {
153        let p = CrossSolver::cross_point(target, other);
154
155        if Triangle::is_line_point(target.a, p, target.b) && Triangle::is_line_point(other.a, p, other.b) {
156            return Some(CrossResult {
157                point: p,
158                cross_type: CrossType::Pure,
159                is_round: false,
160            });
161        }
162
163        // still can be common ends because of rounding
164        // snap to nearest end with r (1^2 + 1^2 == 2)
165
166        let ra0 = target.a.sqr_distance(p);
167        let rb0 = target.b.sqr_distance(p);
168
169        let ra1 = other.a.sqr_distance(p);
170        let rb1 = other.b.sqr_distance(p);
171
172        if ra0 <= radius || ra1 <= radius || rb0 <= radius || rb1 <= radius {
173            let r0 = ra0.min(rb0);
174            let r1 = ra1.min(rb1);
175
176            if r0 <= r1 {
177                let p = if ra0 < rb0 { target.a } else { target.b };
178                // ignore if it's a clean point
179                if Triangle::is_not_line_point(other.a, p, other.b) {
180                    return Some(CrossResult {
181                        point: p,
182                        cross_type: CrossType::TargetEnd,
183                        is_round: true,
184                    });
185                }
186            } else {
187                let p = if ra1 < rb1 { other.a } else { other.b };
188
189                // ignore if it's a clean point
190                if Triangle::is_not_line_point(target.a, p, target.b) {
191                    return Some(CrossResult {
192                        point: p,
193                        cross_type: CrossType::OtherEnd,
194                        is_round: true,
195                    });
196                }
197            }
198        }
199
200        Some(CrossResult {
201            point: p,
202            cross_type: CrossType::Pure,
203            is_round: true,
204        })
205    }
206
207    fn cross_point(target: &XSegment, other: &XSegment) -> IntPoint {
208        // edges are not parallel
209        // any abs(x) and abs(y) < 2^30
210        // The result must be < 2^30
211
212        // Classic approach:
213
214        // let dxA = a0.x - a1.x
215        // let dyB = b0.y - b1.y
216        // let dyA = a0.y - a1.y
217        // let dxB = b0.x - b1.x
218        //
219        // let xyA = a0.x * a1.y - a0.y * a1.x
220        // let xyB = b0.x * b1.y - b0.y * b1.x
221        //
222        // overflow is possible!
223        // let kx = xyA * dxB - dxA * xyB
224        //
225        // overflow is possible!
226        // let ky = xyA * dyB - dyA * xyB
227        //
228        // let divider = dxA * dyB - dyA * dxB
229        //
230        // let x = kx / divider
231        // let y = ky / divider
232        //
233        // return FixVec(x, y)
234
235        // offset approach
236        // move all picture by -a0. Point a0 will be equal (0, 0)
237
238        // move a0.x to 0
239        // move all by a0.x
240        let a0x = target.a.x as i64;
241        let a0y = target.a.y as i64;
242
243        let a1x = target.b.x as i64 - a0x;
244        let b0x = other.a.x as i64 - a0x;
245        let b1x = other.b.x as i64 - a0x;
246
247        // move a0.y to 0
248        // move all by a0.y
249        let a1y = target.b.y as i64 - a0y;
250        let b0y = other.a.y as i64 - a0y;
251        let b1y = other.b.y as i64 - a0y;
252
253        let dy_b = b0y - b1y;
254        let dx_b = b0x - b1x;
255
256        // let xyA = 0
257        let xy_b = b0x * b1y - b0y * b1x;
258
259        let x0: i64;
260        let y0: i64;
261
262        // a1y and a1x cannot be zero simultaneously, because we will get edge a0<>a1 zero length and it is impossible
263
264        if a1x == 0 {
265            // dxB is not zero because it will be parallel case and it's impossible
266            x0 = 0;
267            y0 = xy_b / dx_b;
268        } else if a1y == 0 {
269            // dyB is not zero because it will be parallel case and it's impossible
270            y0 = 0;
271            x0 = -xy_b / dy_b;
272        } else {
273            // divider
274            let div = a1y * dx_b - a1x * dy_b;
275
276            // calculate result sign
277            let s = div.signum() * xy_b.signum();
278            let sx = a1x.signum() * s;
279            let sy = a1y.signum() * s;
280
281            // use custom u128 bit math with rounding
282            let uxy_b = xy_b.unsigned_abs();
283            let udiv = div.unsigned_abs();
284
285            let kx = UInt128::multiply(a1x.unsigned_abs(), uxy_b);
286            let ky = UInt128::multiply(a1y.unsigned_abs(), uxy_b);
287
288            let ux = kx.divide_with_rounding(udiv);
289            let uy = ky.divide_with_rounding(udiv);
290
291            // get i64 bit result
292            x0 = sx * ux as i64;
293            y0 = sy * uy as i64;
294        }
295
296        let x = (x0 + a0x) as i32;
297        let y = (y0 + a0y) as i32;
298
299        IntPoint::new(x, y)
300    }
301}
302
303const LAST_BIT_INDEX: usize = 63;
304
305trait RoundDivide {
306    fn divide_with_rounding(&self, divisor: u64) -> u64;
307}
308
309impl RoundDivide for UInt128 {
310    fn divide_with_rounding(&self, divisor: u64) -> u64 {
311        if self.high == 0 {
312            let result = self.low / divisor;
313            let remainder = self.low - result * divisor;
314            return if remainder >= (divisor + 1) >> 1 {
315                result + 1
316            } else {
317                result
318            };
319        }
320
321        let dn = divisor.leading_zeros();
322        let norm_divisor = divisor << dn;
323        let mut norm_dividend_high = (self.high << dn) | (self.low >> (u64::BITS - dn));
324        let mut norm_dividend_low = self.low << dn;
325
326        let mut quotient = 0;
327        let one = 1 << LAST_BIT_INDEX;
328
329        for _ in 0..u64::BITS {
330            let bit = (norm_dividend_high & one) != 0;
331            norm_dividend_high = (norm_dividend_high << 1) | (norm_dividend_low >> LAST_BIT_INDEX);
332            norm_dividend_low <<= 1;
333            quotient <<= 1;
334            if norm_dividend_high >= norm_divisor || bit {
335                norm_dividend_high = norm_dividend_high.wrapping_sub(norm_divisor);
336                quotient |= 1;
337            }
338        }
339
340        // Check remainder for rounding
341        let remainder = (norm_dividend_high << (u64::BITS - dn)) | (norm_dividend_low >> dn);
342        if remainder >= (divisor + 1) >> 1 {
343            quotient += 1;
344        }
345
346        quotient
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use crate::geom::x_segment::XSegment;
353    use crate::split::cross_solver::{CrossSolver, CrossType};
354    use i_float::int::point::IntPoint;
355
356    impl XSegment {
357        fn new(a: IntPoint, b: IntPoint) -> Self {
358            Self { a, b }
359        }
360    }
361
362    #[test]
363    fn test_simple_cross() {
364        let s: i32 = 1024;
365
366        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0));
367        let eb = XSegment::new(IntPoint::new(0, -s), IntPoint::new(0, s));
368
369        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
370
371        match result.cross_type {
372            CrossType::Pure => {
373                assert_eq!(IntPoint::ZERO, result.point);
374            }
375            _ => {
376                panic!("Fail cross result");
377            }
378        }
379    }
380
381    #[test]
382    fn test_big_cross_1() {
383        let s: i32 = 1024_000_000;
384
385        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0));
386        let eb = XSegment::new(IntPoint::new(0, -s), IntPoint::new(0, s));
387
388        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
389
390        match result.cross_type {
391            CrossType::Pure => {
392                assert_eq!(IntPoint::ZERO, result.point);
393            }
394            _ => {
395                panic!("Fail cross result");
396            }
397        }
398    }
399
400    #[test]
401    fn test_big_cross_2() {
402        let s: i32 = 1024_000_000;
403
404        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0));
405        let eb = XSegment::new(IntPoint::new(1024, -s), IntPoint::new(1024, s));
406
407        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
408
409        match result.cross_type {
410            CrossType::Pure => {
411                assert_eq!(IntPoint::new(1024, 0), result.point);
412            }
413            _ => {
414                panic!("Fail cross result");
415            }
416        }
417    }
418
419    #[test]
420    fn test_big_cross_3() {
421        let s: i32 = 1024_000_000;
422        let q: i32 = s / 2;
423
424        let ea = XSegment::new(IntPoint::new(-s, -s), IntPoint::new(s, s));
425        let eb = XSegment::new(IntPoint::new(q, -s), IntPoint::new(q, s));
426
427        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
428
429        match result.cross_type {
430            CrossType::Pure => {
431                assert_eq!(IntPoint::new(512_000_000, 512_000_000), result.point);
432            }
433            _ => {
434                panic!("Fail cross result");
435            }
436        }
437    }
438
439    #[test]
440    fn test_left_end() {
441        let s: i32 = 1024_000_000;
442
443        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0));
444        let eb = XSegment::new(IntPoint::new(-s, -s), IntPoint::new(-s, s));
445
446        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
447
448        match result.cross_type {
449            CrossType::TargetEnd => {
450                assert_eq!(IntPoint::new(-s, 0), result.point);
451            }
452            _ => {
453                panic!("Fail cross result");
454            }
455        }
456    }
457
458    #[test]
459    fn test_right_end() {
460        let s: i32 = 1024_000_000;
461
462        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s, 0));
463        let eb = XSegment::new(IntPoint::new(s, -s), IntPoint::new(s, s));
464
465        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
466
467        match result.cross_type {
468            CrossType::TargetEnd => {
469                assert_eq!(IntPoint::new(s, 0), result.point);
470            }
471            _ => {
472                panic!("Fail cross result");
473            }
474        }
475    }
476
477    #[test]
478    fn test_left_top() {
479        let s: i32 = 1024_000_000;
480
481        let ea = XSegment::new(IntPoint::new(-s, s), IntPoint::new(s, s));
482        let eb = XSegment::new(IntPoint::new(-s, s), IntPoint::new(-s, -s));
483
484        let result = CrossSolver::cross(&ea, &eb, 2);
485        debug_assert!(result.is_none());
486    }
487
488    #[test]
489    fn test_real_case_1() {
490        let ea = XSegment::new(IntPoint::new(7256, -14637), IntPoint::new(7454, -15045));
491        let eb = XSegment::new(IntPoint::new(7343, -14833), IntPoint::new(7506, -15144));
492
493        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
494
495        match result.cross_type {
496            CrossType::Pure => {}
497            _ => {
498                panic!("Fail cross result");
499            }
500        }
501    }
502
503    #[test]
504    fn test_real_case_2() {
505        let ea = XSegment::new(IntPoint::new(-8555798, -1599355), IntPoint::new(-1024000, 0));
506        let eb = XSegment::new(IntPoint::new(-8571363, 1513719), IntPoint::new(-1023948, -10239));
507
508        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
509
510        match result.cross_type {
511            CrossType::Pure => {
512                assert_eq!(IntPoint::new(-1048691, -5244), result.point);
513            }
514            _ => {
515                panic!("Fail cross result");
516            }
517        }
518    }
519
520    #[test]
521    fn test_real_case_3() {
522        let ea = XSegment::new(IntPoint::new(-8555798, -1599355), IntPoint::new(513224, -5243));
523        let eb = XSegment::new(IntPoint::new(-8555798, -1599355), IntPoint::new(513224, -5243));
524
525        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
526
527        match result.cross_type {
528            CrossType::Overlay => {
529                assert_eq!(IntPoint::ZERO, result.point);
530            }
531            _ => {
532                panic!("Fail cross result");
533            }
534        }
535    }
536
537    #[test]
538    fn test_real_case_4() {
539        let ea = XSegment::new(
540            IntPoint::new(-276659431, 380789039),
541            IntPoint::new(-221915258, 435533212),
542        );
543        let eb = XSegment::new(
544            IntPoint::new(-276659432, 380789038),
545            IntPoint::new(-276659430, 380789040),
546        );
547
548        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
549
550        match result.cross_type {
551            CrossType::Overlay => {
552                assert_eq!(IntPoint::ZERO, result.point);
553            }
554            _ => {
555                panic!("Fail cross result");
556            }
557        }
558    }
559
560    #[test]
561    fn test_penetration() {
562        let s: i32 = 1024;
563
564        let ea = XSegment::new(IntPoint::new(-s, 0), IntPoint::new(s / 2, 0));
565        let eb = XSegment::new(IntPoint::new(0, 0), IntPoint::new(s, 0));
566
567        let result = CrossSolver::cross(&ea, &eb, 2).unwrap();
568
569        match result.cross_type {
570            CrossType::Overlay => {
571                assert_eq!(IntPoint::ZERO, result.point);
572            }
573            _ => {
574                panic!("Fail cross result");
575            }
576        }
577    }
578}