i_curve 0.1.1

Boolean operations on closed paths of lines, Bezier curves, and rational elliptic arcs
Documentation
use crate::collections::stack_vec::StackVec;
use crate::int::CurveInt;
use crate::kernel::int::curve::arc::ArcSegment;
use crate::kernel::int::curve::chord::{Chord, SegmentChord};
use crate::kernel::int::curve::cubic::CubicSegment;
use crate::kernel::int::curve::line::LineSegment;
use crate::kernel::int::curve::quad::QuadSegment;
use i_overlay::i_shape::int::IntPoint;

#[derive(Debug, Clone, Copy)]
pub(crate) enum Segment<I: CurveInt> {
    Line(LineSegment<I>),
    Quad(QuadSegment<I>),
    Cubic(CubicSegment<I>),
    Arc(ArcSegment<I>),
}

impl<I: CurveInt> Default for Segment<I> {
    fn default() -> Self {
        Self::Line(LineSegment {
            control_points: [IntPoint::ZERO; 2],
        })
    }
}

impl<I: CurveInt> Segment<I> {
    #[inline]
    pub(crate) fn convex_hull(&self) -> StackVec<IntPoint<I>, 4> {
        match self {
            Segment::Line(line) => StackVec::with_slice_as_convex(&line.control_points),
            Segment::Quad(quad) => StackVec::with_slice_as_convex(&quad.control_points),
            Segment::Cubic(cubic) => StackVec::with_slice_as_convex(&cubic.control_points),
            Segment::Arc(arc) => StackVec::with_slice_as_convex(&arc.control_points),
        }
    }
}

impl<I: CurveInt> Chord<I> for Segment<I> {
    #[inline]
    fn chord(&self) -> SegmentChord<I> {
        match self {
            Segment::Line(line) => line.chord(),
            Segment::Quad(quad) => quad.chord(),
            Segment::Cubic(cubic) => cubic.chord(),
            Segment::Arc(arc) => SegmentChord {
                a: arc.control_points[0],
                b: arc.control_points[2],
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::kernel::int::curve::arc::{ArcDirection, ArcPhase, ArcVector, EllipseFrame};
    use i_overlay::i_float::int::number::fixed_scale::FixedScale;

    #[test]
    fn arc_chord_and_convex_hull_use_rational_controls() {
        let one = FixedScale::<i32>::DENOMINATOR as i32;
        let arc = ArcSegment {
            ellipse: EllipseFrame {
                center: IntPoint::new(0, 0),
                axis_x: ArcVector { x: 100, y: 0 },
                axis_y: ArcVector { x: 0, y: 100 },
            },
            control_points: [
                IntPoint::new(100, 0),
                IntPoint::new(100, 100),
                IntPoint::new(0, 100),
            ],
            weights: [one, 759_250_125, one],
            start_phase: ArcPhase { cos: one, sin: 0 },
            end_phase: ArcPhase { cos: 0, sin: one },
            direction: ArcDirection::CounterClockwise,
        };
        let segment = Segment::Arc(arc);

        let chord = segment.chord();
        assert_eq!(chord.a, arc.control_points[0]);
        assert_eq!(chord.b, arc.control_points[2]);

        let hull = segment.convex_hull();
        assert_eq!(hull.len(), 3);
        for point in arc.control_points {
            assert!(hull.as_slice().contains(&point));
        }
    }
}