diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 2a4f66006f..26b8a25e2c 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -306,6 +306,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._clipBaseTransform = this.drawingContext.getTransform(); if (this._clipInvert) { // Slight hack: draw a big rectangle over everything with reverse winding @@ -331,7 +332,11 @@ class Renderer2D extends Renderer { } endClip() { + const savedTransform = this.drawingContext.getTransform(); + this.drawingContext.setTransform(this._clipBaseTransform); this.drawingContext.clip(this.clipPath); + this.drawingContext.setTransform(savedTransform); + this.clipPath = null; super.endClip(); @@ -652,7 +657,7 @@ class Renderer2D extends Renderer { * start <= stop < start + TWO_PI */ arc(x, y, w, h, start, stop, mode) { - const ctx = this.clipPa || this.drawingContext; + const ctx = this.drawingContext; const rx = w / 2.0; const ry = h / 2.0; const epsilon = 0.00001; // Smallest visible angle on displays up to 4K. @@ -663,7 +668,15 @@ class Renderer2D extends Renderer { centerY = y + h / 2, radiusX = w / 2, radiusY = h / 2; - + if (this._clipping) { + const tempPath = new Path2D(); + tempPath.ellipse(centerX, centerY, radiusX, radiusY, 0, start, stop); + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; + } // Determines whether to add a line to the center, which should be done // when the mode is PIE or default; as well as when the start and end // angles do not form a full circle. @@ -675,16 +688,16 @@ class Renderer2D extends Renderer { // Fill curves if (this.states.fillColor) { - if (!this._clipping) ctx.beginPath(); + ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, start, stop); if (createPieSlice) ctx.lineTo(centerX, centerY); ctx.closePath(); - if (!this._clipping) ctx.fill(); + ctx.fill(); } // Stroke curves if (this.states.strokeColor) { - if (!this._clipping) ctx.beginPath(); + ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, start, stop); if (mode === constants.PIE && createPieSlice) { @@ -697,8 +710,7 @@ class Renderer2D extends Renderer { // Stroke connects back to path begin for both PIE and CHORD ctx.closePath(); } - - if (!this._clipping) ctx.stroke(); + ctx.stroke(); } return this; @@ -706,7 +718,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]), @@ -726,35 +738,55 @@ class Renderer2D extends Renderer { centerY = y + h / 2, radiusX = w / 2, radiusY = h / 2; - if (!this._clipping) ctx.beginPath(); - + if (this._clipping) { + const tempPath = new Path2D(); + 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); + return this; + } + ctx.beginPath(); ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI); ctx.closePath(); - - if (!this._clipping && doFill) { + if (doFill) { ctx.fill(); } - if (!this._clipping && doStroke) { + if (doStroke) { ctx.stroke(); } + + return this; } line(x1, y1, x2, y2) { - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; if (!this.states.strokeColor) { return this; } else if (this._getStroke() === styleEmpty) { return this; } - if (!this._clipping) ctx.beginPath(); + if (this._clipping) { + const tempPath = new Path2D(); + tempPath.moveTo(x1, y1); + tempPath.lineTo(x2, y2); + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; + } + ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); + return this; } point(x, y) { - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; if (!this.states.strokeColor) { return this; } else if (this._getStroke() === styleEmpty) { @@ -762,20 +794,27 @@ class Renderer2D extends Renderer { } const s = this._getStroke(); const f = this._getFill(); - if (!this._clipping) { - // swapping fill color to stroke and back after for correct point rendering - this._setFill(s); + if (this._clipping) { + const tempPath = new Path2D(); + const drawingContextWidth = this.drawingContext.lineWidth; + tempPath.arc(x, y, drawingContextWidth / 2, 0, constants.TWO_PI); + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; } - if (!this._clipping) ctx.beginPath(); + this._setFill(s); + ctx.beginPath(); ctx.arc(x, y, ctx.lineWidth / 2, 0, constants.TWO_PI, false); - if (!this._clipping) { - ctx.fill(); - this._setFill(f); - } + ctx.fill(); + this._setFill(f); + + return this; } quad(x1, y1, x2, y2, x3, y3, x4, y4) { - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; const doFill = !!this.states.fillColor, doStroke = this.states.strokeColor; if (doFill && !doStroke) { @@ -787,16 +826,29 @@ class Renderer2D extends Renderer { return this; } } - if (!this._clipping) ctx.beginPath(); + if (this._clipping) { + const tempPath = new Path2D(); + tempPath.moveTo(x1, y1); + tempPath.lineTo(x2, y2); + tempPath.lineTo(x3, y3); + tempPath.lineTo(x4, y4); + tempPath.closePath(); + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; + } + ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.lineTo(x4, y4); ctx.closePath(); - if (!this._clipping && doFill) { + if (doFill) { ctx.fill(); } - if (!this._clipping && doStroke) { + if (doStroke) { ctx.stroke(); } return this; @@ -811,7 +863,7 @@ class Renderer2D extends Renderer { let tr = args[5]; let br = args[6]; let bl = args[7]; - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; const doFill = !!this.states.fillColor, doStroke = this.states.strokeColor; if (doFill && !doStroke) { @@ -823,8 +875,20 @@ class Renderer2D extends Renderer { return this; } } - if (!this._clipping) ctx.beginPath(); - + if (this._clipping) { + const tempPath = new Path2D(); + if (typeof tl === 'undefined') { + tempPath.rect(x, y, w, h); + } else { + tempPath.roundRect(x, y, w, h, [tl, tr, br, bl]); + } + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; + } + ctx.beginPath(); if (typeof tl === 'undefined') { // No rounded corners ctx.rect(x, y, w, h); @@ -875,10 +939,10 @@ class Renderer2D extends Renderer { ctx.roundRect(x, y, w, h, [tl, tr, br, bl]); } - if (!this._clipping && this.states.fillColor) { + if (doFill) { ctx.fill(); } - if (!this._clipping && this.states.strokeColor) { + if (doStroke) { ctx.stroke(); } return this; @@ -886,7 +950,7 @@ class Renderer2D extends Renderer { triangle(args) { - const ctx = this.clipPath || this.drawingContext; + const ctx = this.drawingContext; const doFill = !!this.states.fillColor, doStroke = this.states.strokeColor; const x1 = args[0], @@ -904,17 +968,31 @@ class Renderer2D extends Renderer { return this; } } - if (!this._clipping) ctx.beginPath(); + if (this._clipping) { + const tempPath = new Path2D(); + tempPath.moveTo(x1, y1); + tempPath.lineTo(x2, y2); + tempPath.lineTo(x3, y3); + tempPath.closePath(); + const currentTransform = this.drawingContext.getTransform(); + const clipBaseTransform = this._clipBaseTransform.inverse(); + const relativeTransform = clipBaseTransform.multiply(currentTransform); + this.clipPath.addPath(tempPath, relativeTransform); + return this; + } + ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.closePath(); - if (!this._clipping && doFill) { + if (doFill) { ctx.fill(); } - if (!this._clipping && doStroke) { + if (doStroke) { ctx.stroke(); } + + return this; } //////////////////////////////////////////////