Skip to content

Commit 816b4c3

Browse files
authored
Merge pull request #7290 from martinleopold/ellipse-fix
Fix ellipseMode(CORNERS) and rectMode(CORNER)
2 parents 3211fd4 + baa5f9d commit 816b4c3

File tree

33 files changed

+202
-22
lines changed

33 files changed

+202
-22
lines changed

.all-contributorsrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6370,9 +6370,9 @@
63706370
},
63716371
{
63726372
"login": "martinleopold",
6373-
"name": "Martin Leopold",
6373+
"name": "Martin Leopold Groedl",
63746374
"avatar_url": "https://avatars.githubusercontent.com/u/1692826?v=4",
6375-
"profile": "https://github.com/martinleopold",
6375+
"profile": "https://groedl.xyz",
63766376
"contributions": [
63776377
"bug",
63786378
"code"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ We recognize all types of contributions. This project follows the [all-contribut
10671067
<td align="center" valign="top" width="16.66%"><a href="http://computationalmama.xyz"><img src="https://avatars.githubusercontent.com/u/10388784?v=4?s=120" width="120px;" alt="computational mama"/><br /><sub><b>computational mama</b></sub></a><br /><a href="https://github.com/processing/p5.js/commits?author=computationalmama" title="Code">💻</a></td>
10681068
<td align="center" valign="top" width="16.66%"><a href="https://fabianmoronzirfas.me"><img src="https://avatars.githubusercontent.com/u/315106?v=4?s=120" width="120px;" alt="Fabian Morón Zirfas"/><br /><sub><b>Fabian Morón Zirfas</b></sub></a><br /><a href="https://github.com/processing/p5.js/commits?author=ff6347" title="Documentation">📖</a> <a href="https://github.com/processing/p5.js/commits?author=ff6347" title="Code">💻</a></td>
10691069
<td align="center" valign="top" width="16.66%"><a href="http://www.lukeplowden.com"><img src="https://avatars.githubusercontent.com/u/62835749?v=4?s=120" width="120px;" alt="Luke Plowden"/><br /><sub><b>Luke Plowden</b></sub></a><br /><a href="https://github.com/processing/p5.js/commits?author=lukeplowden" title="Code">💻</a></td>
1070-
<td align="center" valign="top" width="16.66%"><a href="https://github.com/martinleopold"><img src="https://avatars.githubusercontent.com/u/1692826?v=4?s=120" width="120px;" alt="Martin Leopold"/><br /><sub><b>Martin Leopold</b></sub></a><br /><a href="https://github.com/processing/p5.js/issues?q=author%3Amartinleopold" title="Bug reports">🐛</a> <a href="https://github.com/processing/p5.js/commits?author=martinleopold" title="Code">💻</a></td>
1070+
<td align="center" valign="top" width="16.66%"><a href="https://github.com/martinleopold"><img src="https://avatars.githubusercontent.com/u/1692826?v=4?s=120" width="120px;" alt="Martin Leopold Groedl"/><br /><sub><b>Martin Leopold Groedl</b></sub></a><br /><a href="https://github.com/processing/p5.js/issues?q=author%3Amartinleopold" title="Bug reports">🐛</a> <a href="https://github.com/processing/p5.js/commits?author=martinleopold" title="Code">💻</a></td>
10711071
</tr>
10721072
<tr>
10731073
<td align="center" valign="top" width="16.66%"><a href="https://www.linkedin.com/in/ashish1729/"><img src="https://avatars.githubusercontent.com/u/10610651?v=4?s=120" width="120px;" alt="ashish singh"/><br /><sub><b>ashish singh</b></sub></a><br /><a href="https://github.com/processing/p5.js/commits?author=ashish1729" title="Code">💻</a></td>

src/core/helpers.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,47 @@
44

55
import * as constants from './constants';
66

7+
/*
8+
This function normalizes the first four arguments given to rect, ellipse and arc
9+
according to the mode.
10+
It returns a 'bounding box' object containing the coordinates of the upper left corner (x, y),
11+
and width and height (w, h). The returned width and height are always positive.
12+
*/
713
function modeAdjust(a, b, c, d, mode) {
14+
let bbox;
15+
816
if (mode === constants.CORNER) {
9-
return { x: a, y: b, w: c, h: d };
17+
18+
// CORNER mode already corresponds to a bounding box (top-left corner, width, height)
19+
bbox = { x: a, y: b, w: c, h: d };
20+
1021
} else if (mode === constants.CORNERS) {
11-
return { x: a, y: b, w: c - a, h: d - b };
22+
23+
// CORNERS mode uses two opposite corners, in any configuration.
24+
// 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 };
26+
1227
} else if (mode === constants.RADIUS) {
13-
return { x: a - c, y: b - d, w: 2 * c, h: 2 * d };
28+
29+
// RADIUS mode uses the center point and half the width and height.
30+
// c (half width) and d (half height) could be negative, so use the absolute value
31+
// 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 };
33+
1434
} else if (mode === constants.CENTER) {
15-
return { x: a - c * 0.5, y: b - d * 0.5, w: c, h: d };
35+
36+
// CENTER mode uses the center point, width and height.
37+
// 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 };
40+
1641
}
42+
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+
47+
return bbox;
1748
}
1849

1950
export default { modeAdjust };

src/core/shape/2d_primitives.js

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,6 @@ p5.prototype.arc = function(x, y, w, h, start, stop, mode, detail) {
327327
start = this._toRadians(start);
328328
stop = this._toRadians(stop);
329329

330-
// p5 supports negative width and heights for ellipses
331-
w = Math.abs(w);
332-
h = Math.abs(h);
333-
334330
const vals = canvas.modeAdjust(x, y, w, h, this._renderer._ellipseMode);
335331
const angles = this._normalizeArcAngles(start, stop, vals.w, vals.h, true);
336332

@@ -547,16 +543,9 @@ p5.prototype._renderEllipse = function(x, y, w, h, detailX) {
547543
return this;
548544
}
549545

550-
// p5 supports negative width and heights for rects
551-
if (w < 0) {
552-
w = Math.abs(w);
553-
}
554-
546+
// Duplicate 3rd argument if only 3 given.
555547
if (typeof h === 'undefined') {
556-
// Duplicate 3rd argument if only 3 given.
557548
h = w;
558-
} else if (h < 0) {
559-
h = Math.abs(h);
560549
}
561550

562551
const vals = canvas.modeAdjust(x, y, w, h, this._renderer._ellipseMode);

test/node/helpers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ suite('helpers/modeAdjust', function() {
1515
});
1616
test('should set mode to corners', function() {
1717
result = helpers.modeAdjust(a, b, c, d, constants.CORNERS);
18-
expect(result).to.eql({ x: 100, y: 200, w: -50, h: -50 });
18+
expect(result).to.eql({ x: 50, y: 150, w: 50, h: 50 });
1919
});
2020
test('should set mode to radius', function() {
2121
result = helpers.modeAdjust(a, b, c, d, constants.RADIUS);

test/unit/spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ var spec = {
5252
// Add the visual tests that you want run as part of CI here. Feel free
5353
// to omit some for speed if they should only be run manually.
5454
'webgl',
55-
'typography'
55+
'typography',
56+
'shape_modes'
5657
]
5758
};
5859
document.write(

test/unit/visual/cases/shape_modes.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
Helper function that draws a shape using the specified shape mode
3+
p5 ............... The p5 Instance
4+
shape ............ The shape to draw. Either 'ellipse', 'arc', or 'rect'
5+
mode ............. The ellipseMode (for ellipse and arc), or rectMode (for rect)
6+
Either p5.CORNERS, p5.CORNER, p5.CENTER or p5.RADIUS
7+
x1, x2, x2, y2 ... Coordinates specifying the shape in CORNERS mode,
8+
i.e. (x1, y1) and (x2, y2) specify two opposite corners P1 and P2
9+
*/
10+
function shapeCorners(p5, shape, mode, x1, y1, x2, y2) {
11+
// Adjust coordinates for testing modes other than CORNERS
12+
if (mode === p5.CORNER) {
13+
// Find top left corner
14+
let x = p5.min(x1, x2); // x
15+
let y = p5.min(y1, y2); // y
16+
// Calculate width and height
17+
// Don't use abs(), so we get negative values as well
18+
let w = x2 - x1; // w
19+
let h = y2 - y1; // h
20+
x1 = x; y1 = y; x2 = w; y2 = h;
21+
} else if (mode === p5.CENTER) {
22+
// Find center
23+
let x = (x2 + x1) / 2; // x
24+
let y = (y2 + y1) / 2; // y
25+
// Calculate width and height
26+
// Don't use abs(), so we get negative values as well
27+
let w = x2 - x1;
28+
let h = y2 - y1;
29+
x1 = x; y1 = y; x2 = w; y2 = h;
30+
} else if (mode === p5.RADIUS) {
31+
// Find Center
32+
let x = (x2 + x1) / 2; // x
33+
let y = (y2 + y1) / 2; // y
34+
// Calculate radii
35+
// Don't use abs(), so we get negative values as well
36+
let r1 = (x2 - x1) / 2; // r1;
37+
let r2 = (y2 - y1) / 2; // r2
38+
x1 = x; y1 = y; x2 = r1; y2 = r2;
39+
}
40+
41+
if (shape === 'ellipse') {
42+
p5.ellipseMode(mode);
43+
p5.ellipse(x1, y1, x2, y2);
44+
} else if (shape === 'arc') {
45+
// Draw four arcs with gaps inbetween
46+
const GAP = p5.radians(20);
47+
p5.ellipseMode(mode);
48+
p5.arc(x1, y1, x2, y2, 0 + GAP, p5.HALF_PI - GAP);
49+
p5.arc(x1, y1, x2, y2, p5.HALF_PI + GAP, p5.PI - GAP);
50+
p5.arc(x1, y1, x2, y2, p5.PI + GAP, p5.PI + p5.HALF_PI - GAP);
51+
p5.arc(x1, y1, x2, y2, p5.PI + p5.HALF_PI + GAP, p5.TWO_PI - GAP);
52+
} else if (shape === 'rect') {
53+
p5.rectMode(mode);
54+
p5.rect(x1, y1, x2, y2);
55+
}
56+
}
57+
58+
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+
*/
66+
visualSuite('Shape Modes', function(...args) {
67+
// Shapes to test
68+
const SHAPES = [ 'ellipse', 'arc', 'rect' ];
69+
70+
// Modes to test (used with ellipseMode or rectMode, according to shape)
71+
const MODES = [ 'CORNERS', 'CORNER', 'CENTER', 'RADIUS' ];
72+
73+
for (let shape of SHAPES) {
74+
visualSuite(`Shape ${shape}`, function() {
75+
76+
for (let mode of MODES) {
77+
visualTest(`Mode ${mode}`, function(p5, screenshot) {
78+
p5.createCanvas(60, 125);
79+
p5.translate(p5.width/2, p5.height/2);
80+
81+
// Make the following calls to shapeCorners shorter
82+
// by omitting p5, shape and mode parameters
83+
function _shapeCorners(x1, y1, x2, y2) {
84+
shapeCorners(p5, shape, p5[mode], x1, y1, x2, y2);
85+
}
86+
87+
// Quadrant I (Bottom Right)
88+
// P1 P2
89+
_shapeCorners( 5, 5, 25, 15); // P1 Top Left, P2 Bottom Right
90+
_shapeCorners( 5, 20, 25, 30); // P1 Bottom Left, P2 Top Right
91+
_shapeCorners(25, 45, 5, 35); // P1 Bottom Right, P2 Top Left
92+
_shapeCorners(25, 50, 5, 60); // P1 Top Right, P2 Bottom Left
93+
94+
// Quadrant II (Bottom Left)
95+
_shapeCorners(-25, 5, -5, 15);
96+
_shapeCorners(-25, 20, -5, 30);
97+
_shapeCorners( -5, 45, -25, 35);
98+
_shapeCorners( -5, 50, -25, 60);
99+
100+
// Quadrant III (Top Left)
101+
_shapeCorners(-25, -60, -5, -50);
102+
_shapeCorners(-25, -35, -5, -45);
103+
_shapeCorners( -5, -20, -25, -30);
104+
_shapeCorners( -5, -15, -25, -5);
105+
106+
// Quadrant IV (Top Right)
107+
_shapeCorners( 5, -60, 25, -50);
108+
_shapeCorners( 5, -35, 25, -45);
109+
_shapeCorners(25, -20, 5, -30);
110+
_shapeCorners(25, -15, 5, -5);
111+
112+
screenshot();
113+
}); // End of: visualTest
114+
} // End of: MODES loop
115+
116+
}); // End of: Inner visualSuite
117+
} // End of: SHAPES loop
118+
}); // End of: Outer visualSuite
1.17 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}
1.17 KB
Loading

0 commit comments

Comments
 (0)