Skip to content

Numerical instability in winding number / contains. #531

@SimonHeidrich

Description

@SimonHeidrich

Hi there.
I've stumbled upon a very specific case where the contains check returns a wrong result if the test point sits right between the endpoint of two Bézier curves:

let bez = BezPath::from_vec(vec![
    PathEl::MoveTo((200.0, 410.0).into()), 
    PathEl::CurveTo((139.12278747558594, 410.0).into(), (90.0, 360.8772277832031).into(), (90.0, 300.0).into()), 
    PathEl::CurveTo((90.0, 239.12278747558594).into(), (139.12278747558594, 190.0).into(), (200.0, 190.0).into()),
    PathEl::CurveTo((150.19137573242188, 210.0).into(), (110.0, 250.19137573242188).into(), (110.0, 300.0).into()), 
    PathEl::CurveTo((110.0, 349.8086242675781).into(), (150.19137573242188, 390.0).into(), (200.0, 390.0).into()),
    PathEl::ClosePath]);

assert!(bez.contains((100.0,300.1).into())); // Works fine.
assert!(bez.contains((100.0,299.9).into())); // Works fine.
assert!(bez.contains((100.0,300.0).into())); // Fails.

I have tried simplifying more, but even if I remove the digits after the decimal points, the test produces the correct results instead of reproducing the issue.
Tried this in both debug and release.
I have no idea if there is an established way to numerically stablise this check. Kurbo can't be the first program to run into this issue.
From what I read, issue #180 is not directly related to this. But that's for the crate experts to decide. :)
All the best,
Simon

Bonus SVG code to quickly see that the point should indeed be contained:

<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 500 500" fill="none">
<path d="M200,410 C139.12278747558594,410 90,360.8772277832031 90,300 C90,239.12278747558594 139.12278747558594,190 200,190 C150.19137573242188,210 110,250.19137573242188 110,300 C110,349.8086242675781 150.19137573242188,390 200,390 Z" fill="red"/>
   <rect x="100" y="300" width="3" height="3" fill="#000"/>
</svg>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions