Skip to content

Commit f43d12d

Browse files
committed
Fix ellipseMode(CORNERS) and rectMode(CORNER)
1 parent 5a7129b commit f43d12d

File tree

3 files changed

+219
-16
lines changed

3 files changed

+219
-16
lines changed

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/shape-modes.html

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
5+
</head>
6+
<body>
7+
<script src="../lib/p5.js"></script>
8+
9+
<script>
10+
function setup() {
11+
createCanvas(600, 900);
12+
noFill();
13+
noLoop();
14+
}
15+
16+
const SHAPES = [ 'ellipse', 'arc', 'rect' ];
17+
const MODES = [ 'corners', 'corner', 'center', 'radius' ];
18+
let SHAPE = 'ellipse';
19+
let MODE = 'corners';
20+
let num = 1;
21+
22+
function nextSetting() {
23+
let modeIdx = MODES.indexOf(MODE);
24+
let shapeIdx = SHAPES.indexOf(SHAPE);
25+
modeIdx++;
26+
if (modeIdx >= MODES.length) {
27+
modeIdx = 0;
28+
shapeIdx++;
29+
if (shapeIdx >= SHAPES.length) {
30+
shapeIdx = 0;
31+
}
32+
}
33+
SHAPE = SHAPES[shapeIdx];
34+
MODE = MODES[modeIdx];
35+
num = (shapeIdx * MODES.length + modeIdx) + 1;
36+
redraw();
37+
}
38+
39+
function keyPressed() {
40+
nextSetting();
41+
}
42+
43+
function mousePressed() {
44+
nextSetting();
45+
}
46+
47+
function ellipseCorners(x1, y1, x2, y2) {
48+
// Bounding box (light gray)
49+
rectMode(CORNERS);
50+
stroke(200);
51+
noFill();
52+
strokeWeight(1);
53+
rect(x1, y1, x2, y2);
54+
55+
// Adjust coordinates for testing modes other than CORNERS
56+
if (MODE == CORNER) {
57+
let x = min(x1, x2); // x
58+
let y = min(y1, y2); // y
59+
// Don't use abs on w and h, so we get negative values as well
60+
let w = x2 - x1; // w
61+
let h = y2 - y1; // h
62+
x1 = x; y1 = y; x2 = w; y2 = h;
63+
} else if (MODE == CENTER) {
64+
let x = (x2 + x1) / 2; // x
65+
let y = (y2 + y1) / 2; // y
66+
// Don't use abs on w and h, so we get negative values as well
67+
let w = x2 - x1;
68+
let h = y2 - y1;
69+
x1 = x; y1 = y; x2 = w; y2 = h;
70+
} else if (MODE == RADIUS) {
71+
let x = (x2 + x1) / 2; // x
72+
let y = (y2 + y1) / 2; // y
73+
// Don't use abs on w and h, so we get negative values as well
74+
let r1 = (x2 - x1) / 2; // r1;
75+
let r2 = (y2 - y1) / 2; // r2
76+
x1 = x; y1 = y; x2 = r1; y2 = r2;
77+
}
78+
79+
80+
// P1, P2 dots
81+
stroke(150);
82+
strokeWeight(4);
83+
point(x1, y1);
84+
if (MODE === CORNERS) {
85+
point(x2, y2);
86+
}
87+
88+
// P1, P2 text labels
89+
fill(150);
90+
noStroke();
91+
text('P1', x1 + 5, y1);
92+
if (MODE === CORNERS) {
93+
text('P2', x2 + 5, y2);
94+
}
95+
96+
97+
// Shape
98+
noFill();
99+
stroke(0);
100+
strokeWeight(1);
101+
ellipseMode(MODE);
102+
rectMode(MODE);
103+
104+
try {
105+
console.log(`Drawing (${x1}, ${y1}, ${x2}, ${y2})`);
106+
if (SHAPE === 'ellipse') {
107+
ellipse(x1, y1, x2, y2);
108+
} else if (SHAPE === 'arc') {
109+
const GAP = 0.1;
110+
arc(x1, y1, x2, y2, 0 + GAP/2, HALF_PI - GAP);
111+
arc(x1, y1, x2, y2, HALF_PI + GAP, PI - GAP);
112+
arc(x1, y1, x2, y2, PI + GAP, PI + HALF_PI - GAP);
113+
arc(x1, y1, x2, y2, PI + HALF_PI + GAP, TWO_PI - GAP);
114+
} else if (SHAPE === 'rect') {
115+
rect(x1, y1, x2, y2);
116+
}
117+
} catch (e) {
118+
console.warn(`Error drawing (${x1}, ${y1}, ${x2}, ${y2})`);
119+
console.error(e);
120+
}
121+
}
122+
123+
124+
function draw() {
125+
background(220);
126+
translate(width/2, height/2);
127+
128+
// Coordinate System
129+
stroke(200);
130+
line(-width/2, 0, width/2, 0);
131+
line(0, -height/2, 0, height/2);
132+
stroke(0);
133+
134+
// Shape + Mode Text Labels
135+
fill(0);
136+
noStroke();
137+
const counterLabel = `(${num}/${SHAPES.length * MODES.length})`;
138+
text(counterLabel, 5, 15);
139+
text(SHAPE, 5, 30);
140+
text(MODE.toUpperCase(), 5, 45);
141+
142+
console.log(counterLabel);
143+
console.log("Shape: ", SHAPE);
144+
console.log("Mode: ", MODE);
145+
146+
147+
// Quadrant I (Bottom Right)
148+
console.log("(I) BOTTOM RIGHT");
149+
ellipseCorners(100, 50, 200, 100); // OK
150+
ellipseCorners(100, 200, 200, 150); // Error
151+
ellipseCorners(200, 300, 100, 250); // Error
152+
ellipseCorners(200, 350, 100, 400); // Error
153+
154+
155+
// Quadrant II (Bottom Left)
156+
console.log();
157+
console.log("(II) BOTTOM LEFT");
158+
ellipseCorners(-200, 50, -100, 100); // Wrong ellipse
159+
ellipseCorners(-200, 200, -100, 150); // Error
160+
ellipseCorners(-100, 300, -200, 250); // Error
161+
ellipseCorners(-100, 350, -200, 400); // Wrong ellipse
162+
163+
164+
// Quadrant III (Top Left)
165+
console.log();
166+
console.log("(III) TOP LEFT");
167+
ellipseCorners(-200, -400, -100, -350); // Wrong ellipse
168+
ellipseCorners(-200, -250, -100, -300); // Wrong ellipse
169+
ellipseCorners(-100, -150, -200, -200); // Wrong ellipse
170+
ellipseCorners(-100, -100, -200, -50); // Wrong ellipse
171+
172+
173+
// Quadrant IV (Top Right)
174+
console.log();
175+
console.log("(IV) TOP RIGHT");
176+
ellipseCorners(100, -400, 200, -350); // Wrong ellipse
177+
ellipseCorners(100, -250, 200, -300); // Wrong ellipse
178+
ellipseCorners(200, -150, 100, -200); // Error
179+
ellipseCorners(200, -100, 100, -50); // Error
180+
}
181+
</script>
182+
</body>
183+
</html>

0 commit comments

Comments
 (0)