Anti-Grain Geometry - AGG (libagg)
2.5
|
00001 //---------------------------------------------------------------------------- 00002 // Anti-Grain Geometry (AGG) - Version 2.5 00003 // A high quality rendering engine for C++ 00004 // Copyright (C) 2002-2006 Maxim Shemanarev 00005 // Contact: mcseem@antigrain.com 00006 // mcseemagg@yahoo.com 00007 // http://antigrain.com 00008 // 00009 // AGG is free software; you can redistribute it and/or 00010 // modify it under the terms of the GNU General Public License 00011 // as published by the Free Software Foundation; either version 2 00012 // of the License, or (at your option) any later version. 00013 // 00014 // AGG is distributed in the hope that it will be useful, 00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 // GNU General Public License for more details. 00018 // 00019 // You should have received a copy of the GNU General Public License 00020 // along with AGG; if not, write to the Free Software 00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00022 // MA 02110-1301, USA. 00023 //---------------------------------------------------------------------------- 00024 00025 #ifndef AGG_STROKE_MATH_INCLUDED 00026 #define AGG_STROKE_MATH_INCLUDED 00027 00028 #include "agg_math.h" 00029 #include "agg_vertex_sequence.h" 00030 00031 namespace agg 00032 { 00033 //-------------------------------------------------------------line_cap_e 00034 enum line_cap_e 00035 { 00036 butt_cap, 00037 square_cap, 00038 round_cap 00039 }; 00040 00041 //------------------------------------------------------------line_join_e 00042 enum line_join_e 00043 { 00044 miter_join = 0, 00045 miter_join_revert = 1, 00046 round_join = 2, 00047 bevel_join = 3, 00048 miter_join_round = 4 00049 }; 00050 00051 00052 //-----------------------------------------------------------inner_join_e 00053 enum inner_join_e 00054 { 00055 inner_bevel, 00056 inner_miter, 00057 inner_jag, 00058 inner_round 00059 }; 00060 00061 //------------------------------------------------------------math_stroke 00062 template<class VertexConsumer> class math_stroke 00063 { 00064 public: 00065 typedef typename VertexConsumer::value_type coord_type; 00066 00067 math_stroke(); 00068 00069 void line_cap(line_cap_e lc) { m_line_cap = lc; } 00070 void line_join(line_join_e lj) { m_line_join = lj; } 00071 void inner_join(inner_join_e ij) { m_inner_join = ij; } 00072 00073 line_cap_e line_cap() const { return m_line_cap; } 00074 line_join_e line_join() const { return m_line_join; } 00075 inner_join_e inner_join() const { return m_inner_join; } 00076 00077 void width(double w); 00078 void miter_limit(double ml) { m_miter_limit = ml; } 00079 void miter_limit_theta(double t); 00080 void inner_miter_limit(double ml) { m_inner_miter_limit = ml; } 00081 void approximation_scale(double as) { m_approx_scale = as; } 00082 00083 double width() const { return m_width * 2.0; } 00084 double miter_limit() const { return m_miter_limit; } 00085 double inner_miter_limit() const { return m_inner_miter_limit; } 00086 double approximation_scale() const { return m_approx_scale; } 00087 00088 void calc_cap(VertexConsumer& vc, 00089 const vertex_dist& v0, 00090 const vertex_dist& v1, 00091 double len); 00092 00093 void calc_join(VertexConsumer& vc, 00094 const vertex_dist& v0, 00095 const vertex_dist& v1, 00096 const vertex_dist& v2, 00097 double len1, 00098 double len2); 00099 00100 private: 00101 AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y) 00102 { 00103 vc.add(coord_type(x, y)); 00104 } 00105 00106 void calc_arc(VertexConsumer& vc, 00107 double x, double y, 00108 double dx1, double dy1, 00109 double dx2, double dy2); 00110 00111 void calc_miter(VertexConsumer& vc, 00112 const vertex_dist& v0, 00113 const vertex_dist& v1, 00114 const vertex_dist& v2, 00115 double dx1, double dy1, 00116 double dx2, double dy2, 00117 line_join_e lj, 00118 double mlimit, 00119 double dbevel); 00120 00121 double m_width; 00122 double m_width_abs; 00123 double m_width_eps; 00124 int m_width_sign; 00125 double m_miter_limit; 00126 double m_inner_miter_limit; 00127 double m_approx_scale; 00128 line_cap_e m_line_cap; 00129 line_join_e m_line_join; 00130 inner_join_e m_inner_join; 00131 }; 00132 00133 //----------------------------------------------------------------------- 00134 template<class VC> math_stroke<VC>::math_stroke() : 00135 m_width(0.5), 00136 m_width_abs(0.5), 00137 m_width_eps(0.5/1024.0), 00138 m_width_sign(1), 00139 m_miter_limit(4.0), 00140 m_inner_miter_limit(1.01), 00141 m_approx_scale(1.0), 00142 m_line_cap(butt_cap), 00143 m_line_join(miter_join), 00144 m_inner_join(inner_miter) 00145 { 00146 } 00147 00148 //----------------------------------------------------------------------- 00149 template<class VC> void math_stroke<VC>::width(double w) 00150 { 00151 m_width = w * 0.5; 00152 if(m_width < 0) 00153 { 00154 m_width_abs = -m_width; 00155 m_width_sign = -1; 00156 } 00157 else 00158 { 00159 m_width_abs = m_width; 00160 m_width_sign = 1; 00161 } 00162 m_width_eps = m_width / 1024.0; 00163 } 00164 00165 //----------------------------------------------------------------------- 00166 template<class VC> void math_stroke<VC>::miter_limit_theta(double t) 00167 { 00168 m_miter_limit = 1.0 / sin(t * 0.5) ; 00169 } 00170 00171 //----------------------------------------------------------------------- 00172 template<class VC> 00173 void math_stroke<VC>::calc_arc(VC& vc, 00174 double x, double y, 00175 double dx1, double dy1, 00176 double dx2, double dy2) 00177 { 00178 double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign); 00179 double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign); 00180 double da = a1 - a2; 00181 int i, n; 00182 00183 da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2; 00184 00185 add_vertex(vc, x + dx1, y + dy1); 00186 if(m_width_sign > 0) 00187 { 00188 if(a1 > a2) a2 += 2 * pi; 00189 n = int((a2 - a1) / da); 00190 da = (a2 - a1) / (n + 1); 00191 a1 += da; 00192 for(i = 0; i < n; i++) 00193 { 00194 add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width); 00195 a1 += da; 00196 } 00197 } 00198 else 00199 { 00200 if(a1 < a2) a2 -= 2 * pi; 00201 n = int((a1 - a2) / da); 00202 da = (a1 - a2) / (n + 1); 00203 a1 -= da; 00204 for(i = 0; i < n; i++) 00205 { 00206 add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width); 00207 a1 -= da; 00208 } 00209 } 00210 add_vertex(vc, x + dx2, y + dy2); 00211 } 00212 00213 //----------------------------------------------------------------------- 00214 template<class VC> 00215 void math_stroke<VC>::calc_miter(VC& vc, 00216 const vertex_dist& v0, 00217 const vertex_dist& v1, 00218 const vertex_dist& v2, 00219 double dx1, double dy1, 00220 double dx2, double dy2, 00221 line_join_e lj, 00222 double mlimit, 00223 double dbevel) 00224 { 00225 double xi = v1.x; 00226 double yi = v1.y; 00227 double di = 1; 00228 double lim = m_width_abs * mlimit; 00229 bool miter_limit_exceeded = true; // Assume the worst 00230 bool intersection_failed = true; // Assume the worst 00231 00232 if(calc_intersection(v0.x + dx1, v0.y - dy1, 00233 v1.x + dx1, v1.y - dy1, 00234 v1.x + dx2, v1.y - dy2, 00235 v2.x + dx2, v2.y - dy2, 00236 &xi, &yi)) 00237 { 00238 // Calculation of the intersection succeeded 00239 //--------------------- 00240 di = calc_distance(v1.x, v1.y, xi, yi); 00241 if(di <= lim) 00242 { 00243 // Inside the miter limit 00244 //--------------------- 00245 add_vertex(vc, xi, yi); 00246 miter_limit_exceeded = false; 00247 } 00248 intersection_failed = false; 00249 } 00250 else 00251 { 00252 // Calculation of the intersection failed, most probably 00253 // the three points lie one straight line. 00254 // First check if v0 and v2 lie on the opposite sides of vector: 00255 // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular 00256 // to the line determined by vertices v0 and v1. 00257 // This condition determines whether the next line segments continues 00258 // the previous one or goes back. 00259 //---------------- 00260 double x2 = v1.x + dx1; 00261 double y2 = v1.y - dy1; 00262 if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) == 00263 (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0)) 00264 { 00265 // This case means that the next segment continues 00266 // the previous one (straight line) 00267 //----------------- 00268 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00269 miter_limit_exceeded = false; 00270 } 00271 } 00272 00273 if(miter_limit_exceeded) 00274 { 00275 // Miter limit exceeded 00276 //------------------------ 00277 switch(lj) 00278 { 00279 case miter_join_revert: 00280 // For the compatibility with SVG, PDF, etc, 00281 // we use a simple bevel join instead of 00282 // "smart" bevel 00283 //------------------- 00284 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00285 add_vertex(vc, v1.x + dx2, v1.y - dy2); 00286 break; 00287 00288 case miter_join_round: 00289 calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2); 00290 break; 00291 00292 default: 00293 // If no miter-revert, calculate new dx1, dy1, dx2, dy2 00294 //---------------- 00295 if(intersection_failed) 00296 { 00297 mlimit *= m_width_sign; 00298 add_vertex(vc, v1.x + dx1 + dy1 * mlimit, 00299 v1.y - dy1 + dx1 * mlimit); 00300 add_vertex(vc, v1.x + dx2 - dy2 * mlimit, 00301 v1.y - dy2 - dx2 * mlimit); 00302 } 00303 else 00304 { 00305 double x1 = v1.x + dx1; 00306 double y1 = v1.y - dy1; 00307 double x2 = v1.x + dx2; 00308 double y2 = v1.y - dy2; 00309 di = (lim - dbevel) / (di - dbevel); 00310 add_vertex(vc, x1 + (xi - x1) * di, 00311 y1 + (yi - y1) * di); 00312 add_vertex(vc, x2 + (xi - x2) * di, 00313 y2 + (yi - y2) * di); 00314 } 00315 break; 00316 } 00317 } 00318 } 00319 00320 //--------------------------------------------------------stroke_calc_cap 00321 template<class VC> 00322 void math_stroke<VC>::calc_cap(VC& vc, 00323 const vertex_dist& v0, 00324 const vertex_dist& v1, 00325 double len) 00326 { 00327 vc.remove_all(); 00328 00329 double dx1 = (v1.y - v0.y) / len; 00330 double dy1 = (v1.x - v0.x) / len; 00331 double dx2 = 0; 00332 double dy2 = 0; 00333 00334 dx1 *= m_width; 00335 dy1 *= m_width; 00336 00337 if(m_line_cap != round_cap) 00338 { 00339 if(m_line_cap == square_cap) 00340 { 00341 dx2 = dy1 * m_width_sign; 00342 dy2 = dx1 * m_width_sign; 00343 } 00344 add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2); 00345 add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2); 00346 } 00347 else 00348 { 00349 double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2; 00350 double a1; 00351 int i; 00352 int n = int(pi / da); 00353 00354 da = pi / (n + 1); 00355 add_vertex(vc, v0.x - dx1, v0.y + dy1); 00356 if(m_width_sign > 0) 00357 { 00358 a1 = atan2(dy1, -dx1); 00359 a1 += da; 00360 for(i = 0; i < n; i++) 00361 { 00362 add_vertex(vc, v0.x + cos(a1) * m_width, 00363 v0.y + sin(a1) * m_width); 00364 a1 += da; 00365 } 00366 } 00367 else 00368 { 00369 a1 = atan2(-dy1, dx1); 00370 a1 -= da; 00371 for(i = 0; i < n; i++) 00372 { 00373 add_vertex(vc, v0.x + cos(a1) * m_width, 00374 v0.y + sin(a1) * m_width); 00375 a1 -= da; 00376 } 00377 } 00378 add_vertex(vc, v0.x + dx1, v0.y - dy1); 00379 } 00380 } 00381 00382 //----------------------------------------------------------------------- 00383 template<class VC> 00384 void math_stroke<VC>::calc_join(VC& vc, 00385 const vertex_dist& v0, 00386 const vertex_dist& v1, 00387 const vertex_dist& v2, 00388 double len1, 00389 double len2) 00390 { 00391 double dx1 = m_width * (v1.y - v0.y) / len1; 00392 double dy1 = m_width * (v1.x - v0.x) / len1; 00393 double dx2 = m_width * (v2.y - v1.y) / len2; 00394 double dy2 = m_width * (v2.x - v1.x) / len2; 00395 00396 vc.remove_all(); 00397 00398 double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y); 00399 if(cp != 0 && (cp > 0) == (m_width > 0)) 00400 { 00401 // Inner join 00402 //--------------- 00403 double limit = ((len1 < len2) ? len1 : len2) / m_width_abs; 00404 if(limit < m_inner_miter_limit) 00405 { 00406 limit = m_inner_miter_limit; 00407 } 00408 00409 switch(m_inner_join) 00410 { 00411 default: // inner_bevel 00412 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00413 add_vertex(vc, v1.x + dx2, v1.y - dy2); 00414 break; 00415 00416 case inner_miter: 00417 calc_miter(vc, 00418 v0, v1, v2, dx1, dy1, dx2, dy2, 00419 miter_join_revert, 00420 limit, 0); 00421 break; 00422 00423 case inner_jag: 00424 case inner_round: 00425 cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2); 00426 if(cp < len1 * len1 && cp < len2 * len2) 00427 { 00428 calc_miter(vc, 00429 v0, v1, v2, dx1, dy1, dx2, dy2, 00430 miter_join_revert, 00431 limit, 0); 00432 } 00433 else 00434 { 00435 if(m_inner_join == inner_jag) 00436 { 00437 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00438 add_vertex(vc, v1.x, v1.y ); 00439 add_vertex(vc, v1.x + dx2, v1.y - dy2); 00440 } 00441 else 00442 { 00443 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00444 add_vertex(vc, v1.x, v1.y ); 00445 calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1); 00446 add_vertex(vc, v1.x, v1.y ); 00447 add_vertex(vc, v1.x + dx2, v1.y - dy2); 00448 } 00449 } 00450 break; 00451 } 00452 } 00453 else 00454 { 00455 // Outer join 00456 //--------------- 00457 00458 // Calculate the distance between v1 and 00459 // the central point of the bevel line segment 00460 //--------------- 00461 double dx = (dx1 + dx2) / 2; 00462 double dy = (dy1 + dy2) / 2; 00463 double dbevel = sqrt(dx * dx + dy * dy); 00464 00465 if(m_line_join == round_join || m_line_join == bevel_join) 00466 { 00467 // This is an optimization that reduces the number of points 00468 // in cases of almost collinear segments. If there's no 00469 // visible difference between bevel and miter joins we'd rather 00470 // use miter join because it adds only one point instead of two. 00471 // 00472 // Here we calculate the middle point between the bevel points 00473 // and then, the distance between v1 and this middle point. 00474 // At outer joins this distance always less than stroke width, 00475 // because it's actually the height of an isosceles triangle of 00476 // v1 and its two bevel points. If the difference between this 00477 // width and this value is small (no visible bevel) we can 00478 // add just one point. 00479 // 00480 // The constant in the expression makes the result approximately 00481 // the same as in round joins and caps. You can safely comment 00482 // out this entire "if". 00483 //------------------- 00484 if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps) 00485 { 00486 if(calc_intersection(v0.x + dx1, v0.y - dy1, 00487 v1.x + dx1, v1.y - dy1, 00488 v1.x + dx2, v1.y - dy2, 00489 v2.x + dx2, v2.y - dy2, 00490 &dx, &dy)) 00491 { 00492 add_vertex(vc, dx, dy); 00493 } 00494 else 00495 { 00496 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00497 } 00498 return; 00499 } 00500 } 00501 00502 switch(m_line_join) 00503 { 00504 case miter_join: 00505 case miter_join_revert: 00506 case miter_join_round: 00507 calc_miter(vc, 00508 v0, v1, v2, dx1, dy1, dx2, dy2, 00509 m_line_join, 00510 m_miter_limit, 00511 dbevel); 00512 break; 00513 00514 case round_join: 00515 calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2); 00516 break; 00517 00518 default: // Bevel join 00519 add_vertex(vc, v1.x + dx1, v1.y - dy1); 00520 add_vertex(vc, v1.x + dx2, v1.y - dy2); 00521 break; 00522 } 00523 } 00524 } 00525 00526 00527 00528 00529 } 00530 00531 #endif