@@ -849,9 +849,6 @@ public void ellipseImpl(float a, float b, float c, float d) {
849
849
return ;
850
850
}
851
851
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
-
855
852
beginShape (POLYGON );
856
853
857
854
//convert corner/diameter to center/radius
@@ -866,10 +863,15 @@ public void ellipseImpl(float a, float b, float c, float d) {
866
863
int segments = circleDetail (PApplet .max (rx , ry ) + (stroke ? strokeWeight : 0 ), TWO_PI );
867
864
float step = TWO_PI / segments ;
868
865
869
- float angle = 0 ;
866
+ float cos = PApplet .cos (step );
867
+ float sin = PApplet .sin (step );
868
+ float dx = 0 , dy = 1 ;
870
869
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 ;
873
875
}
874
876
875
877
knownConvexPolygon = true ;
@@ -934,17 +936,20 @@ protected void arcImpl(float x, float y, float w, float h, float start, float st
934
936
appendContour (vertCount );
935
937
}
936
938
939
+ float dx = PApplet .cos (start );
940
+ float dy = PApplet .sin (start );
941
+ float c = PApplet .cos (step );
942
+ float s = PApplet .sin (step );
937
943
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 ;
944
949
}
945
950
946
951
//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
948
953
knownConvexPolygon = true ;
949
954
if (mode == CHORD || mode == PIE ) {
950
955
endShape (CLOSE );
@@ -1913,9 +1918,9 @@ private void postMatrixChanged() {
1913
1918
float syi = projmodelview .m10 * height / 2 ;
1914
1919
float sxj = projmodelview .m01 * width / 2 ;
1915
1920
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 ) );
1919
1924
}
1920
1925
1921
1926
@@ -1952,55 +1957,40 @@ private void singleLine(float x1, float y1, float x2, float y2, int color) {
1952
1957
triangle (x2 + tx , y2 - ty , x2 - tx , y2 + ty , x1 + tx , y1 - ty , color );
1953
1958
1954
1959
if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND ) {
1955
- float angle = PApplet .atan2 (dx , dy );
1956
-
1957
1960
int segments = circleDetail (r , HALF_PI );
1958
1961
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 ;
1959
1968
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 );
1971
1973
1972
- psin = nsin ;
1973
- pcos = ncos ;
1974
+ tx = nx ;
1975
+ ty = ny ;
1974
1976
}
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 );
1980
1977
}
1981
1978
}
1982
1979
1983
1980
1984
1981
private void singlePoint (float x , float y , int color ) {
1985
1982
float r = strokeWeight * 0.5f ;
1986
- if (strokeCap == ROUND ) {
1983
+ if (r >= LINE_DETAIL_LIMIT && strokeCap == ROUND ) {
1987
1984
int segments = circleDetail (r );
1988
1985
float step = QUARTER_PI / segments ;
1989
1986
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 ) ;
1993
1990
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 ;
2004
1994
2005
1995
triangle (x , y , x + x1 , y + y1 , x + x2 , y + y2 , strokeColor );
2006
1996
triangle (x , y , x + x1 , y - y1 , x + x2 , y - y2 , strokeColor );
@@ -2033,6 +2023,34 @@ private class StrokeRenderer {
2033
2023
float lx , ly ;
2034
2024
float r ;
2035
2025
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
+
2036
2054
void beginLine () {
2037
2055
lineVertexCount = 0 ;
2038
2056
r = strokeWeight * 0.5f ;
@@ -2054,23 +2072,23 @@ void lineVertex(float x, float y) {
2054
2072
sx = x ;
2055
2073
sy = y ;
2056
2074
} 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 ;
2074
2092
2075
2093
if (lineVertexCount == 2 ) {
2076
2094
sdx = tx ;
@@ -2080,48 +2098,35 @@ void lineVertex(float x, float y) {
2080
2098
triangle (px + pdx , py + pdy , lx - tx , ly - ty , lx + tx , ly + ty , strokeColor );
2081
2099
}
2082
2100
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 ;
2088
2103
2104
+ float legCross = leg1x * leg2y - leg1y * leg2x ;
2089
2105
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 ) {
2105
2112
triangle (lx , ly , lx + tx , ly + ty , lx + nx , ly + ny , strokeColor );
2106
2113
} else {
2107
2114
triangle (lx , ly , lx - tx , ly - ty , lx - nx , ly - ny , strokeColor );
2108
2115
}
2109
2116
2110
2117
pdx = nx ;
2111
2118
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 );
2125
2130
2126
2131
if (lineVertexCount == 2 ) {
2127
2132
sdx = bx ;
@@ -2144,6 +2149,26 @@ void lineVertex(float x, float y) {
2144
2149
lineVertexCount += 1 ;
2145
2150
}
2146
2151
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
+
2147
2172
void endLine (boolean closed ) {
2148
2173
if (lineVertexCount < 2 ) {
2149
2174
return ;
@@ -2186,7 +2211,7 @@ void endLine(boolean closed) {
2186
2211
triangle (px + pdx , py + pdy , lx - tx , ly - ty , lx + tx , ly + ty , strokeColor );
2187
2212
2188
2213
if (strokeCap == ROUND ) {
2189
- lineCap (lx , ly , PApplet . atan2 ( dx , dy ) );
2214
+ lineCap (lx , ly , - ty , tx );
2190
2215
}
2191
2216
2192
2217
//draw first line (with cap)
@@ -2205,52 +2230,10 @@ void endLine(boolean closed) {
2205
2230
triangle (sx + sdx , sy + sdy , fx + tx , fy + ty , fx - tx , fy - ty , strokeColor );
2206
2231
2207
2232
if (strokeCap == ROUND ) {
2208
- lineCap (fx , fy , PApplet . atan2 ( dx , dy ) );
2233
+ lineCap (fx , fy , - ty , tx );
2209
2234
}
2210
2235
}
2211
2236
}
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
- }
2254
2237
}
2255
2238
2256
2239
0 commit comments