From 75fac2e6117d15847357ec047107acf6ddbbf175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=B6dl?= Date: Wed, 6 Nov 2024 21:32:21 +0100 Subject: [PATCH] Negative dimensions will flip shapes again Only applicable to shape mode `CORNER` Fixes #7353 --- src/core/helpers.js | 43 ++++++++--- test/unit/visual/cases/shape_modes.js | 72 +++++++++++++++--- .../Negative dimensions/arc/000.png | Bin 0 -> 1083 bytes .../Negative dimensions/arc/metadata.json | 3 + .../Negative dimensions/ellipse/000.png | Bin 0 -> 1225 bytes .../Negative dimensions/ellipse/metadata.json | 3 + .../Negative dimensions/rect/000.png | Bin 0 -> 405 bytes .../Negative dimensions/rect/metadata.json | 3 + 8 files changed, 105 insertions(+), 19 deletions(-) create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/000.png create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/arc/metadata.json create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/000.png create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/ellipse/metadata.json create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/000.png create mode 100644 test/unit/visual/screenshots/Shape Modes/Negative dimensions/rect/metadata.json diff --git a/src/core/helpers.js b/src/core/helpers.js index 5caa9e2b91..c0011663f1 100644 --- a/src/core/helpers.js +++ b/src/core/helpers.js @@ -15,35 +15,58 @@ 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). + // Negative dimensions (width and/or height) result in 'flipping' the shape. + if (c < 0) { c = -c; a -= c; } // Negative width: Move shape to the left (flip left) + if (d < 0) { d = -d; b -= d; } // Negative height: Move shape up (flip up) + bbox = { + x: a, + y: b, + w: c, + h: 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/test/unit/visual/cases/shape_modes.js b/test/unit/visual/cases/shape_modes.js index d17565b320..09cf64b74b 100644 --- a/test/unit/visual/cases/shape_modes.js +++ b/test/unit/visual/cases/shape_modes.js @@ -17,6 +17,10 @@ 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, negative widths/heights result in mirrored/flipped shapes + // In this case, adjust position so the shape is in line with the other cases + 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 +60,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 +118,55 @@ 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. + Negative width should result in the shape flipped horizontally (to the left). + Negative height should result in the shape flipped vertically (up). + */ + visualSuite('Negative dimensions', function() { + 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(); + }); + 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(); + }); + 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 0000000000000000000000000000000000000000..dd6a24bd71d828d1b5ae8b819dc63304ca335012 GIT binary patch literal 1083 zcmV-B1jPG^P)Px&^+`lQRA@u(mOo4zR~*Mbca|@yeLlEzB$F#;hypgHsDGes#7eTJgOQLeJi0@d zs2gLKjAe2cvpMn(tJ487V70EQwic?6p1MS_Zq$bq1>Mr5pS*`Ol6tsBw8pduqO*0=2cwjwh? z;>=eCzV~e-3{6j@A`1}9f}Dj9L-0(22^a2+!&mPEKU@)HfiPJ@WHAZx7YZsa2IG8- zAHCl89w3uDpv}XY0clr4M*OoN#tt`_=Uoim2I3dwzVJFvf6MwEWDJxhl*6zx zz;I!ZF+wy+6Yc@@8^Z_D9H_@(%A;!pK@=Q5BhI*o+>k*Y!u==kRP*kAA&^IyJmyoP zo_b;+?*TV}Z_YLOCE}mJgA!B(Ks=8Iq}se`waKH3%=Vm9;0TA0fuFoU0PQ(6bTB-1 z2T#}n)@vx%T|P(WjcdyN417(EXMA)~so&48gX};>hGn0-7k~{L8gBtQ5)L_J?)a2D zZKNtTEl&0RH9W3Qa~JeE$Q!T?Z8$Fqa48O1Tb)3E1A}d=i_c zxvT5?Tt1&SR##VB$0uq=yGsYJ0@JWM1K+xz<$UJ6%Db`^46$Rwd1g0KnhH0Zrt zhn5Xe2Tp7_c44!{z$9K$RW;Y`b~DLjvK(s>$)5q9JFlPmO7oziEZ1* zuIp~LS_5AS-hp^Y1yjK7Gv6u8X{ui(+0V)jO;6;BB62ys@;L#>D=)!?0|XTbDl#Gm zg4P<5vA$)4iukr}L<@t8jL2BuvOz_BTQ{PGe*@R83qk>inl1nU002ovPDHLkV1mr^ B>u3M~ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..512c41a01461c36a31449de984ce55c28684628b GIT binary patch literal 1225 zcmV;)1UCDLP)Px(gGod|RA@u(n9oZiXBfvnGa+q_6T>9J=-9GdSxv!~mKJ*Okc)V3PgOyo7rmB+ z9y}Jd(o6B=C5o`*FHq1+=tYZ^;$AAmHqw|DC$V7GP1+_(eodK)yBKSdagwlQJ1@*B z@B4k<@AEwKex8ZID>8&v#0QR%|5_%Uj5rzTQUsjR>QZ2B%Q_jc9l9o+^;O_WXLskakSZN=8E9nT5(Ud{C|(bP#wTgW==o8Kgz1mf*Mw!aC$u zf!`XK3gKp!D$@*5)AIs;WOU%1XXFskDUqd$z&e*S(=#Pn7*X~>3Bp1Erhxty0Z#xC zAY6dJHXMFdw?JZ#UV?l?fOi2;3%PUPC*TPMde|mIz23Fujg0t#r?3Nr?pQ6)r-y;1 z6r8+i#@o>Q9C+KN+!Nq?Qsgl7{xupHk+P8X)pFj>*a8+0aL4ZPB|ctnM+=Ap-*Lbh z@849q&B&)P`52ZEQ%@kn93sOu<$eTyKqHHxXZ2-&K2O~Rc?{d5V6$PojmGu6dI8EN z#AfPdOYGudj1Tdf*RRwCsaJgYnfM72s!G0+~ zO0NM?y99j=vIfAk2FD!7%^W&2^qatQ<3*sKUQ_Oj0)L^KK>U+SVC@Qsjr7cItQZM- zV73C&qqWnD9uU3IYY5~Zp9kFq#Wa)!SkKXv#37HzGh3-trbkCdtEEy&^msg?*Xyn6 z<#PFaKCio6u3|c!E(?ONp361u6j~cmU{8VI0tD{FbtnX2`;dAPD~htGC`xc)VIeRz zHAR1a^G+d=NDzrc2#3RkKp?PvczB~zuuxhiiQDEnq)sqNT+`dxAr!LLDTKpemX?-M zCnsj7VC`j>Ae#nXN4O3Lzf01+bUN+xdfQXn+S+1qaq%D)i&-Q4Bs`8nN60=1<(~WZ z%g4vZ9U;4_s>RdOQ)^_Ogh&)RLUz?KGCW*PBoZAlGBh+~Tmsg}-UsJNkUPS4*gx&- zdyq^fWxu~Y#f^;(qqO$p@wj!ei;#(e*cR78OJb5(7DX`=jYh?Z344kwD=RE7FKfx9 z*(q3i*BSEt_rHDYORSEg5|0>Vw_*Xg40i zOX}(|{)9Mg(LLS#xnp`(T9iAU?d42&jJ%w2t$E}b}s zc@Eo5r{KPx%fVZgEy;>rouL^e&mxytoU&Ws?s}ag%e5`;%>R?Shw~0o^=s40yv?`v zelLj?-*&V51kZP?^P#S{x2a{!TDEXqHm{Ixv4gt1+Xv04i!-bAL$=f@{Lo)qy1!BQ zaJQbmt7GJ?7{R&47SEf$oml$ue7o_3yjR<|1^<7#&u&ljm-h^TeZU=@lgQu&X%Q~loCIIS&uK@r6 literal 0 HcmV?d00001 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