Last active
June 14, 2019 22:32
-
-
Save notnullnotvoid/667bd229cc4ab5c93cbc7b1ae21e23c9 to your computer and use it in GitHub Desktop.
Newly optimized versions of P2DX methods
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//NOTE: here I've included only the methods that I changed. | |
@Override | |
public void ellipseImpl(float a, float b, float c, float d) { | |
if (useParentImpl) { | |
super.ellipseImpl(a, b, c, d); | |
return; | |
} | |
beginShape(POLYGON); | |
//convert corner/diameter to center/radius | |
float rx = c * 0.5f; | |
float ry = d * 0.5f; | |
float x = a + rx; | |
float y = b + ry; | |
//since very wide stroke and/or very small radius might cause the | |
//stroke to account for a significant portion of the overall radius, | |
//we take it into account when calculating detail, just to be safe | |
int segments = circleDetail(PApplet.max(rx, ry) + (stroke? strokeWeight : 0), TWO_PI); | |
float step = TWO_PI / segments; | |
float cos = PApplet.cos(step); | |
float sin = PApplet.sin(step); | |
float dx = 0, dy = 1; | |
for (int i = 0; i < segments; ++i) { | |
shapeVertex(x + dx * rx, y + dy * ry, 0, 0, fillColor, 0); | |
//this is the equivalent of multiplying the vector <dx, dy> by the 2x2 rotation matrix [[cos -sin] [sin cos]] | |
float tempx = dx * cos - dy * sin; | |
dy = dx * sin + dy * cos; | |
dx = tempx; | |
} | |
knownConvexPolygon = true; | |
endShape(CLOSE); | |
} | |
@Override | |
protected void arcImpl(float x, float y, float w, float h, float start, float stop, int mode) { | |
if (useParentImpl) { | |
super.arcImpl(x, y, w, h, start, stop, mode); | |
return; | |
} | |
//INVARIANT: stop > start | |
//INVARIANT: stop - start <= TWO_PI | |
//convert corner/diameter to center/radius | |
w *= 0.5f; | |
h *= 0.5f; | |
x += w; | |
y += h; | |
float diff = stop - start; | |
int segments = circleDetail(PApplet.max(w, h), diff); | |
float step = diff / segments; | |
beginShape(POLYGON); | |
//no constant is defined for the default arc mode, so we just use a literal 0 | |
//(this is consistent with code elsewhere) | |
if (mode == 0 || mode == PIE) { | |
vertex(x, y); | |
} | |
if (mode == 0) { | |
//kinda hacky way to disable drawing a stroke along the first edge | |
appendContour(vertCount); | |
} | |
float dx = PApplet.cos(start); | |
float dy = PApplet.sin(start); | |
float c = PApplet.cos(step); | |
float s = PApplet.sin(step); | |
for (int i = 0; i <= segments; ++i) { | |
shapeVertex(x + dx * w, y + dy * h, 0, 0, fillColor, 0); | |
//this is the equivalent of multiplying the vector <dx, dy> by the 2x2 rotation matrix [[c -s] [s c]] | |
float tempx = dx * c - dy * s; | |
dy = dx * s + dy * c; | |
dx = tempx; | |
} | |
//for the case `(mode == PIE || mode == 0) && diff > HALF_PI`, the polygon | |
//will not actually be convex, but due to known vertex order, we can still safely tessellate as if it is | |
knownConvexPolygon = true; | |
if (mode == CHORD || mode == PIE) { | |
endShape(CLOSE); | |
} else { | |
endShape(); | |
} | |
} | |
void postMatrixChanged() { | |
//this serves as a rough approximation of how much the longest axis | |
//of an ellipse will be scaled by a given matrix | |
//(in other words, the amount by which its on-screen size changes) | |
float sxi = projmodelview.m00 * width / 2; | |
float syi = projmodelview.m10 * height / 2; | |
float sxj = projmodelview.m01 * width / 2; | |
float syj = projmodelview.m11 * height / 2; | |
float Imag2 = sxi * sxi + syi * syi; | |
float Jmag2 = sxj * sxj + syj * syj; | |
ellipseDetailMultiplier = PApplet.sqrt(PApplet.max(Imag2, Jmag2)); | |
} | |
void singleLine(float x1, float y1, float x2, float y2, int color) { | |
float r = strokeWeight * 0.5f; | |
float dx = x2 - x1; | |
float dy = y2 - y1; | |
float d = PApplet.sqrt(dx*dx + dy*dy); | |
float tx = dy / d * r; | |
float ty = dx / d * r; | |
if (strokeCap == PROJECT) { | |
x1 -= ty; | |
x2 += ty; | |
y1 -= tx; | |
y2 += tx; | |
} | |
triangle(x1 - tx, y1 + ty, x1 + tx, y1 - ty, x2 - tx, y2 + ty, color); | |
triangle(x2 + tx, y2 - ty, x2 - tx, y2 + ty, x1 + tx, y1 - ty, color); | |
if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) { | |
int segments = circleDetail(r, HALF_PI); | |
float step = HALF_PI / segments; | |
float c = PApplet.cos(step); | |
float s = PApplet.sin(step); | |
for (int i = 0; i < segments; ++i) { | |
//this is the equivalent of multiplying the vector <tx, ty> by the 2x2 rotation matrix [[c -s] [s c]] | |
float nx = c * tx - s * ty; | |
float ny = s * tx + c * ty; | |
triangle(x2, y2, x2 + ty, y2 + tx, x2 + ny, y2 + nx, color); | |
triangle(x2, y2, x2 - tx, y2 + ty, x2 - nx, y2 + ny, color); | |
triangle(x1, y1, x1 - ty, y1 - tx, x1 - ny, y1 - nx, color); | |
triangle(x1, y1, x1 + tx, y1 - ty, x1 + nx, y1 - ny, color); | |
tx = nx; | |
ty = ny; | |
} | |
} | |
} | |
void singlePoint(float x, float y, int color) { | |
float r = strokeWeight * 0.5f; | |
if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND) { | |
int segments = circleDetail(r); | |
float step = QUARTER_PI / segments; | |
float x1 = 0, y1 = r; | |
float c = PApplet.cos(step); | |
float s = PApplet.sin(step); | |
for (int i = 0; i < segments; ++i) { | |
//this is the equivalent of multiplying the vector <x1, y1> by the 2x2 rotation matrix [[c -s] [s c]] | |
float x2 = c * x1 - s * y1; | |
float y2 = s * x1 + c * y1; | |
triangle(x, y, x + x1, y + y1, x + x2, y + y2, strokeColor); | |
triangle(x, y, x + x1, y - y1, x + x2, y - y2, strokeColor); | |
triangle(x, y, x - x1, y + y1, x - x2, y + y2, strokeColor); | |
triangle(x, y, x - x1, y - y1, x - x2, y - y2, strokeColor); | |
triangle(x, y, x + y1, y + x1, x + y2, y + x2, strokeColor); | |
triangle(x, y, x + y1, y - x1, x + y2, y - x2, strokeColor); | |
triangle(x, y, x - y1, y + x1, x - y2, y + x2, strokeColor); | |
triangle(x, y, x - y1, y - x1, x - y2, y - x2, strokeColor); | |
x1 = x2; | |
y1 = y2; | |
} | |
} else { | |
triangle(x - r, y - r, x + r, y - r, x - r, y + r, color); | |
triangle(x + r, y - r, x - r, y + r, x + r, y + r, color); | |
} | |
} | |
private class StrokeRenderer { | |
int lineVertexCount; | |
float fx, fy; | |
float sx, sy, sdx, sdy; | |
float px, py, pdx, pdy; | |
float lx, ly; | |
float r; | |
void arcJoin(float x, float y, float dx1, float dy1, float dx2, float dy2) { | |
//we don't need to normalize before doing these products | |
//since the vectors are the same length and only used as arguments to atan2() | |
float cross = dx1 * dy2 - dy1 * dx2; | |
float dot = dx1 * dx2 + dy1 * dy2; | |
float theta = PApplet.atan2(cross, dot); | |
int segments = circleDetail(r, theta); | |
float px = x + dx1, py = y + dy1; | |
if (segments > 1) { | |
float c = PApplet.cos(theta / segments); | |
float s = PApplet.sin(theta / segments); | |
for (int i = 1; i < segments; ++i) { | |
//this is the equivalent of multiplying the vector <dx1, dy1> by the 2x2 rotation matrix [[c -s] [s c]] | |
float tempx = c * dx1 - s * dy1; | |
dy1 = s * dx1 + c * dy1; | |
dx1 = tempx; | |
float nx = x + dx1; | |
float ny = y + dy1; | |
triangle(x, y, px, py, nx, ny, strokeColor); | |
px = nx; | |
py = ny; | |
} | |
} | |
triangle(x, y, px, py, x + dx2, y + dy2, strokeColor); | |
} | |
void beginLine() { | |
lineVertexCount = 0; | |
r = strokeWeight * 0.5f; | |
} | |
void lineVertex(float x, float y) { | |
//disallow adding consecutive duplicate vertices, | |
//as it is pointless and just creates an extra edge case | |
if (lineVertexCount > 0 && x == lx && y == ly) { | |
return; | |
} | |
if (lineVertexCount == 0) { | |
fx = x; | |
fy = y; | |
} else if (r < LINE_DETAIL_LIMIT) { | |
singleLine(lx, ly, x, y, strokeColor); | |
} else if (lineVertexCount == 1) { | |
sx = x; | |
sy = y; | |
} else { | |
//calculate normalized direction vectors for each leg | |
float leg1x = lx - px; | |
float leg1y = ly - py; | |
float leg2x = x - lx; | |
float leg2y = y - ly; | |
float len1 = PApplet.sqrt(leg1x * leg1x + leg1y * leg1y); | |
float len2 = PApplet.sqrt(leg2x * leg2x + leg2y * leg2y); | |
leg1x /= len1; | |
leg1y /= len1; | |
leg2x /= len2; | |
leg2y /= len2; | |
float legDot = -leg1x * leg2x - leg1y * leg2y; | |
float cosPiOver15 = 0.97815f; | |
if (strokeJoin == BEVEL || strokeJoin == ROUND || legDot > cosPiOver15 || legDot < -0.999) { | |
float tx = leg1y * r; | |
float ty = -leg1x * r; | |
if (lineVertexCount == 2) { | |
sdx = tx; | |
sdy = ty; | |
} else { | |
triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - tx, ly - ty, strokeColor); | |
triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor); | |
} | |
float nx = leg2y * r; | |
float ny = -leg2x * r; | |
float legCross = leg1x * leg2y - leg1y * leg2x; | |
if (strokeJoin == ROUND) { | |
if (legCross > 0) { | |
arcJoin(lx, ly, tx, ty, nx, ny); | |
} else { | |
arcJoin(lx, ly, -tx, -ty, -nx, -ny); | |
} | |
} else if (legCross > 0) { | |
triangle(lx, ly, lx + tx, ly + ty, lx + nx, ly + ny, strokeColor); | |
} else { | |
triangle(lx, ly, lx - tx, ly - ty, lx - nx, ly - ny, strokeColor); | |
} | |
pdx = nx; | |
pdy = ny; | |
} else { //miter joint | |
//find the bisecting vector | |
float x1 = leg2x - leg1x; | |
float y1 = leg2y - leg1y; | |
//find a (normalized) vector perpendicular to one of the legs | |
float x2 = leg1y; | |
float y2 = -leg1x; | |
//scale the bisecting vector to the correct length using magic (not sure how to explain this one) | |
float dot = x1 * x2 + y1 * y2; | |
float bx = x1 * (r / dot); | |
float by = y1 * (r / dot); | |
if (lineVertexCount == 2) { | |
sdx = bx; | |
sdy = by; | |
} else { | |
triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - bx, ly - by, strokeColor); | |
triangle(px + pdx, py + pdy, lx - bx, ly - by, lx + bx, ly + by, strokeColor); | |
} | |
pdx = bx; | |
pdy = by; | |
} | |
} | |
px = lx; | |
py = ly; | |
lx = x; | |
ly = y; | |
lineVertexCount += 1; | |
} | |
void lineCap(float x, float y, float dx, float dy) { | |
int segments = circleDetail(r, HALF_PI); | |
float px = dy, py = -dx; | |
if (segments > 1) { | |
float c = PApplet.cos(HALF_PI / segments); | |
float s = PApplet.sin(HALF_PI / segments); | |
for (int i = 1; i < segments; ++i) { | |
//this is the equivalent of multiplying the vector <px, py> by the 2x2 rotation matrix [[c -s] [s c]] | |
float nx = c * px - s * py; | |
float ny = s * px + c * py; | |
triangle(x, y, x + px, y + py, x + nx, y + ny, strokeColor); | |
triangle(x, y, x - py, y + px, x - ny, y + nx, strokeColor); | |
px = nx; | |
py = ny; | |
} | |
} | |
triangle(x, y, x + px, y + py, x + dx, y + dy, strokeColor); | |
triangle(x, y, x - py, y + px, x - dy, y + dx, strokeColor); | |
} | |
void endLine(boolean closed) { | |
if (lineVertexCount < 2) { | |
return; | |
} | |
if (lineVertexCount == 2) { | |
singleLine(px, py, lx, ly, strokeColor); | |
return; | |
} | |
if (r < LINE_DETAIL_LIMIT) { | |
if (closed) { | |
singleLine(lx, ly, fx, fy, strokeColor); | |
} | |
return; | |
} | |
if (closed) { | |
//draw the last two legs | |
lineVertex(fx, fy); | |
lineVertex(sx, sy); | |
//connect first and second vertices | |
triangle(px - pdx, py - pdy, px + pdx, py + pdy, sx - sdx, sy - sdy, strokeColor); | |
triangle(px + pdx, py + pdy, sx - sdx, sy - sdy, sx + sdx, sy + sdy, strokeColor); | |
} else { | |
//draw last line (with cap) | |
float dx = lx - px; | |
float dy = ly - py; | |
float d = PApplet.sqrt(dx*dx + dy*dy); | |
float tx = dy / d * r; | |
float ty = -dx / d * r; | |
if (strokeCap == PROJECT) { | |
lx -= ty; | |
ly += tx; | |
} | |
triangle(px - pdx, py - pdy, px + pdx, py + pdy, lx - tx, ly - ty, strokeColor); | |
triangle(px + pdx, py + pdy, lx - tx, ly - ty, lx + tx, ly + ty, strokeColor); | |
if (strokeCap == ROUND) { | |
lineCap(lx, ly, -ty, tx); | |
} | |
//draw first line (with cap) | |
dx = fx - sx; | |
dy = fy - sy; | |
d = PApplet.sqrt(dx*dx + dy*dy); | |
tx = dy / d * r; | |
ty = -dx / d * r; | |
if (strokeCap == PROJECT) { | |
fx -= ty; | |
fy += tx; | |
} | |
triangle(sx - sdx, sy - sdy, sx + sdx, sy + sdy, fx + tx, fy + ty, strokeColor); | |
triangle(sx + sdx, sy + sdy, fx + tx, fy + ty, fx - tx, fy - ty, strokeColor); | |
if (strokeCap == ROUND) { | |
lineCap(fx, fy, -ty, tx); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment