diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index 4b0099db50..124fa62bfa 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -313,6 +313,27 @@ export class ShapeBuilder { } } + // Normalize nearly identical consecutive vertices to prevent tessellation artifacts + // This addresses numerical precision issues in libtess when consecutive vertices + // have coordinates that are almost (but not exactly) equal (e.g., differing by ~1e-8) + const epsilon = 1e-6; + for (const contour of contours) { + const stride = this.tessyVertexSize; + for (let i = stride; i < contour.length; i += stride) { + const prevX = contour[i - stride]; + const prevY = contour[i - stride + 1]; + const currX = contour[i]; + const currY = contour[i + 1]; + + if (Math.abs(currX - prevX) < epsilon) { + contour[i] = prevX; + } + if (Math.abs(currY - prevY) < epsilon) { + contour[i + 1] = prevY; + } + } + } + const polyTriangles = this._triangulate(contours); // If there were no valid faces, we still want to use the original vertices diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index 1b644a27a0..375a3ebfaf 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -1242,4 +1242,77 @@ visualSuite('WebGL', function() { screenshot(); }); }); + + visualSuite('Tessellation', function() { + visualTest('Handles nearly identical consecutive vertices', function(p5, screenshot) { + p5.createCanvas(400, 400, p5.WEBGL); + + const contours = [ + [ + [-3.8642425537109375, -6.120738636363637, 0], + [3.2025188099254267, -6.120738636363637, 0], + [3.2025188099254267, -4.345170454545455, 0], + [-3.8642425537109375, -4.345170454545455, 0], + [-3.8642425537109375, -6.120738636363637, 0] + ], + [ + [-1.8045834628018462, 4.177556818181818, 0], + [-1.8045834628018462, -9.387784090909093, 0], + [0.29058699174360836, -9.387784090909093, 0], + [0.2905869917436083, 3.609374411367136, 0], + [0.31044303036623855, 4.068235883781435, 0], + [0.38522861430307975, 4.522728865422799, 0], + [0.548044378107245, 4.941051136363637, 0], + [0.8364672032828204, 5.2932224887960775, 0], + [1.2227602871981542, 5.526988636363637, 0], + [1.6572258237923885, 5.634502949876295, 0], + [2.101666537198154, 5.669034090909091, 0], + [2.6695604948237173, 5.633568761673102, 0], + [3.0249619917436084, 5.5625, 0], + [3.4510983553799726, 7.4446022727272725, 0], + [2.8568950819856695, 7.613138889205699, 0], + [2.3751340936529037, 7.676962586830456, 0], + [1.8892600236717598, 7.693181792704519, 0], + [1.2922705720786674, 7.649533731133848, 0], + [0.7080836288276859, 7.519788939617751, 0], + [0.14854153719815422, 7.311434659090909, 0], + [-0.38643934048179873, 7.00959666478984, 0], + [-0.858113258144025, 6.61653855366859, 0], + [-1.25415732643821, 6.1484375, 0], + [-1.5108595282965422, 5.697682732328092, 0], + [-1.6824918355513252, 5.207533878495854, 0], + [-1.7762971052870198, 4.695933154267308, 0], + [-1.8045834628018462, 4.177556818181818, 0] + ] + ]; + + p5.background('red'); + p5.push(); + p5.stroke(0); + p5.fill('#EEE'); + p5.scale(15); + p5.beginShape(); + for (const contour of contours) { + p5.beginContour(); + for (const v of contour) { + p5.vertex(...v); + } + p5.endContour(); + } + p5.endShape(); + + p5.stroke(0, 255, 0); + p5.strokeWeight(5); + p5.beginShape(p5.POINTS); + for (const contour of contours) { + for (const v of contour) { + p5.vertex(...v); + } + } + p5.endShape(); + p5.pop(); + + screenshot(); + }); + }); }); diff --git a/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/000.png b/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/000.png new file mode 100644 index 0000000000..4cc83482af Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/metadata.json b/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/Tessellation/Handles nearly identical consecutive vertices/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file