merge the formfield patch from ooo-build
[ooovba.git] / agg / source / agg_bezier_arc.cpp
blob3517f168b387ab08e2dda0d14b27a8ac27002ce3
1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.3
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 //----------------------------------------------------------------------------
16 // Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e.,
17 // 4, 7, 10, or 13 vertices.
19 //----------------------------------------------------------------------------
22 #include <math.h>
23 #include "agg_bezier_arc.h"
26 namespace agg
29 //------------------------------------------------------------arc_to_bezier
30 void arc_to_bezier(double cx, double cy, double rx, double ry,
31 double start_angle, double sweep_angle,
32 double* curve)
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;
38 double px[4];
39 double py[4];
40 px[0] = x0;
41 py[0] = -y0;
42 px[1] = x0 + tx;
43 py[1] = -ty;
44 px[2] = x0 + tx;
45 py[2] = ty;
46 px[3] = x0;
47 py[3] = y0;
49 double sn = sin(start_angle + sweep_angle / 2.0);
50 double cs = cos(start_angle + sweep_angle / 2.0);
52 unsigned i;
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,
64 double rx, double ry,
65 double start_angle,
66 double sweep_angle)
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;
74 m_num_vertices = 2;
75 bool done = false;
78 if(sweep_angle < 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);
85 done = true;
88 else
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);
95 done = true;
99 arc_to_bezier(x, y, rx, ry,
100 start_angle,
101 local_sweep,
102 m_vertices + m_num_vertices - 2);
104 m_num_vertices += 6;
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,
116 double angle,
117 bool large_arc_flag,
118 bool sweep_flag,
119 double x2, double y2)
121 m_radii_ok = true;
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;
156 prx = rx * rx;
157 pry = ry * 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;
183 double p, n;
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;
190 double v = p / n;
191 if(v < -1.0) v = -1.0;
192 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;
200 v = p / n;
201 if(v < -1.0) v = -1.0;
202 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;
208 else
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;