1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.4
3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
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.
10 //----------------------------------------------------------------------------
11 // Contact: mcseem@antigrain.com
12 // mcseemagg@yahoo.com
13 // http://www.antigrain.com
14 //----------------------------------------------------------------------------
18 //----------------------------------------------------------------------------
20 #ifndef AGG_STROKE_MATH_INCLUDED
21 #define AGG_STROKE_MATH_INCLUDED
24 #include "agg_vertex_sequence.h"
28 //-------------------------------------------------------------line_cap_e
36 //------------------------------------------------------------line_join_e
40 miter_join_revert
= 1,
47 //-----------------------------------------------------------inner_join_e
56 //------------------------------------------------------------math_stroke
57 template<class VertexConsumer
> class math_stroke
60 typedef typename
VertexConsumer::value_type coord_type
;
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
; }
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
; }
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
; }
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
; }
83 void calc_cap(VertexConsumer
& out_vertices
,
84 const vertex_dist
& v0
,
85 const vertex_dist
& v1
,
88 void calc_join(VertexConsumer
& out_vertices
,
89 const vertex_dist
& v0
,
90 const vertex_dist
& v1
,
91 const vertex_dist
& v2
,
96 void calc_arc(VertexConsumer
& out_vertices
,
98 double dx1
, double dy1
,
99 double dx2
, double dy2
);
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
,
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
;
121 //-----------------------------------------------------------------------
122 template<class VC
> math_stroke
<VC
>::math_stroke() :
127 m_inner_miter_limit(1.01),
129 m_line_cap(butt_cap
),
130 m_line_join(miter_join
),
131 m_inner_join(inner_miter
)
135 //-----------------------------------------------------------------------
136 template<class VC
> void math_stroke
<VC
>::width(double w
)
141 m_width_abs
= -m_width
;
146 m_width_abs
= m_width
;
151 //-----------------------------------------------------------------------
152 template<class VC
> void math_stroke
<VC
>::miter_limit_theta(double t
)
154 m_miter_limit
= 1.0 / sin(t
* 0.5) ;
157 //-----------------------------------------------------------------------
159 void math_stroke
<VC
>::calc_arc(VC
& out_vertices
,
161 double dx1
, double dy1
,
162 double dx2
, double dy2
)
164 double a1
= atan2(dy1
* m_width_sign
, dx1
* m_width_sign
);
165 double a2
= atan2(dy2
* m_width_sign
, dx2
* m_width_sign
);
169 da
= acos(m_width_abs
/ (m_width_abs
+ 0.125 / m_approx_scale
)) * 2;
171 out_vertices
.add(coord_type(x
+ dx1
, y
+ dy1
));
174 if(a1
> a2
) a2
+= 2 * pi
;
175 n
= int((a2
- a1
) / da
);
176 da
= (a2
- a1
) / (n
+ 1);
178 for(i
= 0; i
< n
; i
++)
180 out_vertices
.add(coord_type(x
+ cos(a1
) * m_width
,
181 y
+ sin(a1
) * m_width
));
187 if(a1
< a2
) a2
-= 2 * pi
;
188 n
= int((a1
- a2
) / da
);
189 da
= (a1
- a2
) / (n
+ 1);
191 for(i
= 0; i
< n
; i
++)
193 out_vertices
.add(coord_type(x
+ cos(a1
) * m_width
,
194 y
+ sin(a1
) * m_width
));
198 out_vertices
.add(coord_type(x
+ dx2
, y
+ dy2
));
201 //-----------------------------------------------------------------------
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
,
214 bool miter_limit_exceeded
= true; // Assume the worst
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
,
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
;
228 // Inside the miter limit
229 //---------------------
230 out_vertices
.add(coord_type(xi
, yi
));
231 miter_limit_exceeded
= false;
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.
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))
249 // This case means that the next segment continues
250 // the previous one (straight line)
252 out_vertices
.add(coord_type(v1
.x
+ dx1
, v1
.y
- dy1
));
253 miter_limit_exceeded
= false;
257 if(miter_limit_exceeded
)
259 // Miter limit exceeded
260 //------------------------
263 case miter_join_revert
:
264 // For the compatibility with SVG, PDF, etc,
265 // we use a simple bevel join instead of
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
));
272 case miter_join_round
:
273 calc_arc(out_vertices
, v1
.x
, v1
.y
, dx1
, -dy1
, dx2
, -dy2
);
277 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
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
));
289 //--------------------------------------------------------stroke_calc_cap
291 void math_stroke
<VC
>::calc_cap(VC
& out_vertices
,
292 const vertex_dist
& v0
,
293 const vertex_dist
& v1
,
296 out_vertices
.remove_all();
298 double dx1
= (v1
.y
- v0
.y
) / len
;
299 double dy1
= (v1
.x
- v0
.x
) / len
;
306 if(m_line_cap
!= round_cap
)
308 if(m_line_cap
== square_cap
)
310 dx2
= dy1
* m_width_sign
;
311 dy2
= dx1
* m_width_sign
;
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
));
318 double da
= acos(m_width_abs
/ (m_width_abs
+ 0.125 / m_approx_scale
)) * 2;
321 int n
= int(pi
/ da
);
324 out_vertices
.add(coord_type(v0
.x
- dx1
, v0
.y
+ dy1
));
327 a1
= atan2(dy1
, -dx1
);
329 for(i
= 0; i
< n
; i
++)
331 out_vertices
.add(coord_type(v0
.x
+ cos(a1
) * m_width
,
332 v0
.y
+ sin(a1
) * m_width
));
338 a1
= atan2(-dy1
, dx1
);
340 for(i
= 0; i
< n
; i
++)
342 out_vertices
.add(coord_type(v0
.x
+ cos(a1
) * m_width
,
343 v0
.y
+ sin(a1
) * m_width
));
347 out_vertices
.add(coord_type(v0
.x
+ dx1
, v0
.y
- dy1
));
351 //-----------------------------------------------------------------------
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
,
360 double dx1
, dy1
, dx2
, dy2
;
363 dx1
= m_width
* (v1
.y
- v0
.y
) / len1
;
364 dy1
= m_width
* (v1
.x
- v0
.x
) / len1
;
366 dx2
= m_width
* (v2
.y
- v1
.y
) / len2
;
367 dy2
= m_width
* (v2
.x
- v1
.x
) / len2
;
369 out_vertices
.remove_all();
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))
376 double limit
= ((len1
< len2
) ? len1
: len2
) / m_width_abs
;
377 if(limit
< m_inner_miter_limit
)
379 limit
= m_inner_miter_limit
;
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
));
390 calc_miter(out_vertices
,
391 v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
,
399 d
= (dx1
-dx2
) * (dx1
-dx2
) + (dy1
-dy2
) * (dy1
-dy2
);
400 if(d
< len1
* len1
&& d
< len2
* len2
)
402 calc_miter(out_vertices
,
403 v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
,
409 if(m_inner_join
== inner_jag
)
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
));
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
));
432 line_join_e lj
= m_line_join
;
433 if(m_line_join
== round_join
|| m_line_join
== bevel_join
)
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.
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.
448 // The constant in the expression makes the result approximately
449 // the same as in round joins and caps. One can safely comment
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
)
464 case miter_join_revert
:
465 case miter_join_round
:
466 calc_miter(out_vertices
,
467 v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
,
473 calc_arc(out_vertices
, v1
.x
, v1
.y
, dx1
, -dy1
, dx2
, -dy2
);
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
));