1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry (AGG) - Version 2.5
3 // A high quality rendering engine for C++
4 // Copyright (C) 2002-2006 Maxim Shemanarev
5 // Contact: mcseem@antigrain.com
6 //          mcseemagg@yahoo.com
7 //          http://antigrain.com
8 //
9 // AGG is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // AGG is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with AGG; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22 // MA 02110-1301, USA.
23 //----------------------------------------------------------------------------
24 
25 #ifndef AGG_STROKE_MATH_INCLUDED
26 #define AGG_STROKE_MATH_INCLUDED
27 
28 #include "agg_math.h"
29 #include "agg_vertex_sequence.h"
30 
31 namespace agg
32 {
33     //-------------------------------------------------------------line_cap_e
34     enum line_cap_e
35     {
36         butt_cap,
37         square_cap,
38         round_cap
39     };
40 
41     //------------------------------------------------------------line_join_e
42     enum line_join_e
43     {
44         miter_join         = 0,
45         miter_join_revert  = 1,
46         round_join         = 2,
47         bevel_join         = 3,
48         miter_join_round   = 4
49     };
50 
51 
52     //-----------------------------------------------------------inner_join_e
53     enum inner_join_e
54     {
55         inner_bevel,
56         inner_miter,
57         inner_jag,
58         inner_round
59     };
60 
61     //------------------------------------------------------------math_stroke
62     template<class VertexConsumer> class math_stroke
63     {
64     public:
65         typedef typename VertexConsumer::value_type coord_type;
66 
67         math_stroke();
68 
line_cap(line_cap_e lc)69         void line_cap(line_cap_e lc)     { m_line_cap = lc; }
line_join(line_join_e lj)70         void line_join(line_join_e lj)   { m_line_join = lj; }
inner_join(inner_join_e ij)71         void inner_join(inner_join_e ij) { m_inner_join = ij; }
72 
line_cap()73         line_cap_e   line_cap()   const { return m_line_cap; }
line_join()74         line_join_e  line_join()  const { return m_line_join; }
inner_join()75         inner_join_e inner_join() const { return m_inner_join; }
76 
77         void width(double w);
miter_limit(double ml)78         void miter_limit(double ml) { m_miter_limit = ml; }
79         void miter_limit_theta(double t);
inner_miter_limit(double ml)80         void inner_miter_limit(double ml) { m_inner_miter_limit = ml; }
approximation_scale(double as)81         void approximation_scale(double as) { m_approx_scale = as; }
82 
width()83         double width() const { return m_width * 2.0; }
miter_limit()84         double miter_limit() const { return m_miter_limit; }
inner_miter_limit()85         double inner_miter_limit() const { return m_inner_miter_limit; }
approximation_scale()86         double approximation_scale() const { return m_approx_scale; }
87 
88         void calc_cap(VertexConsumer& vc,
89                       const vertex_dist& v0,
90                       const vertex_dist& v1,
91                       double len);
92 
93         void calc_join(VertexConsumer& vc,
94                        const vertex_dist& v0,
95                        const vertex_dist& v1,
96                        const vertex_dist& v2,
97                        double len1,
98                        double len2);
99 
100     private:
add_vertex(VertexConsumer & vc,double x,double y)101         AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y)
102         {
103             vc.add(coord_type(x, y));
104         }
105 
106         void calc_arc(VertexConsumer& vc,
107                       double x,   double y,
108                       double dx1, double dy1,
109                       double dx2, double dy2);
110 
111         void calc_miter(VertexConsumer& vc,
112                         const vertex_dist& v0,
113                         const vertex_dist& v1,
114                         const vertex_dist& v2,
115                         double dx1, double dy1,
116                         double dx2, double dy2,
117                         line_join_e lj,
118                         double mlimit,
119                         double dbevel);
120 
121         double       m_width;
122         double       m_width_abs;
123         double       m_width_eps;
124         int          m_width_sign;
125         double       m_miter_limit;
126         double       m_inner_miter_limit;
127         double       m_approx_scale;
128         line_cap_e   m_line_cap;
129         line_join_e  m_line_join;
130         inner_join_e m_inner_join;
131     };
132 
133     //-----------------------------------------------------------------------
math_stroke()134     template<class VC> math_stroke<VC>::math_stroke() :
135         m_width(0.5),
136         m_width_abs(0.5),
137         m_width_eps(0.5/1024.0),
138         m_width_sign(1),
139         m_miter_limit(4.0),
140         m_inner_miter_limit(1.01),
141         m_approx_scale(1.0),
142         m_line_cap(butt_cap),
143         m_line_join(miter_join),
144         m_inner_join(inner_miter)
145     {
146     }
147 
148     //-----------------------------------------------------------------------
width(double w)149     template<class VC> void math_stroke<VC>::width(double w)
150     {
151         m_width = w * 0.5;
152         if(m_width < 0)
153         {
154             m_width_abs  = -m_width;
155             m_width_sign = -1;
156         }
157         else
158         {
159             m_width_abs  = m_width;
160             m_width_sign = 1;
161         }
162         m_width_eps = m_width / 1024.0;
163     }
164 
165     //-----------------------------------------------------------------------
miter_limit_theta(double t)166     template<class VC> void math_stroke<VC>::miter_limit_theta(double t)
167     {
168         m_miter_limit = 1.0 / sin(t * 0.5) ;
169     }
170 
171     //-----------------------------------------------------------------------
172     template<class VC>
calc_arc(VC & vc,double x,double y,double dx1,double dy1,double dx2,double dy2)173     void math_stroke<VC>::calc_arc(VC& vc,
174                                    double x,   double y,
175                                    double dx1, double dy1,
176                                    double dx2, double dy2)
177     {
178         double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign);
179         double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign);
180         double da = a1 - a2;
181         int i, n;
182 
183         da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
184 
185         add_vertex(vc, x + dx1, y + dy1);
186         if(m_width_sign > 0)
187         {
188             if(a1 > a2) a2 += 2 * pi;
189             n = int((a2 - a1) / da);
190             da = (a2 - a1) / (n + 1);
191             a1 += da;
192             for(i = 0; i < n; i++)
193             {
194                 add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
195                 a1 += da;
196             }
197         }
198         else
199         {
200             if(a1 < a2) a2 -= 2 * pi;
201             n = int((a1 - a2) / da);
202             da = (a1 - a2) / (n + 1);
203             a1 -= da;
204             for(i = 0; i < n; i++)
205             {
206                 add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
207                 a1 -= da;
208             }
209         }
210         add_vertex(vc, x + dx2, y + dy2);
211     }
212 
213     //-----------------------------------------------------------------------
214     template<class VC>
calc_miter(VC & vc,const vertex_dist & v0,const vertex_dist & v1,const vertex_dist & v2,double dx1,double dy1,double dx2,double dy2,line_join_e lj,double mlimit,double dbevel)215     void math_stroke<VC>::calc_miter(VC& vc,
216                                      const vertex_dist& v0,
217                                      const vertex_dist& v1,
218                                      const vertex_dist& v2,
219                                      double dx1, double dy1,
220                                      double dx2, double dy2,
221                                      line_join_e lj,
222                                      double mlimit,
223                                      double dbevel)
224     {
225         double xi  = v1.x;
226         double yi  = v1.y;
227         double di  = 1;
228         double lim = m_width_abs * mlimit;
229         bool miter_limit_exceeded = true; // Assume the worst
230         bool intersection_failed  = true; // Assume the worst
231 
232         if(calc_intersection(v0.x + dx1, v0.y - dy1,
233                              v1.x + dx1, v1.y - dy1,
234                              v1.x + dx2, v1.y - dy2,
235                              v2.x + dx2, v2.y - dy2,
236                              &xi, &yi))
237         {
238             // Calculation of the intersection succeeded
239             //---------------------
240             di = calc_distance(v1.x, v1.y, xi, yi);
241             if(di <= lim)
242             {
243                 // Inside the miter limit
244                 //---------------------
245                 add_vertex(vc, xi, yi);
246                 miter_limit_exceeded = false;
247             }
248             intersection_failed = false;
249         }
250         else
251         {
252             // Calculation of the intersection failed, most probably
253             // the three points lie one straight line.
254             // First check if v0 and v2 lie on the opposite sides of vector:
255             // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
256             // to the line determined by vertices v0 and v1.
257             // This condition determines whether the next line segments continues
258             // the previous one or goes back.
259             //----------------
260             double x2 = v1.x + dx1;
261             double y2 = v1.y - dy1;
262             if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) ==
263                (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0))
264             {
265                 // This case means that the next segment continues
266                 // the previous one (straight line)
267                 //-----------------
268                 add_vertex(vc, v1.x + dx1, v1.y - dy1);
269                 miter_limit_exceeded = false;
270             }
271         }
272 
273         if(miter_limit_exceeded)
274         {
275             // Miter limit exceeded
276             //------------------------
277             switch(lj)
278             {
279             case miter_join_revert:
280                 // For the compatibility with SVG, PDF, etc,
281                 // we use a simple bevel join instead of
282                 // "smart" bevel
283                 //-------------------
284                 add_vertex(vc, v1.x + dx1, v1.y - dy1);
285                 add_vertex(vc, v1.x + dx2, v1.y - dy2);
286                 break;
287 
288             case miter_join_round:
289                 calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
290                 break;
291 
292             default:
293                 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
294                 //----------------
295                 if(intersection_failed)
296                 {
297                     mlimit *= m_width_sign;
298                     add_vertex(vc, v1.x + dx1 + dy1 * mlimit,
299                                    v1.y - dy1 + dx1 * mlimit);
300                     add_vertex(vc, v1.x + dx2 - dy2 * mlimit,
301                                    v1.y - dy2 - dx2 * mlimit);
302                 }
303                 else
304                 {
305                     double x1 = v1.x + dx1;
306                     double y1 = v1.y - dy1;
307                     double x2 = v1.x + dx2;
308                     double y2 = v1.y - dy2;
309                     di = (lim - dbevel) / (di - dbevel);
310                     add_vertex(vc, x1 + (xi - x1) * di,
311                                    y1 + (yi - y1) * di);
312                     add_vertex(vc, x2 + (xi - x2) * di,
313                                    y2 + (yi - y2) * di);
314                 }
315                 break;
316             }
317         }
318     }
319 
320     //--------------------------------------------------------stroke_calc_cap
321     template<class VC>
calc_cap(VC & vc,const vertex_dist & v0,const vertex_dist & v1,double len)322     void math_stroke<VC>::calc_cap(VC& vc,
323                                    const vertex_dist& v0,
324                                    const vertex_dist& v1,
325                                    double len)
326     {
327         vc.remove_all();
328 
329         double dx1 = (v1.y - v0.y) / len;
330         double dy1 = (v1.x - v0.x) / len;
331         double dx2 = 0;
332         double dy2 = 0;
333 
334         dx1 *= m_width;
335         dy1 *= m_width;
336 
337         if(m_line_cap != round_cap)
338         {
339             if(m_line_cap == square_cap)
340             {
341                 dx2 = dy1 * m_width_sign;
342                 dy2 = dx1 * m_width_sign;
343             }
344             add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2);
345             add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2);
346         }
347         else
348         {
349             double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
350             double a1;
351             int i;
352             int n = int(pi / da);
353 
354             da = pi / (n + 1);
355             add_vertex(vc, v0.x - dx1, v0.y + dy1);
356             if(m_width_sign > 0)
357             {
358                 a1 = atan2(dy1, -dx1);
359                 a1 += da;
360                 for(i = 0; i < n; i++)
361                 {
362                     add_vertex(vc, v0.x + cos(a1) * m_width,
363                                    v0.y + sin(a1) * m_width);
364                     a1 += da;
365                 }
366             }
367             else
368             {
369                 a1 = atan2(-dy1, dx1);
370                 a1 -= da;
371                 for(i = 0; i < n; i++)
372                 {
373                     add_vertex(vc, v0.x + cos(a1) * m_width,
374                                    v0.y + sin(a1) * m_width);
375                     a1 -= da;
376                 }
377             }
378             add_vertex(vc, v0.x + dx1, v0.y - dy1);
379         }
380     }
381 
382     //-----------------------------------------------------------------------
383     template<class VC>
calc_join(VC & vc,const vertex_dist & v0,const vertex_dist & v1,const vertex_dist & v2,double len1,double len2)384     void math_stroke<VC>::calc_join(VC& vc,
385                                     const vertex_dist& v0,
386                                     const vertex_dist& v1,
387                                     const vertex_dist& v2,
388                                     double len1,
389                                     double len2)
390     {
391         double dx1 = m_width * (v1.y - v0.y) / len1;
392         double dy1 = m_width * (v1.x - v0.x) / len1;
393         double dx2 = m_width * (v2.y - v1.y) / len2;
394         double dy2 = m_width * (v2.x - v1.x) / len2;
395 
396         vc.remove_all();
397 
398         double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
399         if(cp != 0 && (cp > 0) == (m_width > 0))
400         {
401             // Inner join
402             //---------------
403             double limit = ((len1 < len2) ? len1 : len2) / m_width_abs;
404             if(limit < m_inner_miter_limit)
405             {
406                 limit = m_inner_miter_limit;
407             }
408 
409             switch(m_inner_join)
410             {
411             default: // inner_bevel
412                 add_vertex(vc, v1.x + dx1, v1.y - dy1);
413                 add_vertex(vc, v1.x + dx2, v1.y - dy2);
414                 break;
415 
416             case inner_miter:
417                 calc_miter(vc,
418                            v0, v1, v2, dx1, dy1, dx2, dy2,
419                            miter_join_revert,
420                            limit, 0);
421                 break;
422 
423             case inner_jag:
424             case inner_round:
425                 cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2);
426                 if(cp < len1 * len1 && cp < len2 * len2)
427                 {
428                     calc_miter(vc,
429                                v0, v1, v2, dx1, dy1, dx2, dy2,
430                                miter_join_revert,
431                                limit, 0);
432                 }
433                 else
434                 {
435                     if(m_inner_join == inner_jag)
436                     {
437                         add_vertex(vc, v1.x + dx1, v1.y - dy1);
438                         add_vertex(vc, v1.x,       v1.y      );
439                         add_vertex(vc, v1.x + dx2, v1.y - dy2);
440                     }
441                     else
442                     {
443                         add_vertex(vc, v1.x + dx1, v1.y - dy1);
444                         add_vertex(vc, v1.x,       v1.y      );
445                         calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
446                         add_vertex(vc, v1.x,       v1.y      );
447                         add_vertex(vc, v1.x + dx2, v1.y - dy2);
448                     }
449                 }
450                 break;
451             }
452         }
453         else
454         {
455             // Outer join
456             //---------------
457 
458             // Calculate the distance between v1 and
459             // the central point of the bevel line segment
460             //---------------
461             double dx = (dx1 + dx2) / 2;
462             double dy = (dy1 + dy2) / 2;
463             double dbevel = sqrt(dx * dx + dy * dy);
464 
465             if(m_line_join == round_join || m_line_join == bevel_join)
466             {
467                 // This is an optimization that reduces the number of points
468                 // in cases of almost collinear segments. If there's no
469                 // visible difference between bevel and miter joins we'd rather
470                 // use miter join because it adds only one point instead of two.
471                 //
472                 // Here we calculate the middle point between the bevel points
473                 // and then, the distance between v1 and this middle point.
474                 // At outer joins this distance always less than stroke width,
475                 // because it's actually the height of an isosceles triangle of
476                 // v1 and its two bevel points. If the difference between this
477                 // width and this value is small (no visible bevel) we can
478                 // add just one point.
479                 //
480                 // The constant in the expression makes the result approximately
481                 // the same as in round joins and caps. You can safely comment
482                 // out this entire "if".
483                 //-------------------
484                 if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps)
485                 {
486                     if(calc_intersection(v0.x + dx1, v0.y - dy1,
487                                          v1.x + dx1, v1.y - dy1,
488                                          v1.x + dx2, v1.y - dy2,
489                                          v2.x + dx2, v2.y - dy2,
490                                          &dx, &dy))
491                     {
492                         add_vertex(vc, dx, dy);
493                     }
494                     else
495                     {
496                         add_vertex(vc, v1.x + dx1, v1.y - dy1);
497                     }
498                     return;
499                 }
500             }
501 
502             switch(m_line_join)
503             {
504             case miter_join:
505             case miter_join_revert:
506             case miter_join_round:
507                 calc_miter(vc,
508                            v0, v1, v2, dx1, dy1, dx2, dy2,
509                            m_line_join,
510                            m_miter_limit,
511                            dbevel);
512                 break;
513 
514             case round_join:
515                 calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
516                 break;
517 
518             default: // Bevel join
519                 add_vertex(vc, v1.x + dx1, v1.y - dy1);
520                 add_vertex(vc, v1.x + dx2, v1.y - dy2);
521                 break;
522             }
523         }
524     }
525 
526 
527 
528 
529 }
530 
531 #endif
532