nical/lyon

Line tesselation renders not existing line back to origin (WGPU)

Closed this issue · 3 comments

Hi, I'm having some issue when tesselating (and rendering) lines using the StrokeTessellator with a SVG builder. Looking mostly for an explanation as it's likely that I'm setting something wrong or that there is some issue related to floating point rounding (where the original data cannot be represented).

I'm rendering some data points from OpenStreetMap using WGPU, and while buildings rendered with FillTessellator and closed paths seem to work fine, I'm seeing strange results when trying to render highways. As a simple example I have the following commands (implemented as a combination of move_to and line_to using a Path::builder().with_svg()):

M 850557.4428653744 5630569.199290542
L 850557.5095832518 5630562.99291237
L 850558.0766852108 5630544.783360512
L 850557.9321298096 5630464.778335465
L 850570.1637406882 5630391.515842587

wrong

Where it seems that there is an additional line that goes forever towards the origin.

If I try to normalize all the points and move them to the origin I get the expected representation:

M 0.0 0.0
L 0.06671787740197033 -6.206378172151744
L 0.6338198364246637 -24.415930029936135
L 0.4892644352512434 -104.4209550768137
L 12.720875313854776 -177.68344795517623

origin

It also seems to do the correct thing if I close the builder independently of the coordinate values (which is why I didn't notice this issue when rendering the buildings as closed polygons).

This is the path tesselation call:

stroke_tess
    .tessellate_path(
        &builder.build(),
        &StrokeOptions::DEFAULT,
        &mut BuffersBuilder::new(&mut vertex_buf, |vertex: StrokeVertex| {
            let position = vertex.position();
            Vertex {
                position: [position.x, position.y],
                color,
            }
        }),
    )
    .expect("should tesselate stroked path");
nical commented

Hi, thanks for filing this. It looks like a floating precision issue, although I haven't looked closely yet.

A reduced version of your test case shows the issue M 50557.4428653744 630569.199290542 L 50557.5095832518 630562.99291237

But it does not reproduce when offset by (50000, 600000) : M 557.4428653744 30569.199290542 L 557.5095832518 30562.99291237

Edit: There is likely more than float precision at play because it does not reproduce with round line caps.

nical commented

So it is indeed a float precision issue with a line intersection computed in tessellate_first_edge that is performed for some types of caps and not others. Using 64 bits floats for the intersection fixes the issue.

Thanks a lot for the quick fix!