From 5155fbcc5229b69c8bf5edd4631937ad87412377 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak Date: Fri, 31 Oct 2025 23:16:37 +0530 Subject: [PATCH 1/3] fix(webgl): normalize nearly identical vertices before tessellation --- src/webgl/ShapeBuilder.js | 26 ++++++++++++++++++++++++++ test/unit/visual/cases/webgl.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index 4b0099db50..30a5c2ce2f 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -313,6 +313,32 @@ 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 prevZ = contour[i - stride + 2]; + const currX = contour[i]; + const currY = contour[i + 1]; + const currZ = contour[i + 2]; + + if (Math.abs(currX - prevX) < epsilon) { + contour[i] = prevX; + } + if (Math.abs(currY - prevY) < epsilon) { + contour[i + 1] = prevY; + } + if (Math.abs(currZ - prevZ) < epsilon) { + contour[i + 2] = prevZ; + } + } + } + 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 670c5eda00..c35f4af420 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -693,4 +693,35 @@ visualSuite('WebGL', function() { screenshot(); }); }); + + visualSuite('Tessellation', function() { + visualTest('Handles nearly identical consecutive vertices', function(p5, screenshot) { + p5.createCanvas(100, 100, p5.WEBGL); + p5.pixelDensity(1); + p5.background(255); + p5.fill(0); + p5.noStroke(); + + // Contours with nearly identical consecutive vertices (as can occur with textToContours) + // Outer contour + p5.beginShape(); + p5.vertex(-30, -30, 0); + p5.vertex(30, -30, 0); + p5.vertex(30, 30, 0); + p5.vertex(-30, 30, 0); + + // Inner contour (hole) with nearly identical vertices + p5.beginContour(); + p5.vertex(-10, -10, 0); + p5.vertex(-10, 10, 0); + // This vertex has x coordinate almost equal to previous (10.00000001 vs 10) + p5.vertex(10.00000001, 10, 0); + p5.vertex(10, -10, 0); + p5.endContour(); + + p5.endShape(p5.CLOSE); + + screenshot(); + }); + }); }); From 291f70f567a528b3e5c527661f87b943e95c4e76 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak Date: Mon, 24 Nov 2025 12:46:04 +0530 Subject: [PATCH 2/3] fix(webgl): normalize nearly identical vertices before tessellation - Remove Z-axis normalization (only X/Y needed based on testing) - Update visual test to use textToContours() example from issue #8186 - Test now reproduces the actual bug that gets fixed Resolves #8186 --- src/webgl/ShapeBuilder.js | 5 ---- test/unit/visual/cases/webgl.js | 43 +++++++++++++++++---------------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index 30a5c2ce2f..124fa62bfa 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -322,10 +322,8 @@ export class ShapeBuilder { for (let i = stride; i < contour.length; i += stride) { const prevX = contour[i - stride]; const prevY = contour[i - stride + 1]; - const prevZ = contour[i - stride + 2]; const currX = contour[i]; const currY = contour[i + 1]; - const currZ = contour[i + 2]; if (Math.abs(currX - prevX) < epsilon) { contour[i] = prevX; @@ -333,9 +331,6 @@ export class ShapeBuilder { if (Math.abs(currY - prevY) < epsilon) { contour[i + 1] = prevY; } - if (Math.abs(currZ - prevZ) < epsilon) { - contour[i + 2] = prevZ; - } } } diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index c35f4af420..9e7bcf1d40 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -695,31 +695,32 @@ visualSuite('WebGL', function() { }); visualSuite('Tessellation', function() { - visualTest('Handles nearly identical consecutive vertices', function(p5, screenshot) { - p5.createCanvas(100, 100, p5.WEBGL); - p5.pixelDensity(1); + visualTest('Handles nearly identical consecutive vertices from textToContours', async function(p5, screenshot) { + p5.createCanvas(200, 200, p5.WEBGL); p5.background(255); p5.fill(0); p5.noStroke(); - // Contours with nearly identical consecutive vertices (as can occur with textToContours) - // Outer contour - p5.beginShape(); - p5.vertex(-30, -30, 0); - p5.vertex(30, -30, 0); - p5.vertex(30, 30, 0); - p5.vertex(-30, 30, 0); - - // Inner contour (hole) with nearly identical vertices - p5.beginContour(); - p5.vertex(-10, -10, 0); - p5.vertex(-10, 10, 0); - // This vertex has x coordinate almost equal to previous (10.00000001 vs 10) - p5.vertex(10.00000001, 10, 0); - p5.vertex(10, -10, 0); - p5.endContour(); - - p5.endShape(p5.CLOSE); + const font = await p5.loadFont('/unit/assets/Inconsolata-Bold.ttf'); + const contours = font.textToContours('p', 0, 0, 60); + + if (contours && contours.length > 0) { + p5.translate(-p5.width / 4, -p5.height / 4); + p5.beginShape(); + for (let contourIdx = 0; contourIdx < contours.length; contourIdx++) { + const contour = contours[contourIdx]; + if (contourIdx > 0) { + p5.beginContour(); + } + for (let i = 0; i < contour.length; i++) { + p5.vertex(contour[i].x, contour[i].y, 0); + } + if (contourIdx > 0) { + p5.endContour(); + } + } + p5.endShape(p5.CLOSE); + } screenshot(); }); From bd7011f0946945d255e60660d276fa5506d6dc3f Mon Sep 17 00:00:00 2001 From: Perminder Singh <127239756+perminder-17@users.noreply.github.com> Date: Tue, 25 Nov 2025 01:48:06 +0530 Subject: [PATCH 3/3] Fixing merge conflicts. --- test/unit/visual/cases/webgl.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index af3c32d10b..5f9cd27308 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -719,6 +719,9 @@ visualSuite('WebGL', function() { } p5.endShape(p5.CLOSE); } + screenshot(); + }); + }); visualSuite('textures in p5.strands', async () => { visualTest('uniformTexture() works', async (p5, screenshot) => {