use crate::int::CurveInt;
use crate::kernel::int::curve::chord::Chord;
use crate::kernel::int::curve::param::SegmentParam;
use crate::kernel::int::curve::segment::Segment;
use crate::kernel::int::math::angle::ApproximateAngle;
use i_overlay::i_float::int::number::wide_int::WideIntNumber;
use i_overlay::i_shape::int::IntPoint;
pub(super) struct Split<I: CurveInt> {
pub(super) t0: SegmentParam<I>,
pub(super) s0: Option<Segment<I>>,
pub(super) t1: SegmentParam<I>,
pub(super) s1: Option<Segment<I>>,
pub(super) step: SegmentParam<I>,
}
impl<I: CurveInt> Segment<I> {
pub(crate) fn is_nearly_linear(&self, sin_angle_neg_pow2: u32) -> bool {
let chord = self.chord();
let chord_vector = chord.vector();
if chord_vector.sqr_length() == I::Wide::ZERO {
return false;
}
match self {
Segment::Line(_) => true,
Segment::Quad(quad) => {
let [p0, p1, p2] = quad.control_points;
let chord = p0 - p2;
let h0 = chord.is_nearly_collinear_with(p0 - p1, sin_angle_neg_pow2);
let h1 = chord.is_nearly_collinear_with(p2 - p1, sin_angle_neg_pow2);
h0 && h1
}
Segment::Cubic(cubic) => {
let [p0, p1, p2, p3] = cubic.control_points;
let chord = p0 - p3;
let h0 = chord.is_nearly_collinear_with(p0 - p1, sin_angle_neg_pow2);
let h1 = chord.is_nearly_collinear_with(p3 - p2, sin_angle_neg_pow2);
h0 && h1
}
Segment::Arc(arc) => {
let [p0, p1, p2] = arc.control_points;
let chord = p0 - p2;
let h0 = chord.is_nearly_collinear_with(p0 - p1, sin_angle_neg_pow2);
let h1 = chord.is_nearly_collinear_with(p2 - p1, sin_angle_neg_pow2);
h0 && h1
}
}
}
#[inline]
pub(super) fn split(
&self,
t: SegmentParam<I>,
step: SegmentParam<I>,
a: IntPoint<I>,
b: IntPoint<I>,
) -> Split<I> {
let half_step = step.value() >> 1;
let step = SegmentParam::new(I::from_wide(half_step));
let t0 = SegmentParam::new(I::from_wide(t.value() - half_step));
let t1 = SegmentParam::new(I::from_wide(t.value() + half_step));
let [s0, s1] = self.bisect(a, b, SegmentParam::half());
Split { t0, s0, t1, s1, step }
}
}
#[cfg(test)]
mod tests {
use super::Segment;
use crate::kernel::int::curve::arc::{ArcDirection, ArcPhase, ArcSegment, ArcVector, EllipseFrame};
use crate::kernel::int::curve::cubic::CubicSegment;
use crate::kernel::int::curve::quad::QuadSegment;
use i_overlay::i_float::int::number::fixed_scale::FixedScale;
use i_overlay::i_shape::int::IntPoint;
fn arc_with_controls(control_points: [IntPoint<i32>; 3]) -> Segment<i32> {
let one = FixedScale::<i32>::DENOMINATOR as i32;
Segment::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,
weights: [one, 759_250_125, one],
start_phase: ArcPhase { cos: one, sin: 0 },
end_phase: ArcPhase { cos: 0, sin: one },
direction: ArcDirection::CounterClockwise,
})
}
#[test]
fn detects_nearly_linear_segment() {
let segment = Segment::Cubic(CubicSegment {
control_points: [[0, 0].into(), [8, 0].into(), [24, 1].into(), [32, 0].into()],
});
assert!(segment.is_nearly_linear(3));
}
#[test]
fn rejects_curved_segment() {
let segment = Segment::Quad(QuadSegment {
control_points: [[0, 0].into(), [4, 8].into(), [8, 0].into()],
});
assert!(!segment.is_nearly_linear(3));
}
#[test]
fn detects_nearly_linear_arc_from_control_triangle() {
let segment = arc_with_controls([IntPoint::new(0, 0), IntPoint::new(16, 1), IntPoint::new(32, 0)]);
assert!(segment.is_nearly_linear(3));
}
#[test]
fn rejects_curved_arc_control_triangle() {
let segment = arc_with_controls([IntPoint::new(0, 0), IntPoint::new(16, 16), IntPoint::new(32, 0)]);
assert!(!segment.is_nearly_linear(3));
}
}