@@ -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