Anti-Grain Geometry - AGG (libagg)  2.5
agg-2.5/include/agg_math_stroke.h
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines