Skip to content

Commit 4180fd1

Browse files
authored
Merge pull request #7363 from martinleopold/ellipse-fix
Negative dimensions will mirror rect() again
2 parents 33883e5 + ebd00c2 commit 4180fd1

File tree

10 files changed

+119
-19
lines changed

10 files changed

+119
-19
lines changed

src/core/helpers.js

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,56 @@ function modeAdjust(a, b, c, d, mode) {
1515

1616
if (mode === constants.CORNER) {
1717

18-
// CORNER mode already corresponds to a bounding box (top-left corner, width, height)
19-
bbox = { x: a, y: b, w: c, h: d };
18+
// CORNER mode already corresponds to a bounding box (top-left corner, width, height).
19+
// For negative widhts or heights, the absolute value is used.
20+
bbox = {
21+
x: a,
22+
y: b,
23+
w: Math.abs(c),
24+
h: Math.abs(d)
25+
};
2026

2127
} else if (mode === constants.CORNERS) {
2228

2329
// CORNERS mode uses two opposite corners, in any configuration.
2430
// Make sure to get the top left corner by using the minimum of the x and y coordniates.
25-
bbox = { x: Math.min(a, c), y: Math.min(b, d), w: c - a, h: d - b };
31+
bbox = {
32+
x: Math.min(a, c),
33+
y: Math.min(b, d),
34+
w: Math.abs(c - a),
35+
h: Math.abs(d - b)
36+
};
2637

2738
} else if (mode === constants.RADIUS) {
2839

2940
// RADIUS mode uses the center point and half the width and height.
3041
// c (half width) and d (half height) could be negative, so use the absolute value
3142
// in calculating the top left corner (x, y).
32-
bbox = { x: a - Math.abs(c), y: b - Math.abs(d), w: 2 * c, h: 2 * d };
43+
c = Math.abs(c);
44+
d = Math.abs(d);
45+
bbox = {
46+
x: a - c,
47+
y: b - d,
48+
w: 2 * c,
49+
h: 2 * d
50+
};
3351

3452
} else if (mode === constants.CENTER) {
3553

3654
// CENTER mode uses the center point, width and height.
3755
// c (width) and d (height) could be negative, so use the absolute value
38-
// in calculating the top-left corner (x,y).
39-
bbox = { x: a - Math.abs(c * 0.5), y: b - Math.abs(d * 0.5), w: c, h: d };
56+
// in calculating the top-left corner (x, y).
57+
c = Math.abs(c);
58+
d = Math.abs(d);
59+
bbox = {
60+
x: a - (c * 0.5),
61+
y: b - (d * 0.5),
62+
w: c,
63+
h: d
64+
};
4065

4166
}
4267

43-
// p5 supports negative width and heights for rectangles, ellipses and arcs
44-
bbox.w = Math.abs(bbox.w);
45-
bbox.h = Math.abs(bbox.h);
46-
4768
return bbox;
4869
}
4970

src/core/shape/2d_primitives.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,15 @@ p5.prototype._renderRect = function() {
13391339
this._renderer._rectMode
13401340
);
13411341

1342+
// For the default rectMode (CORNER), restore a possible negative width/height
1343+
// removed by modeAdjust(). This results in flipped/mirrored rendering,
1344+
// which is especially noticable when using WEGBL rendering and texture().
1345+
// Note that this behavior only applies to rect(), NOT to ellipse() and arc().
1346+
if (this._renderer._rectMode === constants.CORNER) {
1347+
vals.w = arguments[2];
1348+
vals.h = arguments[3];
1349+
}
1350+
13421351
const args = [vals.x, vals.y, vals.w, vals.h];
13431352
// append the additional arguments (either cornder radii, or
13441353
// segment details) to the argument list

test/unit/visual/cases/shape_modes.js

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
1717
// Don't use abs(), so we get negative values as well
1818
let w = x2 - x1; // w
1919
let h = y2 - y1; // h
20+
// With mode CORNER, rects with negative widths/heights result in mirrored/flipped rendering
21+
// In this case, adjust position so the rect is in line with the other cases
22+
if (shape === 'rect') {
23+
if (w < 0) { x += (-w); } // Move right
24+
if (h < 0) { y += (-h); } // Move down
25+
}
2026
x1 = x; y1 = y; x2 = w; y2 = h;
2127
} else if (mode === p5.CENTER) {
2228
// Find center
@@ -56,14 +62,15 @@ function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
5662
}
5763

