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

Severe line-offset artifacts with non-miter joins #3809

Open
aaronlidman opened this issue Dec 15, 2016 · 4 comments
Open

Severe line-offset artifacts with non-miter joins #3809

aaronlidman opened this issue Dec 15, 2016 · 4 comments
Labels

Comments

@aaronlidman
Copy link

aaronlidman commented Dec 15, 2016

offset-yum

mapbox-gl-js version 0.28.0

Steps to Trigger Behavior

  1. set line-offset to > 0
  2. set line-round-limit to 1

Expected Behavior

Nicely rendered lines

Actual Behavior

Very artifacty lines.


Similar to #2316, but applies with any line-join.

cc @lucaswoj @jfirebaugh @pveugen

@lucaswoj
Copy link
Contributor

One potential fix: mapbox/mapbox-gl-style-spec#286

@1ec5
Copy link
Contributor

1ec5 commented Dec 23, 2016

We ran into a similar issue on iOS when we changed the default line-round-limit in gl-native to 1, so we reverted the default to 1.05: mapbox/mapbox-gl-native#5258.

@lbud
Copy link
Contributor

lbud commented Jan 30, 2017

I dug into this a bit last week (also, this and #2316 are more or less the same issue — same underlying problem, just present in different joins).

The crux of the issue is that miter joins are able to offset correctly because they contain only a single (well, two-way) join vertex, and its normal is the correct normal vector for that line vertex (that is, its normal is the sum of the normals of the lines that meet there). Our method of line offsetting is more or less just shifting it along its normal vertex. Here's a miter-joined line, offset, where you can see all line vertices are just shifted along their normals:

image

Now, a bevel join: The visual lines we draw are created with GL vertices with position attributes — that's the position of the center of the line — and then (simplified) data about the direction of their normal vertex (i.e. which way is perpendicular) and how wide to draw the line (how far out along the normal vertex do we visually draw). In bevel joins, we still draw the inner vertex at the true join normal (sum of the normals of both lines that meet there) but we draw two outer vertices, and calculate different normals to create a bevel. The problem is then when we offset all vertices in the direction of their normal vector, the bevel vertices go flying off in the wrong direction (this drawing isn't an exact reflection of what happens in mbgl, but conveys the general idea):

image

When what we need in all join cases is to offset all vertices involved in the direction of the line's actual join normal, which would look like this:

image

(Round line joins similarly employ modified normals and so suffer from the same problem.)

line-round-limit really just masks the problem by forcing round joins to render as miter joins. Additionally, this problem only becomes obvious at high offets because the funkiness is hidden within opaque lines, but it's technically present at any offset value.

To actually fix offset lines we would need to pack an additional join normal into all line vertices (so that we'd know both the render normal and the line join's true normal), which would cause some bloat… 🤔

@endanke
Copy link
Contributor

endanke commented Sep 28, 2022

Recently I investigated this issue a bit further, but I didn't get too far with the proposed workaround.

First, I noticed that we have issues even with miter joins at sharp corners. On the example below we rendered a polygon with a line layer, using miter joins. The top corner is miter with some resampling for sharp corners and the other two are flipBevel joins.

Screenshot 2022-09-28 at 9 19 18

The problems become visible when we apply some line offset:

Screenshot 2022-09-28 at 9 21 35

I found that we can skip the resampling of sharp corners when the join is miter which would fix the top corner. But this can be only used if the line is solid, otherwise if we use pattern, gradient, dash array, or trim offset, then the slanted shape of the sections become visible. See the illustration:

Solid line with no resampling
Screenshot 2022-09-28 at 9 29 29

Dashed line with resampling
Screenshot 2022-09-28 at 9 30 17

Dashed line with no resampling
Screenshot 2022-09-28 at 9 29 57

This fix could be applied if we limit it to solid lines, but the problem still stays for the bevel joins.

Next I tried the proposed workaround by @lbud, so I added a separate join normal to do the offsetting of the vertices. This seems to fix the bevel joins when the whole geometry is inside one tile, however when it crosses tile borders the lines get misaligned:

I suspect that the normal I used for the offset is not in a correct angle or might depend on other corners of the geometry which are not available in the left tile. I tried several configurations but couldn't get it working yet. Most likely using joinNormal is not enough, I think a separate normal needs to be calculated for each vertex, which would offset it with an angle that correctly follows the shape of the corner. My work in progress branch is here: https://github.com/mapbox/mapbox-gl-js/tree/endanke/fix-line-offset

@pozdnyakov also suggested to try masking the offsetted line with a smaller offset. This approach works in some cases, but also breaks on tile borders and if the line has self-intersecting parts.

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

No branches or pull requests

6 participants