use crate::{Path, Point, Transform};
use crate::dash::StrokeDash;
use crate::floating_point::{NonZeroPositiveF32, NormalizedF32, NormalizedF32Exclusive};
use crate::path::{PathSegment, PathSegmentsIter};
use crate::path_builder::{PathBuilder, PathDirection};
use crate::path_geometry;
use crate::scalar::{Scalar, SCALAR_NEARLY_ZERO, SCALAR_ROOT_2_OVER_2};
#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
use crate::NoStdFloat;
struct SwappableBuilders<'a> {
    inner: &'a mut PathBuilder,
    outer: &'a mut PathBuilder,
}
impl<'a> SwappableBuilders<'a> {
    fn swap(&mut self) {
        core::mem::swap(&mut self.inner, &mut self.outer);
    }
}
#[derive(Clone, PartialEq, Debug)]
pub struct Stroke {
    pub width: f32,
    pub miter_limit: f32,
    pub line_cap: LineCap,
    pub line_join: LineJoin,
    pub dash: Option<StrokeDash>,
}
impl Default for Stroke {
    fn default() -> Self {
        Stroke {
            width: 1.0,
            miter_limit: 4.0,
            line_cap: LineCap::default(),
            line_join: LineJoin::default(),
            dash: None,
        }
    }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum LineCap {
    Butt,
    Round,
    Square,
}
impl Default for LineCap {
    fn default() -> Self {
        LineCap::Butt
    }
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum LineJoin {
    Miter,
    Round,
    Bevel,
}
impl Default for LineJoin {
    fn default() -> Self {
        LineJoin::Miter
    }
}
const QUAD_RECURSIVE_LIMIT: usize = 3;
const RECURSIVE_LIMITS: [i32; 4] = [5 * 3, 26 * 3, 11 * 3, 11 * 3]; type CapProc = fn(
    pivot: Point,
    normal: Point,
    stop: Point,
    other_path: Option<&PathBuilder>,
    path: &mut PathBuilder,
);
type JoinProc = fn(
    before_unit_normal: Point,
    pivot: Point,
    after_unit_normal: Point,
    radius: f32,
    inv_miter_limit: f32,
    prev_is_line: bool,
    curr_is_line: bool,
    builders: SwappableBuilders,
);
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
enum ReductionType {
    Point,       Line,        Quad,        Degenerate,  Degenerate2, Degenerate3, }
#[derive(Copy, Clone, PartialEq, Debug)]
enum StrokeType {
    Outer = 1, Inner = -1,
}
#[derive(Copy, Clone, PartialEq, Debug)]
enum ResultType {
    Split,      Degenerate, Quad,       }
#[derive(Copy, Clone, PartialEq, Debug)]
enum IntersectRayType {
    CtrlPt,
    ResultType,
}
impl Path {
    pub fn stroke(&self, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
        PathStroker::new().stroke(self, stroke, resolution_scale)
    }
}
#[allow(missing_debug_implementations)]
#[derive(Clone)]
pub struct PathStroker {
    radius: f32,
    inv_miter_limit: f32,
    res_scale: f32,
    inv_res_scale: f32,
    inv_res_scale_squared: f32,
    first_normal: Point,
    prev_normal: Point,
    first_unit_normal: Point,
    prev_unit_normal: Point,
    first_pt: Point,
    prev_pt: Point,
    first_outer_pt: Point,
    first_outer_pt_index_in_contour: usize,
    segment_count: i32,
    prev_is_line: bool,
    capper: CapProc,
    joiner: JoinProc,
    inner: PathBuilder,
    outer: PathBuilder,
    cusper: PathBuilder,
    stroke_type: StrokeType,
    recursion_depth: i32, found_tangents: bool, join_completed: bool, }
impl Default for PathStroker {
    fn default() -> Self {
        PathStroker::new()
    }
}
impl PathStroker {
    pub fn new() -> Self {
        PathStroker {
            radius: 0.0,
            inv_miter_limit: 0.0,
            res_scale: 1.0,
            inv_res_scale: 1.0,
            inv_res_scale_squared: 1.0,
            first_normal: Point::zero(),
            prev_normal: Point::zero(),
            first_unit_normal: Point::zero(),
            prev_unit_normal: Point::zero(),
            first_pt: Point::zero(),
            prev_pt: Point::zero(),
            first_outer_pt: Point::zero(),
            first_outer_pt_index_in_contour: 0,
            segment_count: -1,
            prev_is_line: false,
            capper: butt_capper,
            joiner: miter_joiner,
            inner: PathBuilder::new(),
            outer: PathBuilder::new(),
            cusper: PathBuilder::new(),
            stroke_type: StrokeType::Outer,
            recursion_depth: 0,
            found_tangents: false,
            join_completed: false,
        }
    }
    pub fn compute_resolution_scale(ts: &Transform) -> f32 {
        let sx = Point::from_xy(ts.sx, ts.kx).length();
        let sy = Point::from_xy(ts.ky, ts.sy).length();
        if sx.is_finite() && sy.is_finite() {
            let scale = sx.max(sy);
            if scale > 0.0 {
                return scale;
            }
        }
        1.0
    }
    pub fn stroke(&mut self, path: &Path, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
        let width = NonZeroPositiveF32::new(stroke.width)?;
        self.stroke_inner(
            path,
            width,
            stroke.miter_limit,
            stroke.line_cap,
            stroke.line_join,
            resolution_scale,
        )
    }
    fn stroke_inner(
        &mut self,
        path: &Path,
        width: NonZeroPositiveF32,
        miter_limit: f32,
        line_cap: LineCap,
        mut line_join: LineJoin,
        res_scale: f32,
    ) -> Option<Path> {
        let mut inv_miter_limit = 0.0;
        if line_join == LineJoin::Miter {
            if miter_limit <= 1.0 {
                line_join = LineJoin::Bevel;
            } else {
                inv_miter_limit = miter_limit.invert();
            }
        }
        self.res_scale = res_scale;
        self.inv_res_scale = (res_scale * 4.0).invert();
        self.inv_res_scale_squared = self.inv_res_scale.sqr();
        self.radius = width.get().half();
        self.inv_miter_limit = inv_miter_limit;
        self.first_normal = Point::zero();
        self.prev_normal = Point::zero();
        self.first_unit_normal = Point::zero();
        self.prev_unit_normal = Point::zero();
        self.first_pt = Point::zero();
        self.prev_pt = Point::zero();
        self.first_outer_pt = Point::zero();
        self.first_outer_pt_index_in_contour = 0;
        self.segment_count = -1;
        self.prev_is_line = false;
        self.capper = cap_factory(line_cap);
        self.joiner = join_factory(line_join);
        self.inner.clear();
        self.inner.reserve(path.verbs.len(), path.points.len());
        self.outer.clear();
        self.outer
            .reserve(path.verbs.len() * 3, path.points.len() * 3);
        self.cusper.clear();
        self.stroke_type = StrokeType::Outer;
        self.recursion_depth = 0;
        self.found_tangents = false;
        self.join_completed = false;
        let mut last_segment_is_line = false;
        let mut iter = path.segments();
        iter.set_auto_close(true);
        while let Some(segment) = iter.next() {
            match segment {
                PathSegment::MoveTo(p) => {
                    self.move_to(p);
                }
                PathSegment::LineTo(p) => {
                    self.line_to(p, Some(&iter));
                    last_segment_is_line = true;
                }
                PathSegment::QuadTo(p1, p2) => {
                    self.quad_to(p1, p2);
                    last_segment_is_line = false;
                }
                PathSegment::CubicTo(p1, p2, p3) => {
                    self.cubic_to(p1, p2, p3);
                    last_segment_is_line = false;
                }
                PathSegment::Close => {
                    if line_cap != LineCap::Butt {
                        if self.has_only_move_to() {
                            self.line_to(self.move_to_pt(), None);
                            last_segment_is_line = true;
                            continue;
                        }
                        if self.is_current_contour_empty() {
                            last_segment_is_line = true;
                            continue;
                        }
                    }
                    self.close(last_segment_is_line);
                }
            }
        }
        self.finish(last_segment_is_line)
    }
    fn builders(&mut self) -> SwappableBuilders {
        SwappableBuilders {
            inner: &mut self.inner,
            outer: &mut self.outer,
        }
    }
    fn move_to_pt(&self) -> Point {
        self.first_pt
    }
    fn move_to(&mut self, p: Point) {
        if self.segment_count > 0 {
            self.finish_contour(false, false);
        }
        self.segment_count = 0;
        self.first_pt = p;
        self.prev_pt = p;
        self.join_completed = false;
    }
    fn line_to(&mut self, p: Point, iter: Option<&PathSegmentsIter>) {
        let teeny_line = self
            .prev_pt
            .equals_within_tolerance(p, SCALAR_NEARLY_ZERO * self.inv_res_scale);
        if fn_ptr_eq(self.capper, butt_capper) && teeny_line {
            return;
        }
        if teeny_line && (self.join_completed || iter.map(|i| i.has_valid_tangent()) == Some(true))
        {
            return;
        }
        let mut normal = Point::zero();
        let mut unit_normal = Point::zero();
        if !self.pre_join_to(p, true, &mut normal, &mut unit_normal) {
            return;
        }
        self.outer.line_to(p.x + normal.x, p.y + normal.y);
        self.inner.line_to(p.x - normal.x, p.y - normal.y);
        self.post_join_to(p, normal, unit_normal);
    }
    fn quad_to(&mut self, p1: Point, p2: Point) {
        let quad = [self.prev_pt, p1, p2];
        let (reduction, reduction_type) = check_quad_linear(&quad);
        if reduction_type == ReductionType::Point {
            self.line_to(p2, None);
            return;
        }
        if reduction_type == ReductionType::Line {
            self.line_to(p2, None);
            return;
        }
        if reduction_type == ReductionType::Degenerate {
            self.line_to(reduction, None);
            let save_joiner = self.joiner;
            self.joiner = round_joiner;
            self.line_to(p2, None);
            self.joiner = save_joiner;
            return;
        }
        debug_assert_eq!(reduction_type, ReductionType::Quad);
        let mut normal_ab = Point::zero();
        let mut unit_ab = Point::zero();
        let mut normal_bc = Point::zero();
        let mut unit_bc = Point::zero();
        if !self.pre_join_to(p1, false, &mut normal_ab, &mut unit_ab) {
            self.line_to(p2, None);
            return;
        }
        let mut quad_points = QuadConstruct::default();
        self.init_quad(
            StrokeType::Outer,
            NormalizedF32::ZERO,
            NormalizedF32::ONE,
            &mut quad_points,
        );
        self.quad_stroke(&quad, &mut quad_points);
        self.init_quad(
            StrokeType::Inner,
            NormalizedF32::ZERO,
            NormalizedF32::ONE,
            &mut quad_points,
        );
        self.quad_stroke(&quad, &mut quad_points);
        let ok = set_normal_unit_normal(
            quad[1],
            quad[2],
            self.res_scale,
            self.radius,
            &mut normal_bc,
            &mut unit_bc,
        );
        if !ok {
            normal_bc = normal_ab;
            unit_bc = unit_ab;
        }
        self.post_join_to(p2, normal_bc, unit_bc);
    }
    fn cubic_to(&mut self, pt1: Point, pt2: Point, pt3: Point) {
        let cubic = [self.prev_pt, pt1, pt2, pt3];
        let mut reduction = [Point::zero(); 3];
        let mut tangent_pt = Point::zero();
        let reduction_type = check_cubic_linear(&cubic, &mut reduction, Some(&mut tangent_pt));
        if reduction_type == ReductionType::Point {
            self.line_to(pt3, None);
            return;
        }
        if reduction_type == ReductionType::Line {
            self.line_to(pt3, None);
            return;
        }
        if ReductionType::Degenerate <= reduction_type
            && ReductionType::Degenerate3 >= reduction_type
        {
            self.line_to(reduction[0], None);
            let save_joiner = self.joiner;
            self.joiner = round_joiner;
            if ReductionType::Degenerate2 <= reduction_type {
                self.line_to(reduction[1], None);
            }
            if ReductionType::Degenerate3 == reduction_type {
                self.line_to(reduction[2], None);
            }
            self.line_to(pt3, None);
            self.joiner = save_joiner;
            return;
        }
        debug_assert_eq!(reduction_type, ReductionType::Quad);
        let mut normal_ab = Point::zero();
        let mut unit_ab = Point::zero();
        let mut normal_cd = Point::zero();
        let mut unit_cd = Point::zero();
        if !self.pre_join_to(tangent_pt, false, &mut normal_ab, &mut unit_ab) {
            self.line_to(pt3, None);
            return;
        }
        let mut t_values = path_geometry::new_t_values();
        let t_values = path_geometry::find_cubic_inflections(&cubic, &mut t_values);
        let mut last_t = NormalizedF32::ZERO;
        for index in 0..=t_values.len() {
            let next_t = t_values
                .get(index)
                .cloned()
                .map(|n| n.to_normalized())
                .unwrap_or(NormalizedF32::ONE);
            let mut quad_points = QuadConstruct::default();
            self.init_quad(StrokeType::Outer, last_t, next_t, &mut quad_points);
            self.cubic_stroke(&cubic, &mut quad_points);
            self.init_quad(StrokeType::Inner, last_t, next_t, &mut quad_points);
            self.cubic_stroke(&cubic, &mut quad_points);
            last_t = next_t;
        }
        if let Some(cusp) = path_geometry::find_cubic_cusp(&cubic) {
            let cusp_loc = path_geometry::eval_cubic_pos_at(&cubic, cusp.to_normalized());
            self.cusper.push_circle(cusp_loc.x, cusp_loc.y, self.radius);
        }
        self.set_cubic_end_normal(&cubic, normal_ab, unit_ab, &mut normal_cd, &mut unit_cd);
        self.post_join_to(pt3, normal_cd, unit_cd);
    }
    fn cubic_stroke(&mut self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
        if !self.found_tangents {
            let result_type = self.tangents_meet(cubic, quad_points);
            if result_type != ResultType::Quad {
                let ok = points_within_dist(
                    quad_points.quad[0],
                    quad_points.quad[2],
                    self.inv_res_scale,
                );
                if (result_type == ResultType::Degenerate || ok)
                    && self.cubic_mid_on_line(cubic, quad_points)
                {
                    self.add_degenerate_line(quad_points);
                    return true;
                }
            } else {
                self.found_tangents = true;
            }
        }
        if self.found_tangents {
            let result_type = self.compare_quad_cubic(cubic, quad_points);
            if result_type == ResultType::Quad {
                let stroke = &quad_points.quad;
                if self.stroke_type == StrokeType::Outer {
                    self.outer
                        .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
                } else {
                    self.inner
                        .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
                }
                return true;
            }
            if result_type == ResultType::Degenerate {
                if !quad_points.opposite_tangents {
                    self.add_degenerate_line(quad_points);
                    return true;
                }
            }
        }
        if !quad_points.quad[2].x.is_finite() || !quad_points.quad[2].x.is_finite() {
            return false; }
        self.recursion_depth += 1;
        if self.recursion_depth > RECURSIVE_LIMITS[self.found_tangents as usize] {
            return false; }
        let mut half = QuadConstruct::default();
        if !half.init_with_start(quad_points) {
            self.add_degenerate_line(quad_points);
            self.recursion_depth -= 1;
            return true;
        }
        if !self.cubic_stroke(cubic, &mut half) {
            return false;
        }
        if !half.init_with_end(quad_points) {
            self.add_degenerate_line(quad_points);
            self.recursion_depth -= 1;
            return true;
        }
        if !self.cubic_stroke(cubic, &mut half) {
            return false;
        }
        self.recursion_depth -= 1;
        true
    }
    fn cubic_mid_on_line(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
        let mut stroke_mid = Point::zero();
        self.cubic_quad_mid(cubic, quad_points, &mut stroke_mid);
        let dist = pt_to_line(stroke_mid, quad_points.quad[0], quad_points.quad[2]);
        dist < self.inv_res_scale_squared
    }
    fn cubic_quad_mid(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct, mid: &mut Point) {
        let mut cubic_mid_pt = Point::zero();
        self.cubic_perp_ray(cubic, quad_points.mid_t, &mut cubic_mid_pt, mid, None);
    }
    fn cubic_perp_ray(
        &self,
        cubic: &[Point; 4],
        t: NormalizedF32,
        t_pt: &mut Point,
        on_pt: &mut Point,
        tangent: Option<&mut Point>,
    ) {
        *t_pt = path_geometry::eval_cubic_pos_at(cubic, t);
        let mut dxy = path_geometry::eval_cubic_tangent_at(cubic, t);
        let mut chopped = [Point::zero(); 7];
        if dxy.x == 0.0 && dxy.y == 0.0 {
            let mut c_points: &[Point] = cubic;
            if t.get().is_nearly_zero() {
                dxy = cubic[2] - cubic[0];
            } else if (1.0 - t.get()).is_nearly_zero() {
                dxy = cubic[3] - cubic[1];
            } else {
                let t = NormalizedF32Exclusive::new(t.get()).unwrap();
                path_geometry::chop_cubic_at2(cubic, t, &mut chopped);
                dxy = chopped[3] - chopped[2];
                if dxy.x == 0.0 && dxy.y == 0.0 {
                    dxy = chopped[3] - chopped[1];
                    c_points = &chopped;
                }
            }
            if dxy.x == 0.0 && dxy.y == 0.0 {
                dxy = c_points[3] - c_points[0];
            }
        }
        self.set_ray_points(*t_pt, &mut dxy, on_pt, tangent);
    }
    fn set_cubic_end_normal(
        &mut self,
        cubic: &[Point; 4],
        normal_ab: Point,
        unit_normal_ab: Point,
        normal_cd: &mut Point,
        unit_normal_cd: &mut Point,
    ) {
        let mut ab = cubic[1] - cubic[0];
        let mut cd = cubic[3] - cubic[2];
        let mut degenerate_ab = degenerate_vector(ab);
        let mut degenerate_cb = degenerate_vector(cd);
        if degenerate_ab && degenerate_cb {
            *normal_cd = normal_ab;
            *unit_normal_cd = unit_normal_ab;
            return;
        }
        if degenerate_ab {
            ab = cubic[2] - cubic[0];
            degenerate_ab = degenerate_vector(ab);
        }
        if degenerate_cb {
            cd = cubic[3] - cubic[1];
            degenerate_cb = degenerate_vector(cd);
        }
        if degenerate_ab || degenerate_cb {
            *normal_cd = normal_ab;
            *unit_normal_cd = unit_normal_ab;
            return;
        }
        let res = set_normal_unit_normal2(cd, self.radius, normal_cd, unit_normal_cd);
        debug_assert!(res);
    }
    fn compare_quad_cubic(
        &self,
        cubic: &[Point; 4],
        quad_points: &mut QuadConstruct,
    ) -> ResultType {
        self.cubic_quad_ends(cubic, quad_points);
        let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
        if result_type != ResultType::Quad {
            return result_type;
        }
        let mut ray0 = Point::zero();
        let mut ray1 = Point::zero();
        self.cubic_perp_ray(cubic, quad_points.mid_t, &mut ray1, &mut ray0, None);
        self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
    }
    fn cubic_quad_ends(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) {
        if !quad_points.start_set {
            let mut cubic_start_pt = Point::zero();
            self.cubic_perp_ray(
                cubic,
                quad_points.start_t,
                &mut cubic_start_pt,
                &mut quad_points.quad[0],
                Some(&mut quad_points.tangent_start),
            );
            quad_points.start_set = true;
        }
        if !quad_points.end_set {
            let mut cubic_end_pt = Point::zero();
            self.cubic_perp_ray(
                cubic,
                quad_points.end_t,
                &mut cubic_end_pt,
                &mut quad_points.quad[2],
                Some(&mut quad_points.tangent_end),
            );
            quad_points.end_set = true;
        }
    }
    fn close(&mut self, is_line: bool) {
        self.finish_contour(true, is_line);
    }
    fn finish_contour(&mut self, close: bool, curr_is_line: bool) {
        if self.segment_count > 0 {
            if close {
                (self.joiner)(
                    self.prev_unit_normal,
                    self.prev_pt,
                    self.first_unit_normal,
                    self.radius,
                    self.inv_miter_limit,
                    self.prev_is_line,
                    curr_is_line,
                    self.builders(),
                );
                self.outer.close();
                let pt = self.inner.last_point().unwrap_or_default();
                self.outer.move_to(pt.x, pt.y);
                self.outer.reverse_path_to(&self.inner);
                self.outer.close();
            } else {
                let pt = self.inner.last_point().unwrap_or_default();
                let other_path = if curr_is_line {
                    Some(&self.inner)
                } else {
                    None
                };
                (self.capper)(
                    self.prev_pt,
                    self.prev_normal,
                    pt,
                    other_path,
                    &mut self.outer,
                );
                self.outer.reverse_path_to(&self.inner);
                let other_path = if self.prev_is_line {
                    Some(&self.inner)
                } else {
                    None
                };
                (self.capper)(
                    self.first_pt,
                    -self.first_normal,
                    self.first_outer_pt,
                    other_path,
                    &mut self.outer,
                );
                self.outer.close();
            }
            if !self.cusper.is_empty() {
                self.outer.push_path(&self.cusper);
                self.cusper.clear();
            }
        }
        self.inner.clear();
        self.segment_count = -1;
        self.first_outer_pt_index_in_contour = self.outer.points.len();
    }
    fn pre_join_to(
        &mut self,
        p: Point,
        curr_is_line: bool,
        normal: &mut Point,
        unit_normal: &mut Point,
    ) -> bool {
        debug_assert!(self.segment_count >= 0);
        let prev_x = self.prev_pt.x;
        let prev_y = self.prev_pt.y;
        let normal_set = set_normal_unit_normal(
            self.prev_pt,
            p,
            self.res_scale,
            self.radius,
            normal,
            unit_normal,
        );
        if !normal_set {
            if fn_ptr_eq(self.capper, butt_capper) {
                return false;
            }
            *normal = Point::from_xy(self.radius, 0.0);
            *unit_normal = Point::from_xy(1.0, 0.0);
        }
        if self.segment_count == 0 {
            self.first_normal = *normal;
            self.first_unit_normal = *unit_normal;
            self.first_outer_pt = Point::from_xy(prev_x + normal.x, prev_y + normal.y);
            self.outer
                .move_to(self.first_outer_pt.x, self.first_outer_pt.y);
            self.inner.move_to(prev_x - normal.x, prev_y - normal.y);
        } else {
            (self.joiner)(
                self.prev_unit_normal,
                self.prev_pt,
                *unit_normal,
                self.radius,
                self.inv_miter_limit,
                self.prev_is_line,
                curr_is_line,
                self.builders(),
            );
        }
        self.prev_is_line = curr_is_line;
        true
    }
    fn post_join_to(&mut self, p: Point, normal: Point, unit_normal: Point) {
        self.join_completed = true;
        self.prev_pt = p;
        self.prev_unit_normal = unit_normal;
        self.prev_normal = normal;
        self.segment_count += 1;
    }
    fn init_quad(
        &mut self,
        stroke_type: StrokeType,
        start: NormalizedF32,
        end: NormalizedF32,
        quad_points: &mut QuadConstruct,
    ) {
        self.stroke_type = stroke_type;
        self.found_tangents = false;
        quad_points.init(start, end);
    }
    fn quad_stroke(&mut self, quad: &[Point; 3], quad_points: &mut QuadConstruct) -> bool {
        let result_type = self.compare_quad_quad(quad, quad_points);
        if result_type == ResultType::Quad {
            let path = if self.stroke_type == StrokeType::Outer {
                &mut self.outer
            } else {
                &mut self.inner
            };
            path.quad_to(
                quad_points.quad[1].x,
                quad_points.quad[1].y,
                quad_points.quad[2].x,
                quad_points.quad[2].y,
            );
            return true;
        }
        if result_type == ResultType::Degenerate {
            self.add_degenerate_line(quad_points);
            return true;
        }
        self.recursion_depth += 1;
        if self.recursion_depth > RECURSIVE_LIMITS[QUAD_RECURSIVE_LIMIT] {
            return false; }
        let mut half = QuadConstruct::default();
        half.init_with_start(quad_points);
        if !self.quad_stroke(quad, &mut half) {
            return false;
        }
        half.init_with_end(quad_points);
        if !self.quad_stroke(quad, &mut half) {
            return false;
        }
        self.recursion_depth -= 1;
        true
    }
    fn compare_quad_quad(
        &mut self,
        quad: &[Point; 3],
        quad_points: &mut QuadConstruct,
    ) -> ResultType {
        if !quad_points.start_set {
            let mut quad_start_pt = Point::zero();
            self.quad_perp_ray(
                quad,
                quad_points.start_t,
                &mut quad_start_pt,
                &mut quad_points.quad[0],
                Some(&mut quad_points.tangent_start),
            );
            quad_points.start_set = true;
        }
        if !quad_points.end_set {
            let mut quad_end_pt = Point::zero();
            self.quad_perp_ray(
                quad,
                quad_points.end_t,
                &mut quad_end_pt,
                &mut quad_points.quad[2],
                Some(&mut quad_points.tangent_end),
            );
            quad_points.end_set = true;
        }
        let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
        if result_type != ResultType::Quad {
            return result_type;
        }
        let mut ray0 = Point::zero();
        let mut ray1 = Point::zero();
        self.quad_perp_ray(quad, quad_points.mid_t, &mut ray1, &mut ray0, None);
        self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
    }
    fn set_ray_points(
        &self,
        tp: Point,
        dxy: &mut Point,
        on_p: &mut Point,
        mut tangent: Option<&mut Point>,
    ) {
        if !dxy.set_length(self.radius) {
            *dxy = Point::from_xy(self.radius, 0.0);
        }
        let axis_flip = self.stroke_type as i32 as f32; on_p.x = tp.x + axis_flip * dxy.y;
        on_p.y = tp.y - axis_flip * dxy.x;
        if let Some(ref mut tangent) = tangent {
            tangent.x = on_p.x + dxy.x;
            tangent.y = on_p.y + dxy.y;
        }
    }
    fn quad_perp_ray(
        &self,
        quad: &[Point; 3],
        t: NormalizedF32,
        tp: &mut Point,
        on_p: &mut Point,
        tangent: Option<&mut Point>,
    ) {
        *tp = path_geometry::eval_quad_at(quad, t);
        let mut dxy = path_geometry::eval_quad_tangent_at(quad, t);
        if dxy.is_zero() {
            dxy = quad[2] - quad[0];
        }
        self.set_ray_points(*tp, &mut dxy, on_p, tangent);
    }
    fn add_degenerate_line(&mut self, quad_points: &QuadConstruct) {
        if self.stroke_type == StrokeType::Outer {
            self.outer
                .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
        } else {
            self.inner
                .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
        }
    }
    fn stroke_close_enough(
        &self,
        stroke: &[Point; 3],
        ray: &[Point; 2],
        quad_points: &mut QuadConstruct,
    ) -> ResultType {
        let half = NormalizedF32::new_clamped(0.5);
        let stroke_mid = path_geometry::eval_quad_at(stroke, half);
        if points_within_dist(ray[0], stroke_mid, self.inv_res_scale) {
            if sharp_angle(&quad_points.quad) {
                return ResultType::Split;
            }
            return ResultType::Quad;
        }
        if !pt_in_quad_bounds(stroke, ray[0], self.inv_res_scale) {
            return ResultType::Split;
        }
        let mut roots = path_geometry::new_t_values();
        let roots = intersect_quad_ray(ray, stroke, &mut roots);
        if roots.len() != 1 {
            return ResultType::Split;
        }
        let quad_pt = path_geometry::eval_quad_at(stroke, roots[0].to_normalized());
        let error = self.inv_res_scale * (1.0 - (roots[0].get() - 0.5).abs() * 2.0);
        if points_within_dist(ray[0], quad_pt, error) {
            if sharp_angle(&quad_points.quad) {
                return ResultType::Split;
            }
            return ResultType::Quad;
        }
        ResultType::Split
    }
    fn intersect_ray(
        &self,
        intersect_ray_type: IntersectRayType,
        quad_points: &mut QuadConstruct,
    ) -> ResultType {
        let start = quad_points.quad[0];
        let end = quad_points.quad[2];
        let a_len = quad_points.tangent_start - start;
        let b_len = quad_points.tangent_end - end;
        let denom = a_len.cross(b_len);
        if denom == 0.0 || !denom.is_finite() {
            quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
            return ResultType::Degenerate;
        }
        quad_points.opposite_tangents = false;
        let ab0 = start - end;
        let mut numer_a = b_len.cross(ab0);
        let numer_b = a_len.cross(ab0);
        if (numer_a >= 0.0) == (numer_b >= 0.0) {
            let dist1 = pt_to_line(start, end, quad_points.tangent_end);
            let dist2 = pt_to_line(end, start, quad_points.tangent_start);
            if dist1.max(dist2) <= self.inv_res_scale_squared {
                return ResultType::Degenerate;
            }
            return ResultType::Split;
        }
        numer_a /= denom;
        let valid_divide = numer_a > numer_a - 1.0;
        if valid_divide {
            if intersect_ray_type == IntersectRayType::CtrlPt {
                quad_points.quad[1].x =
                    start.x * (1.0 - numer_a) + quad_points.tangent_start.x * numer_a;
                quad_points.quad[1].y =
                    start.y * (1.0 - numer_a) + quad_points.tangent_start.y * numer_a;
            }
            return ResultType::Quad;
        }
        quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
        ResultType::Degenerate
    }
    fn tangents_meet(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> ResultType {
        self.cubic_quad_ends(cubic, quad_points);
        self.intersect_ray(IntersectRayType::ResultType, quad_points)
    }
    fn finish(&mut self, is_line: bool) -> Option<Path> {
        self.finish_contour(false, is_line);
        let mut buf = PathBuilder::new();
        core::mem::swap(&mut self.outer, &mut buf);
        buf.finish()
    }
    fn has_only_move_to(&self) -> bool {
        self.segment_count == 0
    }
    fn is_current_contour_empty(&self) -> bool {
        self.inner.is_zero_length_since_point(0)
            && self
                .outer
                .is_zero_length_since_point(self.first_outer_pt_index_in_contour)
    }
}
fn cap_factory(cap: LineCap) -> CapProc {
    match cap {
        LineCap::Butt => butt_capper,
        LineCap::Round => round_capper,
        LineCap::Square => square_capper,
    }
}
fn butt_capper(_: Point, _: Point, stop: Point, _: Option<&PathBuilder>, path: &mut PathBuilder) {
    path.line_to(stop.x, stop.y);
}
fn round_capper(
    pivot: Point,
    normal: Point,
    stop: Point,
    _: Option<&PathBuilder>,
    path: &mut PathBuilder,
) {
    let mut parallel = normal;
    parallel.rotate_cw();
    let projected_center = pivot + parallel;
    path.conic_points_to(
        projected_center + normal,
        projected_center,
        SCALAR_ROOT_2_OVER_2,
    );
    path.conic_points_to(projected_center - normal, stop, SCALAR_ROOT_2_OVER_2);
}
fn square_capper(
    pivot: Point,
    normal: Point,
    stop: Point,
    other_path: Option<&PathBuilder>,
    path: &mut PathBuilder,
) {
    let mut parallel = normal;
    parallel.rotate_cw();
    if other_path.is_some() {
        path.set_last_point(Point::from_xy(
            pivot.x + normal.x + parallel.x,
            pivot.y + normal.y + parallel.y,
        ));
        path.line_to(
            pivot.x - normal.x + parallel.x,
            pivot.y - normal.y + parallel.y,
        );
    } else {
        path.line_to(
            pivot.x + normal.x + parallel.x,
            pivot.y + normal.y + parallel.y,
        );
        path.line_to(
            pivot.x - normal.x + parallel.x,
            pivot.y - normal.y + parallel.y,
        );
        path.line_to(stop.x, stop.y);
    }
}
fn join_factory(join: LineJoin) -> JoinProc {
    match join {
        LineJoin::Miter => miter_joiner,
        LineJoin::Round => round_joiner,
        LineJoin::Bevel => bevel_joiner,
    }
}
fn is_clockwise(before: Point, after: Point) -> bool {
    before.x * after.y > before.y * after.x
}
#[derive(Copy, Clone, PartialEq, Debug)]
enum AngleType {
    Nearly180,
    Sharp,
    Shallow,
    NearlyLine,
}
fn dot_to_angle_type(dot: f32) -> AngleType {
    if dot >= 0.0 {
        if (1.0 - dot).is_nearly_zero() {
            AngleType::NearlyLine
        } else {
            AngleType::Shallow
        }
    } else {
        if (1.0 + dot).is_nearly_zero() {
            AngleType::Nearly180
        } else {
            AngleType::Sharp
        }
    }
}
fn handle_inner_join(pivot: Point, after: Point, inner: &mut PathBuilder) {
    inner.line_to(pivot.x, pivot.y);
    inner.line_to(pivot.x - after.x, pivot.y - after.y);
}
fn bevel_joiner(
    before_unit_normal: Point,
    pivot: Point,
    after_unit_normal: Point,
    radius: f32,
    _: f32,
    _: bool,
    _: bool,
    mut builders: SwappableBuilders,
) {
    let mut after = after_unit_normal.scaled(radius);
    if !is_clockwise(before_unit_normal, after_unit_normal) {
        builders.swap();
        after = -after;
    }
    builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
    handle_inner_join(pivot, after, builders.inner);
}
fn round_joiner(
    before_unit_normal: Point,
    pivot: Point,
    after_unit_normal: Point,
    radius: f32,
    _: f32,
    _: bool,
    _: bool,
    mut builders: SwappableBuilders,
) {
    let dot_prod = before_unit_normal.dot(after_unit_normal);
    let angle_type = dot_to_angle_type(dot_prod);
    if angle_type == AngleType::NearlyLine {
        return;
    }
    let mut before = before_unit_normal;
    let mut after = after_unit_normal;
    let mut dir = PathDirection::CW;
    if !is_clockwise(before, after) {
        builders.swap();
        before = -before;
        after = -after;
        dir = PathDirection::CCW;
    }
    let ts = Transform::from_row(radius, 0.0, 0.0, radius, pivot.x, pivot.y);
    let mut conics = [path_geometry::Conic::default(); 5];
    let conics = path_geometry::Conic::build_unit_arc(before, after, dir, ts, &mut conics);
    if let Some(conics) = conics {
        for conic in conics {
            builders
                .outer
                .conic_points_to(conic.points[1], conic.points[2], conic.weight);
        }
        after.scale(radius);
        handle_inner_join(pivot, after, builders.inner);
    }
}
fn miter_joiner(
    before_unit_normal: Point,
    pivot: Point,
    after_unit_normal: Point,
    radius: f32,
    inv_miter_limit: f32,
    prev_is_line: bool,
    mut curr_is_line: bool,
    mut builders: SwappableBuilders,
) {
    fn do_blunt(
        builders: SwappableBuilders,
        pivot: Point,
        radius: f32,
        curr_is_line: bool,
        mut after: Point,
    ) {
        after.scale(radius);
        if !curr_is_line {
            builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
        }
        handle_inner_join(pivot, after, builders.inner);
    }
    fn do_miter(
        builders: SwappableBuilders,
        pivot: Point,
        radius: f32,
        prev_is_line: bool,
        curr_is_line: bool,
        mid: Point,
        after: Point,
    ) {
        if prev_is_line {
            builders
                .outer
                .set_last_point(Point::from_xy(pivot.x + mid.x, pivot.y + mid.y));
        } else {
            builders.outer.line_to(pivot.x + mid.x, pivot.y + mid.y);
        }
        do_blunt(builders, pivot, radius, curr_is_line, after);
    }
    let dot_prod = before_unit_normal.dot(after_unit_normal);
    let angle_type = dot_to_angle_type(dot_prod);
    let mut before = before_unit_normal;
    let mut after = after_unit_normal;
    let mut mid;
    if angle_type == AngleType::NearlyLine {
        return;
    }
    if angle_type == AngleType::Nearly180 {
        curr_is_line = false;
        do_blunt(builders, pivot, radius, curr_is_line, after);
        return;
    }
    let ccw = !is_clockwise(before, after);
    if ccw {
        builders.swap();
        before = -before;
        after = -after;
    }
    if dot_prod == 0.0 && inv_miter_limit <= SCALAR_ROOT_2_OVER_2 {
        mid = (before + after).scaled(radius);
        do_miter(
            builders,
            pivot,
            radius,
            prev_is_line,
            curr_is_line,
            mid,
            after,
        );
        return;
    }
    let sin_half_angle = (1.0 + dot_prod).half().sqrt();
    if sin_half_angle < inv_miter_limit {
        curr_is_line = false;
        do_blunt(builders, pivot, radius, curr_is_line, after);
        return;
    }
    if angle_type == AngleType::Sharp {
        mid = Point::from_xy(after.y - before.y, before.x - after.x);
        if ccw {
            mid = -mid;
        }
    } else {
        mid = Point::from_xy(before.x + after.x, before.y + after.y);
    }
    mid.set_length(radius / sin_half_angle);
    do_miter(
        builders,
        pivot,
        radius,
        prev_is_line,
        curr_is_line,
        mid,
        after,
    );
}
fn set_normal_unit_normal(
    before: Point,
    after: Point,
    scale: f32,
    radius: f32,
    normal: &mut Point,
    unit_normal: &mut Point,
) -> bool {
    if !unit_normal.set_normalize((after.x - before.x) * scale, (after.y - before.y) * scale) {
        return false;
    }
    unit_normal.rotate_ccw();
    *normal = unit_normal.scaled(radius);
    true
}
fn set_normal_unit_normal2(
    vec: Point,
    radius: f32,
    normal: &mut Point,
    unit_normal: &mut Point,
) -> bool {
    if !unit_normal.set_normalize(vec.x, vec.y) {
        return false;
    }
    unit_normal.rotate_ccw();
    *normal = unit_normal.scaled(radius);
    true
}
fn fn_ptr_eq(f1: CapProc, f2: CapProc) -> bool {
    core::ptr::eq(f1 as *const (), f2 as *const ())
}
#[derive(Debug)]
struct QuadConstruct {
    quad: [Point; 3],       tangent_start: Point,   tangent_end: Point,     start_t: NormalizedF32, mid_t: NormalizedF32,
    end_t: NormalizedF32,
    start_set: bool, end_set: bool,
    opposite_tangents: bool, }
impl Default for QuadConstruct {
    fn default() -> Self {
        Self {
            quad: Default::default(),
            tangent_start: Point::default(),
            tangent_end: Point::default(),
            start_t: NormalizedF32::ZERO,
            mid_t: NormalizedF32::ZERO,
            end_t: NormalizedF32::ZERO,
            start_set: false,
            end_set: false,
            opposite_tangents: false,
        }
    }
}
impl QuadConstruct {
    fn init(&mut self, start: NormalizedF32, end: NormalizedF32) -> bool {
        self.start_t = start;
        self.mid_t = NormalizedF32::new_clamped((start.get() + end.get()).half());
        self.end_t = end;
        self.start_set = false;
        self.end_set = false;
        self.start_t < self.mid_t && self.mid_t < self.end_t
    }
    fn init_with_start(&mut self, parent: &Self) -> bool {
        if !self.init(parent.start_t, parent.mid_t) {
            return false;
        }
        self.quad[0] = parent.quad[0];
        self.tangent_start = parent.tangent_start;
        self.start_set = true;
        true
    }
    fn init_with_end(&mut self, parent: &Self) -> bool {
        if !self.init(parent.mid_t, parent.end_t) {
            return false;
        }
        self.quad[2] = parent.quad[2];
        self.tangent_end = parent.tangent_end;
        self.end_set = true;
        true
    }
}
fn check_quad_linear(quad: &[Point; 3]) -> (Point, ReductionType) {
    let degenerate_ab = degenerate_vector(quad[1] - quad[0]);
    let degenerate_bc = degenerate_vector(quad[2] - quad[1]);
    if degenerate_ab & degenerate_bc {
        return (Point::zero(), ReductionType::Point);
    }
    if degenerate_ab | degenerate_bc {
        return (Point::zero(), ReductionType::Line);
    }
    if !quad_in_line(quad) {
        return (Point::zero(), ReductionType::Quad);
    }
    let t = path_geometry::find_quad_max_curvature(quad);
    if t == NormalizedF32::ZERO || t == NormalizedF32::ONE {
        return (Point::zero(), ReductionType::Line);
    }
    (
        path_geometry::eval_quad_at(quad, t),
        ReductionType::Degenerate,
    )
}
fn degenerate_vector(v: Point) -> bool {
    !v.can_normalize()
}
fn quad_in_line(quad: &[Point; 3]) -> bool {
    let mut pt_max = -1.0;
    let mut outer1 = 0;
    let mut outer2 = 0;
    for index in 0..2 {
        for inner in index + 1..3 {
            let test_diff = quad[inner] - quad[index];
            let test_max = test_diff.x.abs().max(test_diff.y.abs());
            if pt_max < test_max {
                outer1 = index;
                outer2 = inner;
                pt_max = test_max;
            }
        }
    }
    debug_assert!(outer1 <= 1);
    debug_assert!(outer2 >= 1 && outer2 <= 2);
    debug_assert!(outer1 < outer2);
    let mid = outer1 ^ outer2 ^ 3;
    const CURVATURE_SLOP: f32 = 0.000005; let line_slop = pt_max * pt_max * CURVATURE_SLOP;
    pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= line_slop
}
fn pt_to_line(pt: Point, line_start: Point, line_end: Point) -> f32 {
    let dxy = line_end - line_start;
    let ab0 = pt - line_start;
    let numer = dxy.dot(ab0);
    let denom = dxy.dot(dxy);
    let t = numer / denom;
    if t >= 0.0 && t <= 1.0 {
        let hit = Point::from_xy(
            line_start.x * (1.0 - t) + line_end.x * t,
            line_start.y * (1.0 - t) + line_end.y * t,
        );
        hit.distance_to_sqd(pt)
    } else {
        pt.distance_to_sqd(line_start)
    }
}
fn intersect_quad_ray<'a>(
    line: &[Point; 2],
    quad: &[Point; 3],
    roots: &'a mut [NormalizedF32Exclusive; 3],
) -> &'a [NormalizedF32Exclusive] {
    let vec = line[1] - line[0];
    let mut r = [0.0; 3];
    for n in 0..3 {
        r[n] = (quad[n].y - line[0].y) * vec.x - (quad[n].x - line[0].x) * vec.y;
    }
    let mut a = r[2];
    let mut b = r[1];
    let c = r[0];
    a += c - 2.0 * b; b -= c; let len = path_geometry::find_unit_quad_roots(a, 2.0 * b, c, roots);
    &roots[0..len]
}
fn points_within_dist(near_pt: Point, far_pt: Point, limit: f32) -> bool {
    near_pt.distance_to_sqd(far_pt) <= limit * limit
}
fn sharp_angle(quad: &[Point; 3]) -> bool {
    let mut smaller = quad[1] - quad[0];
    let mut larger = quad[1] - quad[2];
    let smaller_len = smaller.length_sqd();
    let mut larger_len = larger.length_sqd();
    if smaller_len > larger_len {
        core::mem::swap(&mut smaller, &mut larger);
        larger_len = smaller_len;
    }
    if !smaller.set_length(larger_len) {
        return false;
    }
    let dot = smaller.dot(larger);
    dot > 0.0
}
fn pt_in_quad_bounds(quad: &[Point; 3], pt: Point, inv_res_scale: f32) -> bool {
    let x_min = quad[0].x.min(quad[1].x).min(quad[2].x);
    if pt.x + inv_res_scale < x_min {
        return false;
    }
    let x_max = quad[0].x.max(quad[1].x).max(quad[2].x);
    if pt.x - inv_res_scale > x_max {
        return false;
    }
    let y_min = quad[0].y.min(quad[1].y).min(quad[2].y);
    if pt.y + inv_res_scale < y_min {
        return false;
    }
    let y_max = quad[0].y.max(quad[1].y).max(quad[2].y);
    if pt.y - inv_res_scale > y_max {
        return false;
    }
    true
}
fn check_cubic_linear(
    cubic: &[Point; 4],
    reduction: &mut [Point; 3],
    tangent_pt: Option<&mut Point>,
) -> ReductionType {
    let degenerate_ab = degenerate_vector(cubic[1] - cubic[0]);
    let degenerate_bc = degenerate_vector(cubic[2] - cubic[1]);
    let degenerate_cd = degenerate_vector(cubic[3] - cubic[2]);
    if degenerate_ab & degenerate_bc & degenerate_cd {
        return ReductionType::Point;
    }
    if degenerate_ab as i32 + degenerate_bc as i32 + degenerate_cd as i32 == 2 {
        return ReductionType::Line;
    }
    if !cubic_in_line(cubic) {
        if let Some(tangent_pt) = tangent_pt {
            *tangent_pt = if degenerate_ab { cubic[2] } else { cubic[1] };
        }
        return ReductionType::Quad;
    }
    let mut t_values = [NormalizedF32::ZERO; 3];
    let t_values = path_geometry::find_cubic_max_curvature(cubic, &mut t_values);
    let mut r_count = 0;
    for t in t_values {
        if 0.0 >= t.get() || t.get() >= 1.0 {
            continue;
        }
        reduction[r_count] = path_geometry::eval_cubic_pos_at(cubic, *t);
        if reduction[r_count] != cubic[0] && reduction[r_count] != cubic[3] {
            r_count += 1;
        }
    }
    match r_count {
        0 => ReductionType::Line,
        1 => ReductionType::Degenerate,
        2 => ReductionType::Degenerate2,
        3 => ReductionType::Degenerate3,
        _ => unreachable!(),
    }
}
fn cubic_in_line(cubic: &[Point; 4]) -> bool {
    let mut pt_max = -1.0;
    let mut outer1 = 0;
    let mut outer2 = 0;
    for index in 0..3 {
        for inner in index + 1..4 {
            let test_diff = cubic[inner] - cubic[index];
            let test_max = test_diff.x.abs().max(test_diff.y.abs());
            if pt_max < test_max {
                outer1 = index;
                outer2 = inner;
                pt_max = test_max;
            }
        }
    }
    debug_assert!(outer1 <= 2);
    debug_assert!(outer2 >= 1 && outer2 <= 3);
    debug_assert!(outer1 < outer2);
    let mid1 = (1 + (2 >> outer2)) >> outer1;
    debug_assert!(mid1 <= 2);
    debug_assert!(outer1 != mid1 && outer2 != mid1);
    let mid2 = outer1 ^ outer2 ^ mid1;
    debug_assert!(mid2 >= 1 && mid2 <= 3);
    debug_assert!(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
    debug_assert!(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
    let line_slop = pt_max * pt_max * 0.00001; pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= line_slop
        && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= line_slop
}
#[rustfmt::skip]
#[cfg(test)]
mod tests {
    use super::*;
    impl PathSegment {
        fn new_move_to(x: f32, y: f32) -> Self {
            PathSegment::MoveTo(Point::from_xy(x, y))
        }
        fn new_line_to(x: f32, y: f32) -> Self {
            PathSegment::LineTo(Point::from_xy(x, y))
        }
        fn new_close() -> Self {
            PathSegment::Close
        }
    }
    #[test]
    fn auto_close() {
        let mut pb = PathBuilder::new();
        pb.move_to(10.0, 10.0);
        pb.line_to(20.0, 50.0);
        pb.line_to(30.0, 10.0);
        pb.close();
        let path = pb.finish().unwrap();
        let stroke = Stroke::default();
        let stroke_path = PathStroker::new().stroke(&path, &stroke, 1.0).unwrap();
        let mut iter = stroke_path.segments();
        iter.set_auto_close(true);
        assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(10.485071, 9.878732));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 49.878731));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.0, 50.0));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 49.878731));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(29.514929, 9.878732));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.0));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.5));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.5));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.0));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.485071, 9.878732));
        assert_eq!(iter.next().unwrap(), PathSegment::new_close());
        assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(9.3596115, 9.5));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.640388, 9.5));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 50.121269));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 50.121269));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.514929, 10.121268));
        assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.3596115, 9.5));
        assert_eq!(iter.next().unwrap(), PathSegment::new_close());
    }
    #[test]
    fn cubic_1() {
        let mut pb = PathBuilder::new();
        pb.move_to(51.0161362, 1511.52478);
        pb.cubic_to(
            51.0161362, 1511.52478,
            51.0161362, 1511.52478,
            51.0161362, 1511.52478,
        );
        let path = pb.finish().unwrap();
        let mut stroke = Stroke::default();
        stroke.width = 0.394537568;
        assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_none());
    }
    #[test]
    fn cubic_2() {
        let mut pb = PathBuilder::new();
        pb.move_to(f32::from_bits(0x424c1086), f32::from_bits(0x44bcf0cb)); pb.cubic_to(
            f32::from_bits(0x424c107c), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c10c2), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c1119), f32::from_bits(0x44bcf0ca), );
        let path = pb.finish().unwrap();
        let mut stroke = Stroke::default();
        stroke.width = 0.394537568;
        assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
    }
    #[test]
    fn big() {
        let mut pb = PathBuilder::new();
        pb.move_to(f32::from_bits(0x46380000), f32::from_bits(0xc6380000)); pb.line_to(f32::from_bits(0x46a00000), f32::from_bits(0xc6a00000)); pb.line_to(f32::from_bits(0x468c0000), f32::from_bits(0xc68c0000)); pb.line_to(f32::from_bits(0x46100000), f32::from_bits(0xc6100000)); pb.line_to(f32::from_bits(0x46380000), f32::from_bits(0xc6380000)); pb.close();
        let path = pb.finish().unwrap();
        let mut stroke = Stroke::default();
        stroke.width = 1.49679073e+10;
        assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
    }
    #[test]
    fn quad_stroker_one_off() {
        let mut pb = PathBuilder::new();
        pb.move_to(f32::from_bits(0x43c99223), f32::from_bits(0x42b7417e));
        pb.quad_to(
            f32::from_bits(0x4285d839), f32::from_bits(0x43ed6645),
            f32::from_bits(0x43c941c8), f32::from_bits(0x42b3ace3),
        );
        let path = pb.finish().unwrap();
        let mut stroke = Stroke::default();
        stroke.width = 164.683548;
        assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
    }
    #[test]
    fn cubic_stroker_one_off() {
        let mut pb = PathBuilder::new();
        pb.move_to(f32::from_bits(0x433f5370), f32::from_bits(0x43d1f4b3));
        pb.cubic_to(
            f32::from_bits(0x4331cb76), f32::from_bits(0x43ea3340),
            f32::from_bits(0x4388f498), f32::from_bits(0x42f7f08d),
            f32::from_bits(0x43f1cd32), f32::from_bits(0x42802ec1),
        );
        let path = pb.finish().unwrap();
        let mut stroke = Stroke::default();
        stroke.width = 42.835968;
        assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
    }
}