Repository: typst/typst
File: crates/typst-library/src/visualize/curve.rs (Rust)

Code before:
    }
}

fn point_to_kurbo(point: Point) -> kurbo::Point {
    kurbo::Point::new(point.x.to_raw(), point.y.to_raw())
}

impl Curve {
    fn to_kurbo(&self) -> kurbo::BezPath {
        use kurbo::{BezPath, PathEl};

        let path = self.0.iter().map(|item| match *item {
            CurveItem::Move(point) => PathEl::MoveTo(point_to_kurbo(point)),
            CurveItem::Line(point) => PathEl::LineTo(point_to_kurbo(point)),
            CurveItem::Cubic(point, point1, point2) => PathEl::CurveTo(
                point_to_kurbo(point),
                point_to_kurbo(point1),
                point_to_kurbo(point2),
            ),
            CurveItem::Close => PathEl::ClosePath,
        });
        BezPath::from_vec(path.collect())
    }

    /// When this curve is interpreted as a clip mask, would it contain `point`?
    pub fn contains(&self, needle: Point) -> bool {
        kurbo::Shape::contains(&self.to_kurbo(), point_to_kurbo(needle))
    }
}


Review comment (suggestion):
Since we have this now, it might be a nice and simple addition (for a separate PR) to also handle paths in `jump_from_click`'s `FrameItem::Shape` case.

Code after:

        Size::new(max_x - min_x, max_y - min_y)
    }
}

impl Curve {
    fn to_kurbo(&self) -> impl Iterator<Item = kurbo::PathEl> + '_ {
        use kurbo::PathEl;

        self.0.iter().map(|item| match *item {
            CurveItem::Move(point) => PathEl::MoveTo(point_to_kurbo(point)),
            CurveItem::Line(point) => PathEl::LineTo(point_to_kurbo(point)),
            CurveItem::Cubic(point, point1, point2) => PathEl::CurveTo(
                point_to_kurbo(point),
                point_to_kurbo(point1),
                point_to_kurbo(point2),
            ),
            CurveItem::Close => PathEl::ClosePath,
        })
    }

    /// When this curve is interpreted as a clip mask, would it contain `point`?
    pub fn contains(&self, fill_rule: FillRule, needle: Point) -> bool {
        let kurbo = kurbo::BezPath::from_vec(self.to_kurbo().collect());
        let windings = kurbo::Shape::winding(&kurbo, point_to_kurbo(needle));
        match fill_rule {
            FillRule::NonZero => windings != 0,
            FillRule::EvenOdd => windings % 2 != 0,
        }
    }

    /// When this curve is stroked with `stroke`, would the stroke contain
    /// `point`?
    pub fn stroke_contains(&self, stroke: &FixedStroke, needle: Point) -> bool {
        let width = stroke.thickness.to_raw();
        let cap = match stroke.cap {
            super::LineCap::Butt => kurbo::Cap::Butt,
            super::LineCap::Round => kurbo::Cap::Round,
            super::LineCap::Square => kurbo::Cap::Square,
        };
        let join = match stroke.join {
            super::LineJoin::Miter => kurbo::Join::Miter,
            super::LineJoin::Round => kurbo::Join::Round,
            super::LineJoin::Bevel => kurbo::Join::Bevel,
        };
        let miter_limit = stroke.miter_limit.get();
        let mut style = kurbo::Stroke::new(width)
            .with_caps(cap)
            .with_join(join)
            .with_miter_limit(miter_limit);
        if let Some(dash) = &stroke.dash {