i_curve 0.1.2

Boolean operations on closed paths of lines, Bezier curves, and rational elliptic arcs
Documentation
use crate::int::CurveInt;
use crate::kernel::int::cross::intersector::{
    ContactPoint, SegmentIntersectionBuffer, SegmentIntersector, SplitOptions,
};
use crate::kernel::int::curve::chord::Chord;
use crate::kernel::int::curve::segment::Segment;
use crate::kernel::int::normalization::canonical::PushCanonicalSegment;
use alloc::vec::Vec;

impl<I: CurveInt> Segment<I> {
    pub(crate) fn intersect(self, other: Self) -> Vec<ContactPoint<I>> {
        let mut a_segments = Vec::new();
        let mut b_segments = Vec::new();

        a_segments.push_canonical(self);
        b_segments.push_canonical(other);

        let mut buffer = SegmentIntersectionBuffer::default();
        let mut output = Vec::new();

        for a in a_segments.iter() {
            let a_rect = a.chord().to_rect();
            for b in b_segments.iter() {
                let b_rect = b.chord().to_rect();
                if a_rect.is_intersect_border_exclude(&b_rect) {
                    let intersector = SegmentIntersector::new(*a, *b, SplitOptions::default());
                    for &contact in intersector.intersect_with_buffer(&mut buffer) {
                        if !output.contains(&contact) {
                            output.push(contact);
                        }
                    }
                }
            }
        }

        output
    }
}
#[cfg(test)]
mod tests {
    use crate::kernel::int::cross::intersector::ContactType;
    use crate::kernel::int::curve::arc::{ArcDirection, ArcPhase, ArcSegment, ArcVector, EllipseFrame};
    use crate::kernel::int::curve::cubic::CubicSegment;
    use crate::kernel::int::curve::line::LineSegment;
    use crate::kernel::int::curve::quad::QuadSegment;
    use crate::kernel::int::curve::segment::Segment;
    use i_overlay::i_float::int::number::fixed_scale::FixedScale;
    use i_overlay::i_shape::int::IntPoint;

    fn quarter_circle(center: IntPoint<i32>, left_half: bool) -> Segment<i32> {
        let one = FixedScale::<i32>::DENOMINATOR as i32;
        let (control_points, start_phase, end_phase, direction) = if left_half {
            (
                [
                    IntPoint::new(center.x - 100, center.y),
                    IntPoint::new(center.x - 100, center.y + 100),
                    IntPoint::new(center.x, center.y + 100),
                ],
                ArcPhase { cos: -one, sin: 0 },
                ArcPhase { cos: 0, sin: one },
                ArcDirection::Clockwise,
            )
        } else {
            (
                [
                    IntPoint::new(center.x + 100, center.y),
                    IntPoint::new(center.x + 100, center.y + 100),
                    IntPoint::new(center.x, center.y + 100),
                ],
                ArcPhase { cos: one, sin: 0 },
                ArcPhase { cos: 0, sin: one },
                ArcDirection::CounterClockwise,
            )
        };

        Segment::Arc(ArcSegment {
            ellipse: EllipseFrame {
                center,
                axis_x: ArcVector { x: 100, y: 0 },
                axis_y: ArcVector { x: 0, y: 100 },
            },
            control_points,
            weights: [one, 759_250_125, one],
            start_phase,
            end_phase,
            direction,
        })
    }

    #[test]
    fn test_0() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [940, 899].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert!(!result.is_empty());
    }

    #[test]
    fn test_1() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [900, 700].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert!(!result.is_empty());
    }

    #[test]
    fn intersects_arc_with_line() {
        let arc = quarter_circle(IntPoint::new(0, 0), false);
        let line = Segment::Line(LineSegment {
            control_points: [IntPoint::new(0, 0), IntPoint::new(100, 100)],
        });

        let contacts = arc.intersect(line);

        assert!(contacts.iter().any(|contact| {
            contact.contact_type == ContactType::Cross
                && (contact.point.x - 71).abs() <= 2
                && (contact.point.y - 71).abs() <= 2
        }));
    }

    #[test]
    fn intersects_arc_with_quad_and_cubic() {
        let arc = quarter_circle(IntPoint::new(0, 0), false);
        let quad = Segment::Quad(QuadSegment {
            control_points: [
                IntPoint::new(0, 0),
                IntPoint::new(30, 60),
                IntPoint::new(100, 100),
            ],
        });
        let cubic = Segment::Cubic(CubicSegment {
            control_points: [
                IntPoint::new(0, 0),
                IntPoint::new(20, 60),
                IntPoint::new(80, 40),
                IntPoint::new(100, 100),
            ],
        });

        assert!(
            arc.intersect(quad)
                .iter()
                .any(|contact| contact.contact_type == ContactType::Cross)
        );
        assert!(
            arc.intersect(cubic)
                .iter()
                .any(|contact| contact.contact_type == ContactType::Cross)
        );
    }

    #[test]
    fn intersects_two_arcs() {
        let first = quarter_circle(IntPoint::new(0, 0), false);
        let second = quarter_circle(IntPoint::new(100, 0), true);

        let contacts = first.intersect(second);

        assert!(contacts.iter().any(|contact| {
            contact.contact_type == ContactType::Cross
                && (contact.point.x - 50).abs() <= 2
                && (contact.point.y - 87).abs() <= 2
        }));
    }

    #[test]
    fn test_2() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [596, 795].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert!(!result.is_empty());
    }

    #[test]
    fn test_3() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [597, 795].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert!(!result.is_empty());
    }

    #[test]
    fn test_4() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [1263, 176].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert_eq!(result.len(), 1);
    }

    #[test]
    fn test_5() {
        let s0 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 100].into(),
                [100, 400].into(),
                [600, 900].into(),
                [464, 732].into(),
            ],
        });
        let s1 = Segment::Cubic(CubicSegment {
            control_points: [
                [100i32, 900].into(),
                [100, 500].into(),
                [600, 0].into(),
                [1000, 0].into(),
            ],
        });

        let result = s0.intersect(s1);
        assert_eq!(result.len(), 1);
    }
}