5864

59-
/*
60-
Comprehensive test for rendering ellipse(), arc(), and rect()
61-
with the different ellipseMode() / rectMode() values: CORNERS, CORNER, CENTER, RADIUS.
62-
Each of the 3 shapes is tested with each of the 4 possible modes, resulting in 12 test.
63-
Each test renders the shape in 16 different coordinate configurations,
64-
testing combinations of positive and negative coordinate values.
65-
*/
6665
visualSuite('Shape Modes', function(...args) {
66+
/*
67+
Comprehensive test for rendering ellipse(), arc(), and rect()
68+
with the different ellipseMode() / rectMode() values: CORNERS, CORNER, CENTER, RADIUS.
69+
Each of the 3 shapes is tested with each of the 4 possible modes, resulting in 12 tests.
70+
Each test renders the shape in 16 different coordinate configurations,
71+
testing combinations of positive and negative coordinate values.
72+
*/
73+
6774
// Shapes to test
6875
const SHAPES = [ 'ellipse', 'arc', 'rect' ];
6976

@@ -113,6 +120,60 @@ visualSuite('Shape Modes', function(...args) {
113120
}); // End of: visualTest
114121
} // End of: MODES loop
115122

116-
}); // End of: Inner visualSuite
123+
}); // End of: visualSuite
117124
} // End of: SHAPES loop
118-
}); // End of: Outer visualSuite
125+
126+
127+
/*
128+
An extra test suite specific to shape mode CORNER and negative dimensions.
129+
For rect, negative widths/heights result in flipped rendering (horizontally/vertically)
130+
For ellipse and arc, using negative widths/heights has no effect (the absolute value is used)
131+
*/
132+
visualSuite('Negative dimensions', function() {
133+
// Negative widths/height result in flipped rects.
134+
visualTest('rect', function(p5, screenshot) {
135+
p5.createCanvas(50, 50);
136+
p5.translate(p5.width/2, p5.height/2);
137+
p5.rectMode(p5.CORNER);
138+
p5.rect(0, 0, 20, 10);
139+
p5.fill('red');
140+
p5.rect(0, 0, -20, 10);
141+
p5.fill('green');
142+
p5.rect(0, 0, 20, -10);
143+
p5.fill('blue');
144+
p5.rect(0, 0, -20, -10);
145+
screenshot();
146+
});
147+
// Since negative widths/heights are used with their absolute value,
148+
// ellipses are drawn on top of each other, blue one last
149+
visualTest('ellipse', function(p5, screenshot) {
150+
p5.createCanvas(50, 50);
151+
p5.translate(p5.width/2, p5.height/2);
152+
p5.ellipseMode(p5.CORNER);
153+
p5.ellipse(0, 0, 20, 10);
154+
p5.fill('red');
155+
p5.ellipse(0, 0, -20, 10);
156+
p5.fill('green');
157+
p5.ellipse(0, 0, 20, -10);
158+
p5.fill('blue');
159+
p5.ellipse(0, 0, -20, -10);
160+
screenshot();
161+
});
162+
// Since negative widths/heights are used with their absolute value,
163+
// arcs are drawn on top of each other, blue one last.
164+
visualTest('arc', function(p5, screenshot) {
165+
p5.createCanvas(50, 50);
166+
p5.translate(p5.width/2, p5.height/2);
167+
p5.ellipseMode(p5.CORNER);
168+
p5.arc(0, 0, 20, 10, 0, p5.PI + p5.HALF_PI);
169+
p5.fill('red');
170+
p5.arc(0, 0, -20, 10, 0, p5.PI + p5.HALF_PI);
171+
p5.fill('green');
172+
p5.arc(0, 0, 20, -10, 0, p5.PI + p5.HALF_PI);
173+
p5.fill('blue');
174+
p5.arc(0, 0, -20, -10, 0, p5.PI + p5.HALF_PI);
175+
screenshot();
176+
});
177+
});
178+
179+
});
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}
Loading

0 commit comments

Comments
 (0)