Skip to content

Commit 757e03b

Browse files
committed
latest P2DX optimizations from @hazmatsuitor
1 parent dbacaba commit 757e03b

File tree

1 file changed

+127
-144
lines changed

1 file changed

+127
-144
lines changed

core/src/processing/opengl/PGraphics2DX.java

Lines changed: 127 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -849,9 +849,6 @@ public void ellipseImpl(float a, float b, float c, float d) {
849849
return;
850850
}
851851

852-
//TODO: optimize this function, it is still pretty slow
853-
//TODO: try using a lookup table and see if we can make it faster than real trig
854-
855852
beginShape(POLYGON);
856853

857854
//convert corner/diameter to center/radius
@@ -866,10 +863,15 @@ public void ellipseImpl(float a, float b, float c, float d) {
866863
int segments = circleDetail(PApplet.max(rx, ry) + (stroke? strokeWeight : 0), TWO_PI);
867864
float step = TWO_PI / segments;
868865

869-
float angle = 0;
866+
float cos = PApplet.cos(step);
867+
float sin = PApplet.sin(step);
868+
float dx = 0, dy = 1;
870869
for (int i = 0; i < segments; ++i) {
871-
angle += step;
872-
shapeVertex(x + PApplet.sin(angle) * rx, y + PApplet.cos(angle) * ry, 0, 0, fillColor, 0);
870+
shapeVertex(x + dx * rx, y + dy * ry, 0, 0, fillColor, 0);
871+
//this is the equivalent of multiplying the vector <dx, dy> by the 2x2 rotation matrix [[cos -sin] [sin cos]]
872+
float tempx = dx * cos - dy * sin;
873+
dy = dx * sin + dy * cos;
874+
dx = tempx;
873875
}
874876

875877
knownConvexPolygon = true;
@@ -934,17 +936,20 @@ protected void arcImpl(float x, float y, float w, float h, float start, float st
934936
appendContour(vertCount);
935937
}
936938

939+
float dx = PApplet.cos(start);
940+
float dy = PApplet.sin(start);
941+
float c = PApplet.cos(step);
942+
float s = PApplet.sin(step);
937943
for (int i = 0; i <= segments; ++i) {
938-
float s = PApplet.cos(start) * w;
939-
float c = PApplet.sin(start) * h;
940-
941-
vertex(x + s, y + c);
942-
943-
start += step;
944+
shapeVertex(x + dx * w, y + dy * h, 0, 0, fillColor, 0);
945+
//this is the equivalent of multiplying the vector <dx, dy> by the 2x2 rotation matrix [[c -s] [s c]]
946+
float tempx = dx * c - dy * s;
947+
dy = dx * s + dy * c;
948+
dx = tempx;
944949
}
945950

946951
//for the case `(mode == PIE || mode == 0) && diff > HALF_PI`, the polygon
947-
//will not actually be convex, but we still want to tessellate as if it is
952+
//will not actually be convex, but due to known vertex order, we can still safely tessellate as if it is
948953
knownConvexPolygon = true;
949954
if (mode == CHORD || mode == PIE) {
950955
endShape(CLOSE);
@@ -1913,9 +1918,9 @@ private void postMatrixChanged() {
19131918
float syi = projmodelview.m10 * height / 2;
19141919
float sxj = projmodelview.m01 * width / 2;
19151920
float syj = projmodelview.m11 * height / 2;
1916-
float Imag = PApplet.sqrt(sxi * sxi + syi * syi);
1917-
float Jmag = PApplet.sqrt(sxj * sxj + syj * syj);
1918-
ellipseDetailMultiplier = PApplet.max(Imag, Jmag);
1921+
float Imag2 = sxi * sxi + syi * syi;
1922+
float Jmag2 = sxj * sxj + syj * syj;
1923+
ellipseDetailMultiplier = PApplet.sqrt(PApplet.max(Imag2, Jmag2));
19191924
}
19201925

19211926

@@ -1952,55 +1957,40 @@ private void singleLine(float x1, float y1, float x2, float y2, int color) {
19521957
triangle(x2 + tx, y2 - ty, x2 - tx, y2 + ty, x1 + tx, y1 - ty, color);
19531958

19541959
if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) {
1955-
float angle = PApplet.atan2(dx, dy);
1956-
19571960
int segments = circleDetail(r, HALF_PI);
19581961
float step = HALF_PI / segments;
1962+
float c = PApplet.cos(step);
1963+
float s = PApplet.sin(step);
1964+
for (int i = 0; i < segments; ++i) {
1965+
//this is the equivalent of multiplying the vector <tx, ty> by the 2x2 rotation matrix [[c -s] [s c]]
1966+
float nx = c * tx - s * ty;
1967+
float ny = s * tx + c * ty;
19591968

1960-
float psin = ty;
1961-
float pcos = tx;
1962-
for (int i = 1; i < segments; ++i) {
1963-
angle += step;
1964-
float nsin = PApplet.sin(angle) * r;
1965-
float ncos = PApplet.cos(angle) * r;
1966-
1967-
triangle(x2, y2, x2 + psin, y2 + pcos, x2 + nsin, y2 + ncos, color);
1968-
triangle(x2, y2, x2 - pcos, y2 + psin, x2 - ncos, y2 + nsin, color);
1969-
triangle(x1, y1, x1 - psin, y1 - pcos, x1 - nsin, y1 - ncos, color);
1970-
triangle(x1, y1, x1 + pcos, y1 - psin, x1 + ncos, y1 - nsin, color);
1969+
triangle(x2, y2, x2 + ty, y2 + tx, x2 + ny, y2 + nx, color);
1970+
triangle(x2, y2, x2 - tx, y2 + ty, x2 - nx, y2 + ny, color);
1971+
triangle(x1, y1, x1 - ty, y1 - tx, x1 - ny, y1 - nx, color);
1972+
triangle(x1, y1, x1 + tx, y1 - ty, x1 + nx, y1 - ny, color);
19711973

1972-
psin = nsin;
1973-
pcos = ncos;
1974+
tx = nx;
1975+
ty = ny;
19741976
}
1975-
1976-
triangle(x2, y2, x2 + psin, y2 + pcos, x2 + tx, y2 - ty, color);
1977-
triangle(x2, y2, x2 - pcos, y2 + psin, x2 + ty, y2 + tx, color);
1978-
triangle(x1, y1, x1 - psin, y1 - pcos, x1 - tx, y1 + ty, color);
1979-
triangle(x1, y1, x1 + pcos, y1 - psin, x1 - ty, y1 - tx, color);
19801977
}
19811978
}
19821979

19831980

19841981
private void singlePoint(float x, float y, int color) {
19851982
float r = strokeWeight * 0.5f;
1986-
if (strokeCap == ROUND) {
1983+
if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) {
19871984
int segments = circleDetail(r);
19881985
float step = QUARTER_PI / segments;
19891986

1990-
float x1 = 0;
1991-
float y1 = r;
1992-
float angle = 0;
1987+
float x1 = 0, y1 = r;
1988+
float c = PApplet.cos(step);
1989+
float s = PApplet.sin(step);
19931990
for (int i = 0; i < segments; ++i) {
1994-
angle += step;
1995-
float x2, y2;
1996-
//this is not just for performance
1997-
//it also ensures the circle is drawn with no diagonal gaps
1998-
if (i < segments - 1) {
1999-
x2 = PApplet.sin(angle) * r;
2000-
y2 = PApplet.cos(angle) * r;
2001-
} else {
2002-
x2 = y2 = PApplet.sin(QUARTER_PI) * r;
2003-
}
1991+
//this is the equivalent of multiplying the vector <x1, y1> by the 2x2 rotation matrix [[c -s] [s c]]
1992+
float x2 = c * x1 - s * y1;
1993+
float y2 = s * x1 + c * y1;
20041994

20051995
triangle(x, y, x + x1, y + y1, x + x2, y + y2, strokeColor);
20061996
triangle(x, y, x + x1, y - y1, x + x2, y - y2, strokeColor);
@@ -2033,6 +2023,34 @@ private class StrokeRenderer {
20332023
float lx, ly;
20342024
float r;
20352025

2026+
2027+
void arcJoin(float x, float y, float dx1, float dy1, float dx2, float dy2) {
2028+
//we don't need to normalize before doing these products
2029+
//since the vectors are the same length and only used as arguments to atan2()
2030+
float cross = dx1 * dy2 - dy1 * dx2;
2031+
float dot = dx1 * dx2 + dy1 * dy2;
2032+
float theta = PApplet.atan2(cross, dot);
2033+
int segments = circleDetail(r, theta);
2034+
float px = x + dx1, py = y + dy1;
2035+
if (segments > 1) {
2036+
float c = PApplet.cos(theta / segments);
2037+
float s = PApplet.sin(theta / segments);
2038+
for (int i = 1; i < segments; ++i) {
2039+
//this is the equivalent of multiplying the vector <dx1, dy1> by the 2x2 rotation matrix [[c -s] [s c]]
2040+
float tempx = c * dx1 - s * dy1;
2041+
dy1 = s * dx1 + c * dy1;
2042+
dx1 = tempx;
2043+
2044+
float nx = x + dx1;
2045+
float ny = y + dy1;
2046+
triangle(x, y, px, py, nx, ny, strokeColor);
2047+
px = nx;
2048+
py = ny;
2049+
}
2050+
}
2051+
triangle(x, y, px, py, x + dx2, y + dy2, strokeColor);
2052+
}
2053+
20362054
void beginLine() {
20372055
lineVertexCount = 0;
20382056
r = strokeWeight * 0.5f;
@@ -2054,23 +2072,23 @@ void lineVertex(float x, float y) {
20542072
sx = x;
20552073
sy = y;
20562074
} else {
2057-
//find leg angles
2058-
float angle1 = PApplet.atan2(lx - px, ly - py);
2059-
float angle2 = PApplet.atan2(lx - x, ly - y);
2060-
2061-
//find minimum absolute angle between the two legs
2062-
//FROM: https://stackoverflow.com/a/7869457/3064745
2063-
//NOTE: this only works for angles that are in range [-180, 180] !!!
2064-
float diff = angle1 - angle2;
2065-
diff += diff > PI? -TWO_PI : diff < -PI? TWO_PI : 0;
2066-
2067-
if (strokeJoin == BEVEL || strokeJoin == ROUND ||
2068-
PApplet.abs(diff) < PI/15 || PApplet.abs(diff) > PI - 0.001f) {
2069-
float dx = lx - px;
2070-
float dy = ly - py;
2071-
float d = PApplet.sqrt(dx*dx + dy*dy);
2072-
float tx = dy / d * r;
2073-
float ty = -dx / d * r;
2075+
//calculate normalized direction vectors for each leg
2076+
float leg1x = lx - px;
2077+
float leg1y = ly - py;
2078+
float leg2x = x - lx;
2079+
float leg2y = y - ly;
2080+
float len1 = PApplet.sqrt(leg1x * leg1x + leg1y * leg1y);
2081+
float len2 = PApplet.sqrt(leg2x * leg2x + leg2y * leg2y);
2082+
leg1x /= len1;
2083+
leg1y /= len1;
2084+
leg2x /= len2;
2085+
leg2y /= len2;
2086+
2087+
float legDot = -leg1x * leg2x - leg1y * leg2y;
2088+
float cosPiOver15 = 0.97815f;
2089+
if (strokeJoin == BEVEL || strokeJoin == ROUND || legDot > cosPiOver15 || legDot < -0.999) {
2090+
float tx = leg1y * r;
2091+
float ty = -leg1x * r;
20742092

20752093
if (lineVertexCount == 2) {
20762094
sdx = tx;
@@ -2080,48 +2098,35 @@ void lineVertex(float x, float y) {
20802098
triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor);
20812099
}
20822100

2083-
dx = x - lx;
2084-
dy = y - ly;
2085-
d = PApplet.sqrt(dx*dx + dy*dy);
2086-
float nx = dy / d * r;
2087-
float ny = -dx / d * r;
2101+
float nx = leg2y * r;
2102+
float ny = -leg2x * r;
20882103

2104+
float legCross = leg1x * leg2y - leg1y * leg2x;
20892105
if (strokeJoin == ROUND) {
2090-
float theta1 = diff > 0? angle1 - HALF_PI : angle1 + HALF_PI;
2091-
float theta2 = diff > 0? angle2 + HALF_PI : angle2 - HALF_PI;
2092-
2093-
//find minimum absolute angle diff (again)
2094-
float delta = theta2 - theta1;
2095-
delta += delta > PI? -TWO_PI : delta < -PI? TWO_PI : 0;
2096-
2097-
//start and end points of arc
2098-
float ax1 = diff < 0? lx + tx : lx - tx;
2099-
float ay1 = diff < 0? ly + ty : ly - ty;
2100-
float ax2 = diff < 0? lx + nx : lx - nx;
2101-
float ay2 = diff < 0? ly + ny : ly - ny;
2102-
2103-
arcJoin(lx, ly, theta1, delta, ax1, ay1, ax2, ay2);
2104-
} else if (diff < 0) {
2106+
if (legCross > 0) {
2107+
arcJoin(lx, ly, tx, ty, nx, ny);
2108+
} else {
2109+
arcJoin(lx, ly, -tx, -ty, -nx, -ny);
2110+
}
2111+
} else if (legCross > 0) {
21052112
triangle(lx, ly, lx + tx, ly + ty, lx + nx, ly + ny, strokeColor);
21062113
} else {
21072114
triangle(lx, ly, lx - tx, ly - ty, lx - nx, ly - ny, strokeColor);
21082115
}
21092116

21102117
pdx = nx;
21112118
pdy = ny;
2112-
} else {
2113-
//find offset (hypotenuse) of miter joint
2114-
float theta = HALF_PI - diff/2;
2115-
float offset = r / PApplet.cos(theta);
2116-
2117-
//find bisecting vector
2118-
float angle = (angle1 + angle2)/2;
2119-
float bx = PApplet.sin(angle) * offset;
2120-
float by = PApplet.cos(angle) * offset;
2121-
if (PApplet.abs(angle1 - angle2) < PI) {
2122-
bx *= -1;
2123-
by *= -1;
2124-
}
2119+
} else { //miter joint
2120+
//find the bisecting vector
2121+
float x1 = leg2x - leg1x;
2122+
float y1 = leg2y - leg1y;
2123+
//find a (normalized) vector perpendicular to one of the legs
2124+
float x2 = leg1y;
2125+
float y2 = -leg1x;
2126+
//scale the bisecting vector to the correct length using magic (not sure how to explain this one)
2127+
float dot = x1 * x2 + y1 * y2;
2128+
float bx = x1 * (r / dot);
2129+
float by = y1 * (r / dot);
21252130

21262131
if (lineVertexCount == 2) {
21272132
sdx = bx;
@@ -2144,6 +2149,26 @@ void lineVertex(float x, float y) {
21442149
lineVertexCount += 1;
21452150
}
21462151

2152+
void lineCap(float x, float y, float dx, float dy) {
2153+
int segments = circleDetail(r, HALF_PI);
2154+
float px = dy, py = -dx;
2155+
if (segments > 1) {
2156+
float c = PApplet.cos(HALF_PI / segments);
2157+
float s = PApplet.sin(HALF_PI / segments);
2158+
for (int i = 1; i < segments; ++i) {
2159+
//this is the equivalent of multiplying the vector <px, py> by the 2x2 rotation matrix [[c -s] [s c]]
2160+
float nx = c * px - s * py;
2161+
float ny = s * px + c * py;
2162+
triangle(x, y, x + px, y + py, x + nx, y + ny, strokeColor);
2163+
triangle(x, y, x - py, y + px, x - ny, y + nx, strokeColor);
2164+
px = nx;
2165+
py = ny;
2166+
}
2167+
}
2168+
triangle(x, y, x + px, y + py, x + dx, y + dy, strokeColor);
2169+
triangle(x, y, x - py, y + px, x - dy, y + dx, strokeColor);
2170+
}
2171+
21472172
void endLine(boolean closed) {
21482173
if (lineVertexCount < 2) {
21492174
return;
@@ -2186,7 +2211,7 @@ void endLine(boolean closed) {
21862211
triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor);
21872212

21882213
if (strokeCap == ROUND) {
2189-
lineCap(lx, ly, PApplet.atan2(dx, dy));
2214+
lineCap(lx, ly, -ty, tx);
21902215
}
21912216

21922217
//draw first line (with cap)
@@ -2205,52 +2230,10 @@ void endLine(boolean closed) {
22052230
triangle(sx + sdx, sy + sdy, fx + tx, fy + ty, fx - tx, fy - ty, strokeColor);
22062231

22072232
if (strokeCap == ROUND) {
2208-
lineCap(fx, fy, PApplet.atan2(dx, dy));
2233+
lineCap(fx, fy, -ty, tx);
22092234
}
22102235
}
22112236
}
2212-
2213-
void arcJoin(float x, float y, float start, float delta, float x1, float y1, float x3, float y3) {
2214-
int segments = circleDetail(r, delta);
2215-
float step = delta / segments;
2216-
2217-
for (int i = 0; i < segments - 1; ++i) {
2218-
start += step;
2219-
float x2 = x + PApplet.sin(start) * r;
2220-
float y2 = y + PApplet.cos(start) * r;
2221-
2222-
triangle(x, y, x1, y1, x2, y2, strokeColor);
2223-
2224-
x1 = x2;
2225-
y1 = y2;
2226-
}
2227-
2228-
triangle(x, y, x1, y1, x3, y3, strokeColor);
2229-
}
2230-
2231-
//XXX: wet code, will probably get removed when we optimize lineCap()
2232-
void arcJoin(float x, float y, float start, float delta) {
2233-
int segments = circleDetail(r, delta);
2234-
float step = delta / segments;
2235-
2236-
float x1 = x + PApplet.sin(start) * r;
2237-
float y1 = y + PApplet.cos(start) * r;
2238-
for (int i = 0; i < segments; ++i) {
2239-
start += step;
2240-
float x2 = x + PApplet.sin(start) * r;
2241-
float y2 = y + PApplet.cos(start) * r;
2242-
2243-
triangle(x, y, x1, y1, x2, y2, strokeColor);
2244-
2245-
x1 = x2;
2246-
y1 = y2;
2247-
}
2248-
}
2249-
2250-
void lineCap(float x, float y, float angle) {
2251-
//TODO: optimize this
2252-
arcJoin(x, y, angle - HALF_PI, PI);
2253-
}
22542237
}
22552238

22562239

0 commit comments

Comments
 (0)