diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index b9a7e12d00..2e2d3cb9e3 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -305,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.initialTransform = this.drawingContext.getTransform(); if (this._clipInvert) { // Slight hack: draw a big rectangle over everything with reverse winding @@ -330,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(); @@ -705,7 +709,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]), @@ -725,17 +729,48 @@ 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 && doFill) { - ctx.fill(); - } - if (!this._clipping && doStroke) { - ctx.stroke(); + 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); + } else { + 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) {