From d52b6224b88c00e0e33be21f766d38cbd013b5d7 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Thu, 23 Apr 2026 14:23:56 -0400 Subject: [PATCH] Use more circular rounding for WebGL rect corners --- src/webgl/3d_primitives.js | 15 ++++++++++----- test/unit/visual/cases/webgl.js | 11 +++++++++++ .../rect() rounded into a circle/000.png | Bin 0 -> 612 bytes .../metadata.json | 3 +++ .../on a rect with rounded corners/000.png | Bin 5254 -> 5201 bytes 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 test/unit/visual/screenshots/WebGL/2D Shapes/rect() rounded into a circle/000.png create mode 100644 test/unit/visual/screenshots/WebGL/2D Shapes/rect() rounded into a circle/metadata.json diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 5eb78db6c6..0d10bfdf4b 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -1792,33 +1792,38 @@ function primitives3D(p5, fn){ const prevMode = this.states.textureMode; this.states.setValue('textureMode', constants.NORMAL); const prevOrder = this.bezierOrder(); - this.bezierOrder(2); + this.bezierOrder(3); this.beginShape(); const addUVs = (x, y) => [x, y, 0, (x - x1)/width, (y - y1)/height]; + const rr = 0.5523; // kappa: 4*(sqrt(2)-1)/3, handle ratio for cubic bezier circle approximation if (tr !== 0) { this.vertex(...addUVs(x2 - tr, y1)); - this.bezierVertex(...addUVs(x2, y1)); + this.bezierVertex(...addUVs(x2 - tr + tr * rr, y1)); + this.bezierVertex(...addUVs(x2, y1 + tr - tr * rr)); this.bezierVertex(...addUVs(x2, y1 + tr)); } else { this.vertex(...addUVs(x2, y1)); } if (br !== 0) { this.vertex(...addUVs(x2, y2 - br)); - this.bezierVertex(...addUVs(x2, y2)); + this.bezierVertex(...addUVs(x2, y2 - br + br * rr)); + this.bezierVertex(...addUVs(x2 - br + rr * br, y2)); this.bezierVertex(...addUVs(x2 - br, y2)); } else { this.vertex(...addUVs(x2, y2)); } if (bl !== 0) { this.vertex(...addUVs(x1 + bl, y2)); - this.bezierVertex(...addUVs(x1, y2)); + this.bezierVertex(...addUVs(x1 + bl - bl * rr, y2)); + this.bezierVertex(...addUVs(x1, y2 - bl + bl * rr)); this.bezierVertex(...addUVs(x1, y2 - bl)); } else { this.vertex(...addUVs(x1, y2)); } if (tl !== 0) { this.vertex(...addUVs(x1, y1 + tl)); - this.bezierVertex(...addUVs(x1, y1)); + this.bezierVertex(...addUVs(x1, y1 + tl - tl * rr)); + this.bezierVertex(...addUVs(x1 + tl - tl * rr, y1)); this.bezierVertex(...addUVs(x1 + tl, y1)); } else { this.vertex(...addUVs(x1, y1)); diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index 0941c769cf..1c69ca83a3 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -1506,6 +1506,17 @@ visualSuite('WebGL', function() { }); }); + visualSuite('2D Shapes', function() { + visualTest('rect() rounded into a circle', function(p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + p5.background(255); + p5.noStroke(); + p5.fill('red'); + p5.rect(-20, -20, 40, 40, 20); + screenshot(); + }); + }); + visualSuite('3D Primitives', function() { visualTest('cylinder() renders correctly', function(p5, screenshot) { p5.createCanvas(100, 100, p5.WEBGL); diff --git a/test/unit/visual/screenshots/WebGL/2D Shapes/rect() rounded into a circle/000.png b/test/unit/visual/screenshots/WebGL/2D Shapes/rect() rounded into a circle/000.png new file mode 100644 index 0000000000000000000000000000000000000000..e5ec27fc3379fc0e9ef4cb5930f79562f92084ab GIT binary patch literal 612 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 LNkvXXu0mjfLHIcT literal 5254 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 M07*qoM6N<$f*ANaivR!s