xref: /haiku/headers/libs/agg/agg_math_stroke.h (revision ed6250c95736c0b55da79d6e9dd01369532260c0)
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 
26 namespace 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 = a1 - a2;
167         int i, n;
168 
169         da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
170 
171         out_vertices.add(coord_type(x + dx1, y + dy1));
172         if(m_width_sign > 0)
173         {
174             if(a1 > a2) a2 += 2 * pi;
175             n = int((a2 - a1) / da);
176             da = (a2 - a1) / (n + 1);
177             a1 += da;
178             for(i = 0; i < n; i++)
179             {
180                 out_vertices.add(coord_type(x + cos(a1) * m_width,
181                                             y + sin(a1) * m_width));
182                 a1 += da;
183             }
184         }
185         else
186         {
187             if(a1 < a2) a2 -= 2 * pi;
188             n = int((a1 - a2) / da);
189             da = (a1 - a2) / (n + 1);
190             a1 -= da;
191             for(i = 0; i < n; i++)
192             {
193                 out_vertices.add(coord_type(x + cos(a1) * m_width,
194                                             y + sin(a1) * m_width));
195                 a1 -= da;
196             }
197         }
198         out_vertices.add(coord_type(x + dx2, y + dy2));
199     }
200 
201     //-----------------------------------------------------------------------
202     template<class VC>
203     void math_stroke<VC>::calc_miter(VC& out_vertices,
204                                      const vertex_dist& v0,
205                                      const vertex_dist& v1,
206                                      const vertex_dist& v2,
207                                      double dx1, double dy1,
208                                      double dx2, double dy2,
209                                      line_join_e lj,
210                                      double ml)
211     {
212         double xi = v1.x;
213         double yi = v1.y;
214         bool miter_limit_exceeded = true; // Assume the worst
215 
216         if(calc_intersection(v0.x + dx1, v0.y - dy1,
217                              v1.x + dx1, v1.y - dy1,
218                              v1.x + dx2, v1.y - dy2,
219                              v2.x + dx2, v2.y - dy2,
220                              &xi, &yi))
221         {
222             // Calculation of the intersection succeeded
223             //---------------------
224             double d1 = calc_distance(v1.x, v1.y, xi, yi);
225             double lim = m_width_abs * ml;
226             if(d1 <= lim)
227             {
228                 // Inside the miter limit
229                 //---------------------
230                 out_vertices.add(coord_type(xi, yi));
231                 miter_limit_exceeded = false;
232             }
233         }
234         else
235         {
236             // Calculation of the intersection failed, most probably
237             // the three points lie one straight line.
238             // First check if v0 and v2 lie on the opposite sides of vector:
239             // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
240             // to the line determined by vertices v0 and v1.
241             // This condition determines whether the next line segments continues
242             // the previous one or goes back.
243             //----------------
244             double x2 = v1.x + dx1;
245             double y2 = v1.y - dy1;
246             if(((x2 - v0.x)*dy1 - (v0.y - y2)*dx1 < 0.0) !=
247                ((x2 - v2.x)*dy1 - (v2.y - y2)*dx1 < 0.0))
248             {
249                 // This case means that the next segment continues
250                 // the previous one (straight line)
251                 //-----------------
252                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
253                 miter_limit_exceeded = false;
254             }
255         }
256 
257         if(miter_limit_exceeded)
258         {
259             // Miter limit exceeded
260             //------------------------
261             switch(lj)
262             {
263             case miter_join_revert:
264                 // For the compatibility with SVG, PDF, etc,
265                 // we use a simple bevel join instead of
266                 // "smart" bevel
267                 //-------------------
268                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
269                 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
270                 break;
271 
272             case miter_join_round:
273                 calc_arc(out_vertices, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
274                 break;
275 
276             default:
277                 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
278                 //----------------
279                 ml *= m_width_sign;
280                 out_vertices.add(coord_type(v1.x + dx1 + dy1 * ml,
281                                             v1.y - dy1 + dx1 * ml));
282                 out_vertices.add(coord_type(v1.x + dx2 - dy2 * ml,
283                                             v1.y - dy2 - dx2 * ml));
284                 break;
285             }
286         }
287     }
288 
289     //--------------------------------------------------------stroke_calc_cap
290     template<class VC>
291     void math_stroke<VC>::calc_cap(VC& out_vertices,
292                                    const vertex_dist& v0,
293                                    const vertex_dist& v1,
294                                    double len)
295     {
296         out_vertices.remove_all();
297 
298         double dx1 = (v1.y - v0.y) / len;
299         double dy1 = (v1.x - v0.x) / len;
300         double dx2 = 0;
301         double dy2 = 0;
302 
303         dx1 *= m_width;
304         dy1 *= m_width;
305 
306         if(m_line_cap != round_cap)
307         {
308             if(m_line_cap == square_cap)
309             {
310                 dx2 = dy1 * m_width_sign;
311                 dy2 = dx1 * m_width_sign;
312             }
313             out_vertices.add(coord_type(v0.x - dx1 - dx2, v0.y + dy1 - dy2));
314             out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2));
315         }
316         else
317         {
318             double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
319             double a1;
320             int i;
321             int n = int(pi / da);
322 
323             da = pi / (n + 1);
324             out_vertices.add(coord_type(v0.x - dx1, v0.y + dy1));
325             if(m_width_sign > 0)
326             {
327                 a1 = atan2(dy1, -dx1);
328                 a1 += da;
329                 for(i = 0; i < n; i++)
330                 {
331                     out_vertices.add(coord_type(v0.x + cos(a1) * m_width,
332                                                 v0.y + sin(a1) * m_width));
333                     a1 += da;
334                 }
335             }
336             else
337             {
338                 a1 = atan2(-dy1, dx1);
339                 a1 -= da;
340                 for(i = 0; i < n; i++)
341                 {
342                     out_vertices.add(coord_type(v0.x + cos(a1) * m_width,
343                                                 v0.y + sin(a1) * m_width));
344                     a1 -= da;
345                 }
346             }
347             out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
348         }
349     }
350 
351     //-----------------------------------------------------------------------
352     template<class VC>
353     void math_stroke<VC>::calc_join(VC& out_vertices,
354                                     const vertex_dist& v0,
355                                     const vertex_dist& v1,
356                                     const vertex_dist& v2,
357                                     double len1,
358                                     double len2)
359     {
360         double dx1, dy1, dx2, dy2;
361         double d;
362 
363         dx1 = m_width * (v1.y - v0.y) / len1;
364         dy1 = m_width * (v1.x - v0.x) / len1;
365 
366         dx2 = m_width * (v2.y - v1.y) / len2;
367         dy2 = m_width * (v2.x - v1.x) / len2;
368 
369         out_vertices.remove_all();
370 
371         double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
372         if(cp != 0 && (cp > 0) == (m_width > 0))
373         {
374             // Inner join
375             //---------------
376             double limit = ((len1 < len2) ? len1 : len2) / m_width_abs;
377             if(limit < m_inner_miter_limit)
378             {
379                 limit = m_inner_miter_limit;
380             }
381 
382             switch(m_inner_join)
383             {
384             default: // inner_bevel
385                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
386                 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
387                 break;
388 
389             case inner_miter:
390                 calc_miter(out_vertices,
391                            v0, v1, v2, dx1, dy1, dx2, dy2,
392                            miter_join_revert,
393                            limit);
394                 break;
395 
396             case inner_jag:
397             case inner_round:
398                 {
399                     d = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2);
400                     if(d < len1 * len1 && d < len2 * len2)
401                     {
402                         calc_miter(out_vertices,
403                                    v0, v1, v2, dx1, dy1, dx2, dy2,
404                                    miter_join_revert,
405                                    limit);
406                     }
407                     else
408                     {
409                         if(m_inner_join == inner_jag)
410                         {
411                             out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
412                             out_vertices.add(coord_type(v1.x,       v1.y      ));
413                             out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
414                         }
415                         else
416                         {
417                             out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
418                             out_vertices.add(coord_type(v1.x,       v1.y      ));
419                             calc_arc(out_vertices, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
420                             out_vertices.add(coord_type(v1.x,       v1.y      ));
421                             out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
422                         }
423                     }
424                 }
425                 break;
426             }
427         }
428         else
429         {
430             // Outer join
431             //---------------
432             line_join_e lj = m_line_join;
433             if(m_line_join == round_join || m_line_join == bevel_join)
434             {
435                 // This is an optimization that reduces the number of points
436                 // in cases of almost collonear segments. If there's no
437                 // visible difference between bevel and miter joins we'd rather
438                 // use miter join because it adds only one point instead of two.
439                 //
440                 // Here we calculate the middle point between the bevel points
441                 // and then, the distance between v1 and this middle point.
442                 // At outer joins this distance always less than stroke width,
443                 // because it's actually the height of an isosceles triangle of
444                 // v1 and its two bevel points. If the difference between this
445                 // width and this value is small (no visible bevel) we can switch
446                 // to the miter join.
447                 //
448                 // The constant in the expression makes the result approximately
449                 // the same as in round joins and caps. One can safely comment
450                 // out this "if".
451                 //-------------------
452                 double dx = (dx1 + dx2) / 2;
453                 double dy = (dy1 + dy2) / 2;
454                 d = m_width_abs - sqrt(dx * dx + dy * dy);
455                 if(d < 0.0625 / m_approx_scale)
456                 {
457                     lj = miter_join;
458                 }
459             }
460 
461             switch(lj)
462             {
463             case miter_join:
464             case miter_join_revert:
465             case miter_join_round:
466                 calc_miter(out_vertices,
467                            v0, v1, v2, dx1, dy1, dx2, dy2,
468                            lj,
469                            m_miter_limit);
470                 break;
471 
472             case round_join:
473                 calc_arc(out_vertices, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
474                 break;
475 
476             default: // Bevel join
477                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
478                 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
479                 break;
480             }
481         }
482     }
483 
484 
485 
486 
487 }
488 
489 #endif
490