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