Skip to content

Commit

Permalink
fix: When lines reverse on themselves, mitering could be wrong
Browse files Browse the repository at this point in the history
If a line reversed direction exactly, mitering logic could render only
half the pixels that should have been rendered.  In general, when lines
nearly reverse on themselves, mitering is tricky.  This add some
thresholds, below which the lines aren't mitered but are just rendered.
For opaque lines, this is a strict improvement, though some joints may
no longer show rounded or truncated miters.  For semi-transparent lines,
this is in general an improvement, but there can be cases where they
appear double rendered instead of joined.  The behavior is still deemed
an improvement, as they should never have only have half the pixels
rendered.
  • Loading branch information
manthey committed Feb 12, 2025
1 parent 90ae5d5 commit 08373b4
Showing 1 changed file with 26 additions and 5 deletions.
31 changes: 26 additions & 5 deletions src/webgl/lineFeature.vert
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,27 @@ void main(void)
gl_Position = vec4(2.0, 2.0, 0.0, 1.0);
return;
}
float lineLength = length(vec2(deltaCB.x, deltaCB.y / aspect)) / pixelWidth;
// if lines reverse upon themselves and are not nearly the same length, skip
// joins.
float abLimit = length(vec2(A.x - B.x, (A.y - B.y) / aspect)) / pixelWidth;
float dcLimit = length(vec2(D.x - C.x, (D.y - C.y) / aspect)) / pixelWidth;
if (abLimit >= lineLength - antialiasing - strokeWidth * 0.5 && abLimit <= lineLength + antialiasing + strokeWidth * 0.5) {
abLimit = 0.0001;
} else {
if (abLimit < lineLength) abLimit = lineLength;
abLimit = (strokeWidth - antialiasing) / (abLimit + antialiasing);
if (abLimit < 0.0001) abLimit = 0.0001;
if (abLimit > 0.1) abLimit = 0.1;
}
if (dcLimit >= lineLength - antialiasing - strokeWidth * 0.5 && dcLimit <= lineLength + antialiasing + strokeWidth * 0.5) {
dcLimit = 0.0001;
} else {
if (dcLimit < lineLength) dcLimit = lineLength;
dcLimit = (strokeWidth - antialiasing) / (dcLimit + antialiasing);
if (dcLimit < 0.0001) dcLimit = 0.0001;
if (dcLimit > 0.1) dcLimit = 0.1;
}
float angleCB = atan2(deltaCB.y, deltaCB.x * aspect);
// values we need to pass along
strokeColorVar = vec4(strokeColor, strokeOpacity);
Expand All @@ -78,7 +99,7 @@ void main(void)
// by default, offset by the width and don't extend lines. Later,
// calculate line extensions based on end cap and end join modes
float yOffset = strokeWidth + antialiasing;
if (vertex == 0 || vertex == 2) yOffset *= -1.0;
if (vertex == 0) yOffset *= -1.0;
yOffset += strokeWidth * offset;
float xOffset = 0.0;
// end caps
Expand All @@ -105,7 +126,8 @@ void main(void)
angleABC = (mod(angleABC + 3.0 * PI, 2.0 * PI) - PI) / 2.0;
cosABC = cos(angleABC); sinABC = sin(angleABC);
// if this angle is close to flat, pass-through the join
if (nearMode >= 4 && cosABC > 0.999999) {
// if the line doubles back exactly, do the same
if (nearMode >= 4 && (cosABC > 0.999999 || cosABC < abLimit)) {
nearMode = 3;
}
// miter, miter-clip
Expand Down Expand Up @@ -138,7 +160,8 @@ void main(void)
angleBCD = (mod(angleBCD + 3.0 * PI, 2.0 * PI) - PI) / 2.0;
cosBCD = cos(angleBCD); sinBCD = sin(angleBCD);
// if this angle is close to flat, pass-through the join
if (farMode >= 4 && cosBCD > 0.999999) {
// if the line doubles back exactly, do the same
if (farMode >= 4 && (cosBCD > 0.999999 || cosBCD < dcLimit)) {
farMode = 3;
}
// miter, miter-clip
Expand All @@ -159,8 +182,6 @@ void main(void)
B.y + (xOffset * sin(angleCB) + yOffset * cos(angleCB)) * pixelWidth * aspect,
B.z, 1.0);
// store other values needed to determine which pixels to plot.
float lineLength = length(vec2(deltaCB.x, deltaCB.y / aspect)) / pixelWidth;

if (vertex == 0 || vertex == 1) {
subpos = vec4(xOffset, yOffset, lineLength - xOffset, strokeWidth);
info = vec4(float(nearMode), float(farMode), offset, 0.0);
Expand Down

0 comments on commit 08373b4

Please sign in to comment.