Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting tangents and coordinates of evenly spaced points on kurbo::BezPath #338

Open
zdila opened this issue Feb 14, 2024 · 5 comments
Open

Comments

@zdila
Copy link

zdila commented Feb 14, 2024

Hello,

(How) Is it possible to get coordinates and tangents from kurbo::BezPath of evenly spaced points on a curve if I have a start offset and spacing? I would like to implement something like Mapnik's MarkersSymbolizer.

@platlas
Copy link
Collaborator

platlas commented Feb 14, 2024

One way could be: iterate over segments of BezPath and compare their arclen with desired distance obtained from initial offset and (accumulated) spacings.

This solution is probably bit naive for Bezier segments as pointed out here. But it should work as expected for purely linear paths, like ways in OpenStreetMap data.

use kurbo::{BezPath, ParamCurve, ParamCurveArclen, ParamCurveDeriv, PathSeg, Point, Vec2};

fn eval_spacing(path: &BezPath, start_offset: f64, spacing: f64) -> Vec<(Point, Vec2)> {
    let mut target_length = start_offset;
    let mut prev_length = 0.0;
    let mut result = vec![];

    for seg in path.segments() {
        let seg_length = seg.arclen(1e-3);

        // are we inside current segment?
        while (target_length - prev_length) <= seg_length {
            // local t parameter for segment in range 0.0..1.0
            let t = (target_length - prev_length) / seg_length;

            // obtain curve point and tangent
            let p = seg.eval(t);
            let tangent = match seg {
                PathSeg::Quad(s) => s.deriv().eval(t).to_vec2(),
                PathSeg::Cubic(s) => s.deriv().eval(t).to_vec2(),
                PathSeg::Line(s) => s.deriv().eval(t).to_vec2(),
            };
            result.push((p, tangent));

            // move to next spacing
            target_length += spacing;
        }
        // mark covered length
        prev_length += seg_length;
    }
    result
}

There is maybe better way to obtain tangent, but it is already late evening here >.>

Traits ParamCurve, ParamCurveArclen and ParamCurveDeriv are needed for eval, arclen and deriv functions.

@platlas
Copy link
Collaborator

platlas commented Feb 15, 2024

So I've forgot about inv_arclen method. That one should return correct t values even for Bezier segments.

So instead of line

 let t = (target_length - prev_length) / seg_length;

it should be:

let t = seg.inv_arclen(target_length - prev_length, 1e-3);

Obviously one could pass higher accuracy.

@zdila
Copy link
Author

zdila commented Feb 27, 2024

Thank you.

@simoncozens
Copy link
Collaborator

Traits ParamCurve, ParamCurveArclen and ParamCurveDeriv are needed for eval, arclen and deriv functions.

Some of these should be implementable for BezPath. Probably not ParamCurveDeriv, but certainly the others. Would there be any interest in having that happen?

@raphlinus
Copy link
Contributor

Implementing ParamCurveArclen for BezPath is going to be pretty inefficient, especially for the inverse arclengths (it will have to traverse the entire path). Fundamentally this problem is very much the same as the dash iterator for stroking, the best approach is probably to adapt that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants