diff --git a/src/core/helpers.js b/src/core/helpers.js index 5caa9e2b91..0bc0ddbe3d 100644 --- a/src/core/helpers.js +++ b/src/core/helpers.js @@ -15,35 +15,56 @@ function modeAdjust(a, b, c, d, mode) { if (mode === constants.CORNER) { - // CORNER mode already corresponds to a bounding box (top-left corner, width, height) - bbox = { x: a, y: b, w: c, h: d }; + // CORNER mode already corresponds to a bounding box (top-left corner, width, height). + // For negative widhts or heights, the absolute value is used. + bbox = { + x: a, + y: b, + w: Math.abs(c), + h: Math.abs(d) + }; } else if (mode === constants.CORNERS) { // CORNERS mode uses two opposite corners, in any configuration. // Make sure to get the top left corner by using the minimum of the x and y coordniates. - bbox = { x: Math.min(a, c), y: Math.min(b, d), w: c - a, h: d - b }; + bbox = { + x: Math.min(a, c), + y: Math.min(b, d), + w: Math.abs(c - a), + h: Math.abs(d - b) + }; } else if (mode === constants.RADIUS) { // RADIUS mode uses the center point and half the width and height. // c (half width) and d (half height) could be negative, so use the absolute value // in calculating the top left corner (x, y). - bbox = { x: a - Math.abs(c), y: b - Math.abs(d), w: 2 * c, h: 2 * d }; + c = Math.abs(c); + d = Math.abs(d); + bbox = { + x: a - c, + y: b - d, + w: 2 * c, + h: 2 * d + }; } else if (mode === constants.CENTER) { // CENTER mode uses the center point, width and height. // c (width) and d (height) could be negative, so use the absolute value - // in calculating the top-left corner (x,y). - bbox = { x: a - Math.abs(c * 0.5), y: b - Math.abs(d * 0.5), w: c, h: d }; + // in calculating the top-left corner (x, y). + c = Math.abs(c); + d = Math.abs(d); + bbox = { + x: a - (c * 0.5), + y: b - (d * 0.5), + w: c, + h: d + }; } - // p5 supports negative width and heights for rectangles, ellipses and arcs - bbox.w = Math.abs(bbox.w); - bbox.h = Math.abs(bbox.h); - return bbox; } diff --git a/src/core/shape/2d_primitives.js b/src/core/shape/2d_primitives.js index 92a5f527b2..f916339514 100644 --- a/src/core/shape/2d_primitives.js +++ b/src/core/shape/2d_primitives.js @@ -1339,6 +1339,15 @@ p5.prototype._renderRect = function() { this._renderer._rectMode ); + // For the default rectMode (CORNER), restore a possible negative width/height + // removed by modeAdjust(). This results in flipped/mirrored rendering, + // which is especially noticable when using WEGBL rendering and texture(). + // Note that this behavior only applies to rect(), NOT to ellipse() and arc(). + if (this._renderer._rectMode === constants.CORNER) { + vals.w = arguments[2]; + vals.h = arguments[3]; + } + const args = [vals.x, vals.y, vals.w, vals.h]; // append the additional arguments (either cornder radii, or // segment details) to the argument list diff --git a/test/unit/visual/cases/shape_modes.js b/test/unit/visual/cases/shape_modes.js index d17565b320..8fa4c0f951 100644 --- a/test/unit/visual/cases/shape_modes.js +++ b/test/unit/visual/cases/shape_modes.js @@ -17,6 +17,12 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) { // Don't use abs(), so we get negative values as well let w = x2 - x1; // w let h = y2 - y1; // h + // With mode CORNER, rects with negative widths/heights result in mirrored/flipped rendering + // In this case, adjust position so the rect is in line with the other cases + if (shape === 'rect') { + if (w < 0) { x += (-w); } // Move right + if (h < 0) { y += (-h); } // Move down + } x1 = x; y1 = y; x2 = w; y2 = h; } else if (mode === p5.CENTER) { // Find center @@ -56,14 +62,15 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) { } -/* - Comprehensive test for rendering ellipse(), arc(), and rect() - with the different ellipseMode() / rectMode() values: CORNERS, CORNER, CENTER, RADIUS. - Each of the 3 shapes is tested with each of the 4 possible modes, resulting in 12 test. - Each test renders the shape in 16 different coordinate configurations, - testing combinations of positive and negative coordinate values. -*/ visualSuite('Shape Modes', function(...args) { + /* + Comprehensive test for rendering ellipse(), arc(), and rect() + with the different ellipseMode() / rectMode() values: CORNERS, CORNER, CENTER, RADIUS. + Each of the 3 shapes is tested with each of the 4 possible modes, resulting in 12 tests. + Each test renders the shape in 16 different coordinate configurations, + testing combinations of positive and negative coordinate values. + */ + // Shapes to test const SHAPES = [ 'ellipse', 'arc', 'rect' ]; @@ -113,6 +120,60 @@ visualSuite('Shape Modes', function(...args) { }); // End of: visualTest } // End of: MODES loop - }); // End of: Inner visualSuite + }); // End of: visualSuite } // End of: SHAPES loop -}); // End of: Outer visualSuite + + + /* + An extra test suite specific to shape mode CORNER and negative dimensions. + For rect, negative widths/heights result in flipped rendering (horizontally/vertically) + For ellipse and arc, using negative widths/heights has no effect (the absolute value is used) + */ + visualSuite('Negative dimensions', function() { + // Negative widths/height result in flipped rects. + visualTest('rect', function(p5, screenshot) { + p5.createCanvas(50, 50); + p5.translate(p5.width/2, p5.height/2); + p5.rectMode(p5.CORNER); + p5.rect(0, 0, 20, 10); + p5.fill('red'); + p5.rect(0, 0, -20, 10); + p5.fill('green'); + p5.rect(0, 0, 20, -10); + p5.fill('blue'); + p5.rect(0, 0, -20, -10); + screenshot(); + }); + // Since negative widths/heights are used with their absolute value, + // ellipses are drawn on top of each other, blue one last + visualTest('ellipse', function(p5, screenshot) { + p5.createCanvas(50, 50); + p5.translate(p5.width/2, p5.height/2); + p5.ellipseMode(p5.CORNER); + p5.ellipse(0, 0, 20, 10); + p5.fill('red'); + p5.ellipse(0, 0, -20, 10); + p5.fill('green'); + p5.ellipse(0, 0, 20, -10); + p5.fill('blue'); + p5.ellipse(0, 0, -20, -10); + screenshot(); + }); + // Since negative widths/heights are used with their absolute value, + // arcs are drawn on top of each other, blue one last. + visualTest('arc', function(p5, screenshot) { + p5.createCanvas(50, 50); + p5.translate(p5.width/2, p5.height/2); + p5.ellipseMode(p5.CORNER); + p5.arc(0, 0, 20, 10, 0, p5.PI + p5.HALF_PI); + p5.fill('red'); + p5.arc(0, 0, -20, 10, 0, p5.PI + p5.HALF_PI); + p5.fill('green'); + p5.arc(0, 0, 20, -10, 0, p5.PI + p5.HALF_PI); + p5.fill('blue'); + p5.arc(0, 0, -20, -10, 0, p5.PI + p5.HALF_PI); + screenshot(); + }); + }); + +}); diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/000.png b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/000.png new file mode 100644 index 0000000000..b0732e0093 Binary files /dev/null and b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/000.png differ diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/metadata.json b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/000.png b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/000.png new file mode 100644 index 0000000000..9351dc2adf Binary files /dev/null and b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/000.png differ diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/metadata.json b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/000.png b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/000.png new file mode 100644 index 0000000000..59aab0876f Binary files /dev/null and b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/000.png differ diff --git a/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/metadata.json b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/Shape Modes/Shape rect/Mode CORNER/000.png b/test/unit/visual/screenshots/Shape Modes/Shape rect/Mode CORNER/000.png index 5c096e42bb..9e6ecdcd59 100644 Binary files a/test/unit/visual/screenshots/Shape Modes/Shape rect/Mode CORNER/000.png and b/test/unit/visual/screenshots/Shape Modes/Shape rect/Mode CORNER/000.png differ