From a5e15232e63fbfe087df0e1e0f444f8abeb0c77a Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Thu, 6 Nov 2025 13:06:49 +0530 Subject: [PATCH 1/4] Fix transforms in clip() by using shape system for primitives using manual method --- src/core/p5.Renderer2D.js | 42 +++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 95286b0b51..f0889ac2f8 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -70,14 +70,18 @@ class Renderer2D extends Renderer { } this.scale(this._pixelDensity, this._pixelDensity); - if(!this.filterRenderer){ - this.filterRenderer = new FilterRenderer2D(this); - } // Set and return p5.Element this.wrappedElt = new Element(this.elt, this._pInst); this.clipPath = null; } + get filterRenderer() { + if (!this._filterRenderer) { + this._filterRenderer = new FilterRenderer2D(this); + } + return this._filterRenderer; + } + remove(){ this.wrappedElt.remove(); this.wrappedElt = null; @@ -301,6 +305,7 @@ class Renderer2D extends Renderer { // Start a new path. Everything from here on out should become part of this // one path so that we can clip to the whole thing. this.clipPath = new Path2D(); + this.initialClipTransform = this.drawingContext.getTransform().inverse(); if (this._clipInvert) { // Slight hack: draw a big rectangle over everything with reverse winding @@ -701,7 +706,7 @@ class Renderer2D extends Renderer { } ellipse(args) { - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; const doFill = !!this.states.fillColor, doStroke = this.states.strokeColor; const x = parseFloat(args[0]), @@ -721,17 +726,28 @@ class Renderer2D extends Renderer { centerY = y + h / 2, radiusX = w / 2, radiusY = h / 2; - if (!this._clipping) ctx.beginPath(); - - ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); - ctx.closePath(); + if (this._clipping) { + const tempPath = new Path2D(); + tempPath.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); - if (!this._clipping && doFill) { - ctx.fill(); - } - if (!this._clipping && doStroke) { - ctx.stroke(); + const currentTransform = this.drawingContext.getTransform(); + const initialClip = this.initialClipTransform; + const relativeTransform = initialClip.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + } else { + // Normal drawing (existing code) + ctx.beginPath(); + ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); + ctx.closePath(); + if (doFill) { + ctx.fill(); + } + if (doStroke) { + ctx.stroke(); + } } + + return this; } line(x1, y1, x2, y2) { From cc98f19226242a80f86ccaac04b04483f10b6c91 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Wed, 12 Nov 2025 18:27:10 +0530 Subject: [PATCH 2/4] Handle negative scales in clipping by using absolute values for scale components --- src/core/p5.Renderer2D.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index f0889ac2f8..8cf36ae288 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -732,8 +732,19 @@ class Renderer2D extends Renderer { const currentTransform = this.drawingContext.getTransform(); const initialClip = this.initialClipTransform; - const relativeTransform = initialClip.multiply(currentTransform); - this.clipPath.addPath(tempPath, relativeTransform); + if (currentTransform.a < 0 || currentTransform.d < 0) { + const fixedTransform = new DOMMatrix([ + Math.abs(currentTransform.a), currentTransform.b, + currentTransform.c, Math.abs(currentTransform.d), + currentTransform.e, currentTransform.f + ]); + const relativeTransform = initialClip.multiply(fixedTransform); + this.clipPath.addPath(tempPath, relativeTransform); + } else { + // Normal case + const relativeTransform = initialClip.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + } } else { // Normal drawing (existing code) ctx.beginPath(); From e5f5a41ae83c49f4c8ac0f17718a77c9541b6f8e Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Wed, 19 Nov 2025 14:05:50 +0530 Subject: [PATCH 3/4] Prevent double transform application in clip() with absolute coordinates --- src/core/p5.Renderer2D.js | 50 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 8cf36ae288..2e2d3cb9e3 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -305,7 +305,7 @@ class Renderer2D extends Renderer { // Start a new path. Everything from here on out should become part of this // one path so that we can clip to the whole thing. this.clipPath = new Path2D(); - this.initialClipTransform = this.drawingContext.getTransform().inverse(); + this.initialTransform = this.drawingContext.getTransform(); if (this._clipInvert) { // Slight hack: draw a big rectangle over everything with reverse winding @@ -331,7 +331,10 @@ class Renderer2D extends Renderer { } endClip() { + const currentTransform = this.drawingContext.getTransform(); + this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); this.drawingContext.clip(this.clipPath); + this.drawingContext.setTransform(currentTransform); this.clipPath = null; super.endClip(); @@ -728,25 +731,34 @@ class Renderer2D extends Renderer { radiusY = h / 2; if (this._clipping) { const tempPath = new Path2D(); - tempPath.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); - - const currentTransform = this.drawingContext.getTransform(); - const initialClip = this.initialClipTransform; - if (currentTransform.a < 0 || currentTransform.d < 0) { - const fixedTransform = new DOMMatrix([ - Math.abs(currentTransform.a), currentTransform.b, - currentTransform.c, Math.abs(currentTransform.d), - currentTransform.e, currentTransform.f - ]); - const relativeTransform = initialClip.multiply(fixedTransform); - this.clipPath.addPath(tempPath, relativeTransform); - } else { - // Normal case - const relativeTransform = initialClip.multiply(currentTransform); - this.clipPath.addPath(tempPath, relativeTransform); - } + const current = this.drawingContext.getTransform(); + // Transform coordinates manually but preserve scale signs + const transformPoint = (x, y) => { + return { + x: current.a * x + current.c * y + current.e, + y: current.b * x + current.d * y + current.f + }; + }; + const transformedCenter = transformPoint(centerX, centerY); + // Calculate transformed radii WITHOUT Math.abs() to preserve negative scaling + const scaleX = Math.sqrt(current.a * current.a + current.c * current.c); + const scaleY = Math.sqrt(current.b * current.b + current.d * current.d); + + // Preserve the sign of the scaling for mirroring effects + const signX = current.a < 0 ? -1 : 1; + const signY = current.d < 0 ? -1 : 1; + + const transformedRadiusX = scaleX * radiusX * signX; + const transformedRadiusY = scaleY * radiusY * signY; + tempPath.ellipse( + transformedCenter.x, + transformedCenter.y, + Math.abs(transformedRadiusX), + Math.abs(transformedRadiusY), + 0, 0, 2 * Math.PI + ); + this.clipPath.addPath(tempPath); } else { - // Normal drawing (existing code) ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); ctx.closePath(); From fc826d46d6ef7d70eabf7f2b0e0850bb457f3ef1 Mon Sep 17 00:00:00 2001 From: VANSH3104 Date: Thu, 27 Nov 2025 13:29:38 +0530 Subject: [PATCH 4/4] Reset transforms during clip application to ensure proper coordinate handling --- src/core/p5.Renderer2D.js | 41 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 2e2d3cb9e3..97c38caa76 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -305,7 +305,7 @@ class Renderer2D extends Renderer { // Start a new path. Everything from here on out should become part of this // one path so that we can clip to the whole thing. this.clipPath = new Path2D(); - this.initialTransform = this.drawingContext.getTransform(); + this._clipBaseTransform = this.drawingContext.getTransform(); if (this._clipInvert) { // Slight hack: draw a big rectangle over everything with reverse winding @@ -331,10 +331,11 @@ class Renderer2D extends Renderer { } endClip() { - const currentTransform = this.drawingContext.getTransform(); - this.drawingContext.setTransform(1, 0, 0, 1, 0, 0); + const savedTransform = this.drawingContext.getTransform(); + this.drawingContext.setTransform(this._clipBaseTransform); this.drawingContext.clip(this.clipPath); - this.drawingContext.setTransform(currentTransform); + this.drawingContext.setTransform(savedTransform); + this.clipPath = null; super.endClip(); @@ -731,33 +732,11 @@ class Renderer2D extends Renderer { radiusY = h / 2; if (this._clipping) { const tempPath = new Path2D(); - const current = this.drawingContext.getTransform(); - // Transform coordinates manually but preserve scale signs - const transformPoint = (x, y) => { - return { - x: current.a * x + current.c * y + current.e, - y: current.b * x + current.d * y + current.f - }; - }; - const transformedCenter = transformPoint(centerX, centerY); - // Calculate transformed radii WITHOUT Math.abs() to preserve negative scaling - const scaleX = Math.sqrt(current.a * current.a + current.c * current.c); - const scaleY = Math.sqrt(current.b * current.b + current.d * current.d); - - // Preserve the sign of the scaling for mirroring effects - const signX = current.a < 0 ? -1 : 1; - const signY = current.d < 0 ? -1 : 1; - - const transformedRadiusX = scaleX * radiusX * signX; - const transformedRadiusY = scaleY * radiusY * signY; - tempPath.ellipse( - transformedCenter.x, - transformedCenter.y, - Math.abs(transformedRadiusX), - Math.abs(transformedRadiusY), - 0, 0, 2 * Math.PI - ); - this.clipPath.addPath(tempPath); + tempPath.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); + const currentTransform = this.drawingContext.getTransform(); + const ClipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = ClipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); } else { ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI);