Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 34 additions & 11 deletions src/core/p5.Renderer2D.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.initialClipTransform = this.drawingContext.getTransform().inverse();

if (this._clipInvert) {
// Slight hack: draw a big rectangle over everything with reverse winding
Expand Down Expand Up @@ -705,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]),
Expand All @@ -725,17 +726,39 @@ 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();
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
Comment on lines +735 to +739
Copy link
Collaborator

@perminder-17 perminder-17 Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you're now forcing the geometry with negative transformations to make it positive. If the user actually wanted to mirror the orientation, it will actually cause problem to the geometry. I still think the problem is that our transformation is applied twice.

I just tested code, and it looks like:

When I apply scale(3) , the expected result is:

Screenshot from 2025-11-13 16-03-33

The current behavior in the changes of this PR:

Screenshot from 2025-11-13 16-04-53
function setup() {
  createCanvas(500, 500);
  background(240);

  clip(()=>{
    scale(3);
    ellipse(20, 40, 40, 40); 
  })
  fill(255, 80, 80);
  rect(0, 0, 500, 500);
}

this is the code I am using, Please ignore the calculations, I was just testing scale() and other transformations behaviour.

scale(2) is equivalent to scale(4), scale(3) is equivalent to scale(9) and I think this is the problem with the negetive stuff as well.

As I was mentioning on previous comments,

I think you're storing inverse of initial clip transform into beginClip(), and multiplying with currentTransform , inside endClip() clip() is running with the current transform, and it's applying again i think. I am totally not sure with this but

]);
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();
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) {
Expand Down
Loading