1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.3
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 //----------------------------------------------------------------------------
16 // Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e.,
17 // 4, 7, 10, or 13 vertices.
19 //----------------------------------------------------------------------------
23 #include "agg_bezier_arc.h"
29 //------------------------------------------------------------arc_to_bezier
30 void arc_to_bezier(double cx
, double cy
, double rx
, double ry
,
31 double start_angle
, double sweep_angle
,
34 double x0
= cos(sweep_angle
/ 2.0);
35 double y0
= sin(sweep_angle
/ 2.0);
36 double tx
= (1.0 - x0
) * 4.0 / 3.0;
37 double ty
= y0
- tx
* x0
/ y0
;
49 double sn
= sin(start_angle
+ sweep_angle
/ 2.0);
50 double cs
= cos(start_angle
+ sweep_angle
/ 2.0);
53 for(i
= 0; i
< 4; i
++)
55 curve
[i
* 2] = cx
+ rx
* (px
[i
] * cs
- py
[i
] * sn
);
56 curve
[i
* 2 + 1] = cy
+ ry
* (px
[i
] * sn
+ py
[i
] * cs
);
62 //------------------------------------------------------------------------
63 void bezier_arc::init(double x
, double y
,
68 start_angle
= fmod(start_angle
, 2.0 * pi
);
69 if(sweep_angle
>= 2.0 * pi
) sweep_angle
= 2.0 * pi
;
70 if(sweep_angle
<= -2.0 * pi
) sweep_angle
= -2.0 * pi
;
72 double total_sweep
= 0.0;
73 double local_sweep
= 0.0;
80 local_sweep
= -pi
* 0.5;
81 total_sweep
-= pi
* 0.5;
82 if(total_sweep
<= sweep_angle
)
84 local_sweep
= sweep_angle
- (total_sweep
+ pi
* 0.5);
90 local_sweep
= pi
* 0.5;
91 total_sweep
+= pi
* 0.5;
92 if(total_sweep
>= sweep_angle
)
94 local_sweep
= sweep_angle
- (total_sweep
- pi
* 0.5);
99 arc_to_bezier(x
, y
, rx
, ry
,
102 m_vertices
+ m_num_vertices
- 2);
105 start_angle
+= local_sweep
;
107 while(!done
&& m_num_vertices
< 26);
113 //--------------------------------------------------------------------
114 void bezier_arc_svg::init(double x0
, double y0
,
115 double rx
, double ry
,
119 double x2
, double y2
)
123 if(rx
< 0.0) rx
= -rx
;
124 if(ry
< 0.0) ry
= -rx
;
126 // Calculate the middle point between
127 // the current and the final points
128 //------------------------
129 double dx2
= (x0
- x2
) / 2.0;
130 double dy2
= (y0
- y2
) / 2.0;
132 // Convert angle from degrees to radians
133 //------------------------
134 double cos_a
= cos(angle
);
135 double sin_a
= sin(angle
);
137 // Calculate (x1, y1)
138 //------------------------
139 double x1
= cos_a
* dx2
+ sin_a
* dy2
;
140 double y1
= -sin_a
* dx2
+ cos_a
* dy2
;
142 // Ensure radii are large enough
143 //------------------------
144 double prx
= rx
* rx
;
145 double pry
= ry
* ry
;
146 double px1
= x1
* x1
;
147 double py1
= y1
* y1
;
149 // Check that radii are large enough
150 //------------------------
151 double radii_check
= px1
/prx
+ py1
/pry
;
152 if(radii_check
> 1.0)
154 rx
= sqrt(radii_check
) * rx
;
155 ry
= sqrt(radii_check
) * ry
;
158 if(radii_check
> 10.0) m_radii_ok
= false;
161 // Calculate (cx1, cy1)
162 //------------------------
163 double sign
= (large_arc_flag
== sweep_flag
) ? -1.0 : 1.0;
164 double sq
= (prx
*pry
- prx
*py1
- pry
*px1
) / (prx
*py1
+ pry
*px1
);
165 double coef
= sign
* sqrt((sq
< 0) ? 0 : sq
);
166 double cx1
= coef
* ((rx
* y1
) / ry
);
167 double cy1
= coef
* -((ry
* x1
) / rx
);
170 // Calculate (cx, cy) from (cx1, cy1)
171 //------------------------
172 double sx2
= (x0
+ x2
) / 2.0;
173 double sy2
= (y0
+ y2
) / 2.0;
174 double cx
= sx2
+ (cos_a
* cx1
- sin_a
* cy1
);
175 double cy
= sy2
+ (sin_a
* cx1
+ cos_a
* cy1
);
177 // Calculate the start_angle (angle1) and the sweep_angle (dangle)
178 //------------------------
179 double ux
= (x1
- cx1
) / rx
;
180 double uy
= (y1
- cy1
) / ry
;
181 double vx
= (-x1
- cx1
) / rx
;
182 double vy
= (-y1
- cy1
) / ry
;
185 // Calculate the angle start
186 //------------------------
187 n
= sqrt(ux
*ux
+ uy
*uy
);
188 p
= ux
; // (1 * ux) + (0 * uy)
189 sign
= (uy
< 0) ? -1.0 : 1.0;
191 if(v
< -1.0) v
= -1.0;
193 double start_angle
= sign
* acos(v
);
195 // Calculate the sweep angle
196 //------------------------
197 n
= sqrt((ux
*ux
+ uy
*uy
) * (vx
*vx
+ vy
*vy
));
198 p
= ux
* vx
+ uy
* vy
;
199 sign
= (ux
* vy
- uy
* vx
< 0) ? -1.0 : 1.0;
201 if(v
< -1.0) v
= -1.0;
203 double sweep_angle
= sign
* acos(v
);
204 if(!sweep_flag
&& sweep_angle
> 0)
206 sweep_angle
-= pi
* 2.0;
209 if (sweep_flag
&& sweep_angle
< 0)
211 sweep_angle
+= pi
* 2.0;
214 // We can now build and transform the resulting arc
215 //------------------------
216 m_arc
.init(0.0, 0.0, rx
, ry
, start_angle
, sweep_angle
);
217 trans_affine mtx
= trans_affine_rotation(angle
);
218 mtx
*= trans_affine_translation(cx
, cy
);
220 for(unsigned i
= 2; i
< m_arc
.num_vertices()-2; i
+= 2)
222 mtx
.transform(m_arc
.vertices() + i
, m_arc
.vertices() + i
+ 1);
225 // We must make sure that the starting and ending points
226 // exactly coincide with the initial (x0,y0) and (x2,y2)
227 m_arc
.vertices()[0] = x0
;
228 m_arc
.vertices()[1] = y0
;
229 if(m_arc
.num_vertices() > 2)
231 m_arc
.vertices()[m_arc
.num_vertices() - 2] = x2
;
232 m_arc
.vertices()[m_arc
.num_vertices() - 1] = y2
;