Anti-Grain Geometry Tutorial
agg_math_stroke.h
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& vc,
84  const vertex_dist& v0,
85  const vertex_dist& v1,
86  double len);
87 
88  void calc_join(VertexConsumer& vc,
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  AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y)
97  {
98  vc.add(coord_type(x, y));
99  }
100 
101  void calc_arc(VertexConsumer& vc,
102  double x, double y,
103  double dx1, double dy1,
104  double dx2, double dy2);
105 
106  void calc_miter(VertexConsumer& vc,
107  const vertex_dist& v0,
108  const vertex_dist& v1,
109  const vertex_dist& v2,
110  double dx1, double dy1,
111  double dx2, double dy2,
112  line_join_e lj,
113  double mlimit,
114  double dbevel);
115 
116  double m_width;
117  double m_width_abs;
118  double m_width_eps;
119  int m_width_sign;
120  double m_miter_limit;
121  double m_inner_miter_limit;
122  double m_approx_scale;
123  line_cap_e m_line_cap;
124  line_join_e m_line_join;
125  inner_join_e m_inner_join;
126  };
127 
128  //-----------------------------------------------------------------------
129  template<class VC> math_stroke<VC>::math_stroke() :
130  m_width(0.5),
131  m_width_abs(0.5),
132  m_width_eps(0.5/1024.0),
133  m_width_sign(1),
134  m_miter_limit(4.0),
135  m_inner_miter_limit(1.01),
136  m_approx_scale(1.0),
137  m_line_cap(butt_cap),
138  m_line_join(miter_join),
139  m_inner_join(inner_miter)
140  {
141  }
142 
143  //-----------------------------------------------------------------------
144  template<class VC> void math_stroke<VC>::width(double w)
145  {
146  m_width = w * 0.5;
147  if(m_width < 0)
148  {
149  m_width_abs = -m_width;
150  m_width_sign = -1;
151  }
152  else
153  {
154  m_width_abs = m_width;
155  m_width_sign = 1;
156  }
157  m_width_eps = m_width / 1024.0;
158  }
159 
160  //-----------------------------------------------------------------------
161  template<class VC> void math_stroke<VC>::miter_limit_theta(double t)
162  {
163  m_miter_limit = 1.0 / std::sin(t * 0.5) ;
164  }
165 
166  //-----------------------------------------------------------------------
167  template<class VC>
168  void math_stroke<VC>::calc_arc(VC& vc,
169  double x, double y,
170  double dx1, double dy1,
171  double dx2, double dy2)
172  {
173  double a1 = std::atan2(dy1 * m_width_sign, dx1 * m_width_sign);
174  double a2 = std::atan2(dy2 * m_width_sign, dx2 * m_width_sign);
175  double da = a1 - a2;
176  int i, n;
177 
178  da = std::acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
179 
180  add_vertex(vc, x + dx1, y + dy1);
181  if(m_width_sign > 0)
182  {
183  if(a1 > a2) a2 += 2 * pi;
184  n = int((a2 - a1) / da);
185  da = (a2 - a1) / (n + 1);
186  a1 += da;
187  for(i = 0; i < n; i++)
188  {
189  add_vertex(vc, x + std::cos(a1) * m_width, y + std::sin(a1) * m_width);
190  a1 += da;
191  }
192  }
193  else
194  {
195  if(a1 < a2) a2 -= 2 * pi;
196  n = int((a1 - a2) / da);
197  da = (a1 - a2) / (n + 1);
198  a1 -= da;
199  for(i = 0; i < n; i++)
200  {
201  add_vertex(vc, x + std::cos(a1) * m_width, y + std::sin(a1) * m_width);
202  a1 -= da;
203  }
204  }
205  add_vertex(vc, x + dx2, y + dy2);
206  }
207 
208  //-----------------------------------------------------------------------
209  template<class VC>
210  void math_stroke<VC>::calc_miter(VC& vc,
211  const vertex_dist& v0,
212  const vertex_dist& v1,
213  const vertex_dist& v2,
214  double dx1, double dy1,
215  double dx2, double dy2,
216  line_join_e lj,
217  double mlimit,
218  double dbevel)
219  {
220  double xi = v1.x;
221  double yi = v1.y;
222  double di = 1;
223  double lim = m_width_abs * mlimit;
224  bool miter_limit_exceeded = true; // Assume the worst
225  bool intersection_failed = true; // Assume the worst
226 
227  if(calc_intersection(v0.x + dx1, v0.y - dy1,
228  v1.x + dx1, v1.y - dy1,
229  v1.x + dx2, v1.y - dy2,
230  v2.x + dx2, v2.y - dy2,
231  &xi, &yi))
232  {
233  // Calculation of the intersection succeeded
234  //---------------------
235  di = calc_distance(v1.x, v1.y, xi, yi);
236  if(di <= lim)
237  {
238  // Inside the miter limit
239  //---------------------
240  add_vertex(vc, xi, yi);
241  miter_limit_exceeded = false;
242  }
243  intersection_failed = false;
244  }
245  else
246  {
247  // Calculation of the intersection failed, most probably
248  // the three points lie one straight line.
249  // First check if v0 and v2 lie on the opposite sides of vector:
250  // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
251  // to the line determined by vertices v0 and v1.
252  // This condition determines whether the next line segments continues
253  // the previous one or goes back.
254  //----------------
255  double x2 = v1.x + dx1;
256  double y2 = v1.y - dy1;
257  if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) ==
258  (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0))
259  {
260  // This case means that the next segment continues
261  // the previous one (straight line)
262  //-----------------
263  add_vertex(vc, v1.x + dx1, v1.y - dy1);
264  miter_limit_exceeded = false;
265  }
266  }
267 
268  if(miter_limit_exceeded)
269  {
270  // Miter limit exceeded
271  //------------------------
272  switch(lj)
273  {
274  case miter_join_revert:
275  // For the compatibility with SVG, PDF, etc,
276  // we use a simple bevel join instead of
277  // "smart" bevel
278  //-------------------
279  add_vertex(vc, v1.x + dx1, v1.y - dy1);
280  add_vertex(vc, v1.x + dx2, v1.y - dy2);
281  break;
282 
283  case miter_join_round:
284  calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
285  break;
286 
287  default:
288  // If no miter-revert, calculate new dx1, dy1, dx2, dy2
289  //----------------
290  if(intersection_failed)
291  {
292  mlimit *= m_width_sign;
293  add_vertex(vc, v1.x + dx1 + dy1 * mlimit,
294  v1.y - dy1 + dx1 * mlimit);
295  add_vertex(vc, v1.x + dx2 - dy2 * mlimit,
296  v1.y - dy2 - dx2 * mlimit);
297  }
298  else
299  {
300  double x1 = v1.x + dx1;
301  double y1 = v1.y - dy1;
302  double x2 = v1.x + dx2;
303  double y2 = v1.y - dy2;
304  di = (lim - dbevel) / (di - dbevel);
305  add_vertex(vc, x1 + (xi - x1) * di,
306  y1 + (yi - y1) * di);
307  add_vertex(vc, x2 + (xi - x2) * di,
308  y2 + (yi - y2) * di);
309  }
310  break;
311  }
312  }
313  }
314 
315  //--------------------------------------------------------stroke_calc_cap
316  template<class VC>
317  void math_stroke<VC>::calc_cap(VC& vc,
318  const vertex_dist& v0,
319  const vertex_dist& v1,
320  double len)
321  {
322  vc.remove_all();
323 
324  double dx1 = (v1.y - v0.y) / len;
325  double dy1 = (v1.x - v0.x) / len;
326  double dx2 = 0;
327  double dy2 = 0;
328 
329  dx1 *= m_width;
330  dy1 *= m_width;
331 
332  if(m_line_cap != round_cap)
333  {
334  if(m_line_cap == square_cap)
335  {
336  dx2 = dy1 * m_width_sign;
337  dy2 = dx1 * m_width_sign;
338  }
339  add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2);
340  add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2);
341  }
342  else
343  {
344  double da = std::acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
345  double a1;
346  int i;
347  int n = int(pi / da);
348 
349  da = pi / (n + 1);
350  add_vertex(vc, v0.x - dx1, v0.y + dy1);
351  if(m_width_sign > 0)
352  {
353  a1 = std::atan2(dy1, -dx1);
354  a1 += da;
355  for(i = 0; i < n; i++)
356  {
357  add_vertex(vc, v0.x + std::cos(a1) * m_width,
358  v0.y + std::sin(a1) * m_width);
359  a1 += da;
360  }
361  }
362  else
363  {
364  a1 = std::atan2(-dy1, dx1);
365  a1 -= da;
366  for(i = 0; i < n; i++)
367  {
368  add_vertex(vc, v0.x + std::cos(a1) * m_width,
369  v0.y + std::sin(a1) * m_width);
370  a1 -= da;
371  }
372  }
373  add_vertex(vc, v0.x + dx1, v0.y - dy1);
374  }
375  }
376 
377  //-----------------------------------------------------------------------
378  template<class VC>
379  void math_stroke<VC>::calc_join(VC& vc,
380  const vertex_dist& v0,
381  const vertex_dist& v1,
382  const vertex_dist& v2,
383  double len1,
384  double len2)
385  {
386  double dx1 = m_width * (v1.y - v0.y) / len1;
387  double dy1 = m_width * (v1.x - v0.x) / len1;
388  double dx2 = m_width * (v2.y - v1.y) / len2;
389  double dy2 = m_width * (v2.x - v1.x) / len2;
390 
391  vc.remove_all();
392 
393  double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
394  if(cp != 0 && (cp > 0) == (m_width > 0))
395  {
396  // Inner join
397  //---------------
398  double limit = ((len1 < len2) ? len1 : len2) / m_width_abs;
399  if(limit < m_inner_miter_limit)
400  {
401  limit = m_inner_miter_limit;
402  }
403 
404  switch(m_inner_join)
405  {
406  default: // inner_bevel
407  add_vertex(vc, v1.x + dx1, v1.y - dy1);
408  add_vertex(vc, v1.x + dx2, v1.y - dy2);
409  break;
410 
411  case inner_miter:
412  calc_miter(vc,
413  v0, v1, v2, dx1, dy1, dx2, dy2,
414  miter_join_revert,
415  limit, 0);
416  break;
417 
418  case inner_jag:
419  case inner_round:
420  cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2);
421  if(cp < len1 * len1 && cp < len2 * len2)
422  {
423  calc_miter(vc,
424  v0, v1, v2, dx1, dy1, dx2, dy2,
425  miter_join_revert,
426  limit, 0);
427  }
428  else
429  {
430  if(m_inner_join == inner_jag)
431  {
432  add_vertex(vc, v1.x + dx1, v1.y - dy1);
433  add_vertex(vc, v1.x, v1.y );
434  add_vertex(vc, v1.x + dx2, v1.y - dy2);
435  }
436  else
437  {
438  add_vertex(vc, v1.x + dx1, v1.y - dy1);
439  add_vertex(vc, v1.x, v1.y );
440  calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
441  add_vertex(vc, v1.x, v1.y );
442  add_vertex(vc, v1.x + dx2, v1.y - dy2);
443  }
444  }
445  break;
446  }
447  }
448  else
449  {
450  // Outer join
451  //---------------
452 
453  // Calculate the distance between v1 and
454  // the central point of the bevel line segment
455  //---------------
456  double dx = (dx1 + dx2) / 2;
457  double dy = (dy1 + dy2) / 2;
458  double dbevel = std::sqrt(dx * dx + dy * dy);
459 
460  if(m_line_join == round_join || m_line_join == bevel_join)
461  {
462  // This is an optimization that reduces the number of points
463  // in cases of almost collinear segments. If there's no
464  // visible difference between bevel and miter joins we'd rather
465  // use miter join because it adds only one point instead of two.
466  //
467  // Here we calculate the middle point between the bevel points
468  // and then, the distance between v1 and this middle point.
469  // At outer joins this distance always less than stroke width,
470  // because it's actually the height of an isosceles triangle of
471  // v1 and its two bevel points. If the difference between this
472  // width and this value is small (no visible bevel) we can
473  // add just one point.
474  //
475  // The constant in the expression makes the result approximately
476  // the same as in round joins and caps. You can safely comment
477  // out this entire "if".
478  //-------------------
479  if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps)
480  {
481  if(calc_intersection(v0.x + dx1, v0.y - dy1,
482  v1.x + dx1, v1.y - dy1,
483  v1.x + dx2, v1.y - dy2,
484  v2.x + dx2, v2.y - dy2,
485  &dx, &dy))
486  {
487  add_vertex(vc, dx, dy);
488  }
489  else
490  {
491  add_vertex(vc, v1.x + dx1, v1.y - dy1);
492  }
493  return;
494  }
495  }
496 
497  switch(m_line_join)
498  {
499  case miter_join:
500  case miter_join_revert:
501  case miter_join_round:
502  calc_miter(vc,
503  v0, v1, v2, dx1, dy1, dx2, dy2,
504  m_line_join,
505  m_miter_limit,
506  dbevel);
507  break;
508 
509  case round_join:
510  calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
511  break;
512 
513  default: // Bevel join
514  add_vertex(vc, v1.x + dx1, v1.y - dy1);
515  add_vertex(vc, v1.x + dx2, v1.y - dy2);
516  break;
517  }
518  }
519  }
520 
521 
522 
523 
524 }
525 
526 #endif
Definition: agg_arc.cpp:24