xref: /haiku/headers/libs/agg/agg_math_stroke.h (revision 24159a0c7d6d6dcba9f2a0c1a7c08d2c8167f21b)
1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.2
3 // Copyright (C) 2002-2004 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,
40         miter_join_revert,
41         round_join,
42         bevel_join
43     };
44 
45     // Minimal angle to calculate round joins, less than 0.1 degree.
46     const double stroke_theta = 0.001; //----stroke_theta
47 
48 
49     //--------------------------------------------------------stroke_calc_arc
50     template<class VertexConsumer>
51     void stroke_calc_arc(VertexConsumer& out_vertices,
52                          double x,   double y,
53                          double dx1, double dy1,
54                          double dx2, double dy2,
55                          double width,
56                          double approximation_scale)
57     {
58         typedef typename VertexConsumer::value_type coord_type;
59 
60         // Check if we actually need the arc
61         //-----------------
62         double dd = calc_distance(dx1, dy1, dx2, dy2);
63         if(dd < approximation_scale)
64         {
65             out_vertices.add(coord_type(x + dx1, y + dy1));
66             if(dd > approximation_scale * 0.25)
67             {
68                 out_vertices.add(coord_type(x + dx2, y + dy2));
69             }
70             return;
71         }
72 
73         double a1 = atan2(dy1, dx1);
74         double a2 = atan2(dy2, dx2);
75         double da = a1 - a2;
76 
77         //if(fabs(da) < stroke_theta)
78         //{
79         //    out_vertices.add(coord_type(x + dx1, y + dy1));
80         //    //out_vertices.add(coord_type(x + dx2, y + dy2));
81         //    return;
82         //}
83 
84         bool ccw = da > 0.0 && da < pi;
85 
86         if(width < 0) width = -width;
87         da = fabs(1.0 / (width * approximation_scale));
88         if(!ccw)
89         {
90             if(a1 > a2) a2 += 2 * pi;
91             while(a1 < a2)
92             {
93                 out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width));
94                 a1 += da;
95             }
96         }
97         else
98         {
99             if(a1 < a2) a2 -= 2 * pi;
100             while(a1 > a2)
101             {
102                 out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width));
103                 a1 -= da;
104             }
105         }
106         out_vertices.add(coord_type(x + dx2, y + dy2));
107     }
108 
109 
110 
111     //-------------------------------------------------------stroke_calc_miter
112     template<class VertexConsumer>
113     void stroke_calc_miter(VertexConsumer& out_vertices,
114                            const vertex_dist& v0,
115                            const vertex_dist& v1,
116                            const vertex_dist& v2,
117                            double dx1, double dy1,
118                            double dx2, double dy2,
119                            double width,
120                            bool revert_flag,
121                            double miter_limit)
122     {
123         typedef typename VertexConsumer::value_type coord_type;
124 
125         double xi = v1.x;
126         double yi = v1.y;
127 
128         if(!calc_intersection(v0.x + dx1, v0.y - dy1,
129                               v1.x + dx1, v1.y - dy1,
130                               v1.x + dx2, v1.y - dy2,
131                               v2.x + dx2, v2.y - dy2,
132                               &xi, &yi))
133         {
134             // The calculation didn't succeed, most probaly
135             // the three points lie one straight line
136             //----------------
137             if(calc_distance(dx1, -dy1, dx2, -dy2) < width * 0.025)
138             {
139                 // This case means that the next segment continues
140                 // the previous one (straight line)
141                 //-----------------
142                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
143             }
144             else
145             {
146                 // This case means that the next segment goes back
147                 //-----------------
148                 if(revert_flag)
149                 {
150                     out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
151                     out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
152                 }
153                 else
154                 {
155                     // If no miter-revert, calcuate new dx1, dy1, dx2, dy2
156                     out_vertices.add(coord_type(v1.x + dx1 + dy1 * miter_limit,
157                                                 v1.y - dy1 + dx1 * miter_limit));
158                     out_vertices.add(coord_type(v1.x + dx2 - dy2 * miter_limit,
159                                                 v1.y - dy2 - dx2 * miter_limit));
160                 }
161             }
162         }
163         else
164         {
165             double d1 = calc_distance(v1.x, v1.y, xi, yi);
166             double lim = width * miter_limit;
167             if(d1 > lim)
168             {
169                 // Miter limit exceeded
170                 //------------------------
171                 if(revert_flag)
172                 {
173                     // For the compatibility with SVG, PDF, etc,
174                     // we use a simple bevel join instead of
175                     // "smart" bevel
176                     //-------------------
177                     out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
178                     out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
179                 }
180                 else
181                 {
182                     // Smart bevel that cuts the miter at the limit point
183                     //-------------------
184                     d1  = lim / d1;
185                     double x1 = v1.x + dx1;
186                     double y1 = v1.y - dy1;
187                     double x2 = v1.x + dx2;
188                     double y2 = v1.y - dy2;
189 
190                     x1 += (xi - x1) * d1;
191                     y1 += (yi - y1) * d1;
192                     x2 += (xi - x2) * d1;
193                     y2 += (yi - y2) * d1;
194                     out_vertices.add(coord_type(x1, y1));
195                     out_vertices.add(coord_type(x2, y2));
196                 }
197             }
198             else
199             {
200                 // Inside the miter limit
201                 //---------------------
202                 out_vertices.add(coord_type(xi, yi));
203             }
204         }
205     }
206 
207 
208 
209 
210 
211 
212     //--------------------------------------------------------stroke_calc_cap
213     template<class VertexConsumer>
214     void stroke_calc_cap(VertexConsumer& out_vertices,
215                          const vertex_dist& v0,
216                          const vertex_dist& v1,
217                          double len,
218                          line_cap_e line_cap,
219                          double width,
220                          double approximation_scale)
221     {
222         typedef typename VertexConsumer::value_type coord_type;
223 
224         out_vertices.remove_all();
225 
226         double dx1 = width * (v1.y - v0.y) / len;
227         double dy1 = width * (v1.x - v0.x) / len;
228         double dx2 = 0;
229         double dy2 = 0;
230 
231         if(line_cap == square_cap)
232         {
233             dx2 = dy1;
234             dy2 = dx1;
235         }
236 
237         if(line_cap == round_cap)
238         {
239             double a1 = atan2(dy1, -dx1);
240             double a2 = a1 + pi;
241             double da = fabs(1.0 / (width * approximation_scale));
242             while(a1 < a2)
243             {
244                 out_vertices.add(coord_type(v0.x + cos(a1) * width,
245                                             v0.y + sin(a1) * width));
246                 a1 += da;
247             }
248             out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1));
249         }
250         else
251         {
252             out_vertices.add(coord_type(v0.x - dx1 - dx2, v0.y + dy1 - dy2));
253             out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2));
254         }
255     }
256 
257 
258 
259     //-------------------------------------------------------stroke_calc_join
260     template<class VertexConsumer>
261     void stroke_calc_join(VertexConsumer& out_vertices,
262                           const vertex_dist& v0,
263                           const vertex_dist& v1,
264                           const vertex_dist& v2,
265                           double len1,
266                           double len2,
267                           double width,
268                           line_join_e line_join,
269                           line_join_e inner_line_join,
270                           double miter_limit,
271                           double inner_miter_limit,
272                           double approximation_scale)
273     {
274         typedef typename VertexConsumer::value_type coord_type;
275 
276         double dx1, dy1, dx2, dy2;
277 
278         dx1 = width * (v1.y - v0.y) / len1;
279         dy1 = width * (v1.x - v0.x) / len1;
280 
281         dx2 = width * (v2.y - v1.y) / len2;
282         dy2 = width * (v2.x - v1.x) / len2;
283 
284         out_vertices.remove_all();
285 
286         if(calc_point_location(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y) > 0.0)
287         {
288             // Inner join
289             //---------------
290             stroke_calc_miter(out_vertices,
291                               v0, v1, v2, dx1, dy1, dx2, dy2,
292                               width,
293                               inner_line_join == miter_join_revert,
294                               inner_miter_limit);
295         }
296         else
297         {
298             // Outer join
299             //---------------
300             switch(line_join)
301             {
302             case miter_join:
303                 stroke_calc_miter(out_vertices,
304                                   v0, v1, v2, dx1, dy1, dx2, dy2,
305                                   width,
306                                   false,
307                                   miter_limit);
308                 break;
309 
310             case miter_join_revert:
311                 stroke_calc_miter(out_vertices,
312                                   v0, v1, v2, dx1, dy1, dx2, dy2,
313                                   width,
314                                   true,
315                                   miter_limit);
316                 break;
317 
318             case round_join:
319                 stroke_calc_arc(out_vertices,
320                                 v1.x, v1.y, dx1, -dy1, dx2, -dy2,
321                                 width, approximation_scale);
322                 break;
323 
324             default: // Bevel join
325                 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1));
326                 if(calc_distance(dx1, dy1, dx2, dy2) > approximation_scale * 0.25)
327                 {
328                     out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2));
329                 }
330                 break;
331             }
332         }
333     }
334 
335 
336 
337 
338 }
339 
340 #endif
341