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
//----------------------------------------------------------------------------
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
// This epsilon is used to prevent us from adding degenerate curves
30
// (converging to a single point).
31
// The value isn't very critical. Function arc_to_bezier() has a limit
32
// of the sweep_angle. If fabs(sweep_angle) exceeds pi/2 the curve
33
// becomes inaccurate. But slight exceeding is quite appropriate.
34
//-------------------------------------------------bezier_arc_angle_epsilon
35
const double bezier_arc_angle_epsilon = 0.01;
37
//------------------------------------------------------------arc_to_bezier
38
void arc_to_bezier(double cx, double cy, double rx, double ry,
39
double start_angle, double sweep_angle,
42
double x0 = cos(sweep_angle / 2.0);
43
double y0 = sin(sweep_angle / 2.0);
44
double tx = (1.0 - x0) * 4.0 / 3.0;
45
double ty = y0 - tx * x0 / y0;
57
double sn = sin(start_angle + sweep_angle / 2.0);
58
double cs = cos(start_angle + sweep_angle / 2.0);
61
for(i = 0; i < 4; i++)
63
curve[i * 2] = cx + rx * (px[i] * cs - py[i] * sn);
64
curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs);
70
//------------------------------------------------------------------------
71
void bezier_arc::init(double x, double y,
76
start_angle = fmod(start_angle, 2.0 * pi);
77
if(sweep_angle >= 2.0 * pi) sweep_angle = 2.0 * pi;
78
if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi;
80
if(fabs(sweep_angle) < 1e-10)
83
m_cmd = path_cmd_line_to;
84
m_vertices[0] = x + rx * cos(start_angle);
85
m_vertices[1] = y + ry * sin(start_angle);
86
m_vertices[2] = x + rx * cos(start_angle + sweep_angle);
87
m_vertices[3] = y + ry * sin(start_angle + sweep_angle);
91
double total_sweep = 0.0;
92
double local_sweep = 0.0;
95
m_cmd = path_cmd_curve4;
101
prev_sweep = total_sweep;
102
local_sweep = -pi * 0.5;
103
total_sweep -= pi * 0.5;
104
if(total_sweep <= sweep_angle + bezier_arc_angle_epsilon)
106
local_sweep = sweep_angle - prev_sweep;
112
prev_sweep = total_sweep;
113
local_sweep = pi * 0.5;
114
total_sweep += pi * 0.5;
115
if(total_sweep >= sweep_angle - bezier_arc_angle_epsilon)
117
local_sweep = sweep_angle - prev_sweep;
122
arc_to_bezier(x, y, rx, ry,
125
m_vertices + m_num_vertices - 2);
128
start_angle += local_sweep;
130
while(!done && m_num_vertices < 26);
136
//--------------------------------------------------------------------
137
void bezier_arc_svg::init(double x0, double y0,
138
double rx, double ry,
142
double x2, double y2)
146
if(rx < 0.0) rx = -rx;
147
if(ry < 0.0) ry = -rx;
149
// Calculate the middle point between
150
// the current and the final points
151
//------------------------
152
double dx2 = (x0 - x2) / 2.0;
153
double dy2 = (y0 - y2) / 2.0;
155
double cos_a = cos(angle);
156
double sin_a = sin(angle);
158
// Calculate (x1, y1)
159
//------------------------
160
double x1 = cos_a * dx2 + sin_a * dy2;
161
double y1 = -sin_a * dx2 + cos_a * dy2;
163
// Ensure radii are large enough
164
//------------------------
165
double prx = rx * rx;
166
double pry = ry * ry;
167
double px1 = x1 * x1;
168
double py1 = y1 * y1;
170
// Check that radii are large enough
171
//------------------------
172
double radii_check = px1/prx + py1/pry;
173
if(radii_check > 1.0)
175
rx = sqrt(radii_check) * rx;
176
ry = sqrt(radii_check) * ry;
179
if(radii_check > 10.0) m_radii_ok = false;
182
// Calculate (cx1, cy1)
183
//------------------------
184
double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0;
185
double sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1);
186
double coef = sign * sqrt((sq < 0) ? 0 : sq);
187
double cx1 = coef * ((rx * y1) / ry);
188
double cy1 = coef * -((ry * x1) / rx);
191
// Calculate (cx, cy) from (cx1, cy1)
192
//------------------------
193
double sx2 = (x0 + x2) / 2.0;
194
double sy2 = (y0 + y2) / 2.0;
195
double cx = sx2 + (cos_a * cx1 - sin_a * cy1);
196
double cy = sy2 + (sin_a * cx1 + cos_a * cy1);
198
// Calculate the start_angle (angle1) and the sweep_angle (dangle)
199
//------------------------
200
double ux = (x1 - cx1) / rx;
201
double uy = (y1 - cy1) / ry;
202
double vx = (-x1 - cx1) / rx;
203
double vy = (-y1 - cy1) / ry;
206
// Calculate the angle start
207
//------------------------
208
n = sqrt(ux*ux + uy*uy);
209
p = ux; // (1 * ux) + (0 * uy)
210
sign = (uy < 0) ? -1.0 : 1.0;
212
if(v < -1.0) v = -1.0;
214
double start_angle = sign * acos(v);
216
// Calculate the sweep angle
217
//------------------------
218
n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy));
219
p = ux * vx + uy * vy;
220
sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
222
if(v < -1.0) v = -1.0;
224
double sweep_angle = sign * acos(v);
225
if(!sweep_flag && sweep_angle > 0)
227
sweep_angle -= pi * 2.0;
230
if (sweep_flag && sweep_angle < 0)
232
sweep_angle += pi * 2.0;
235
// We can now build and transform the resulting arc
236
//------------------------
237
m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle);
238
trans_affine mtx = trans_affine_rotation(angle);
239
mtx *= trans_affine_translation(cx, cy);
241
for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2)
243
mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1);
246
// We must make sure that the starting and ending points
247
// exactly coincide with the initial (x0,y0) and (x2,y2)
248
m_arc.vertices()[0] = x0;
249
m_arc.vertices()[1] = y0;
250
if(m_arc.num_vertices() > 2)
252
m_arc.vertices()[m_arc.num_vertices() - 2] = x2;
253
m_arc.vertices()[m_arc.num_vertices() - 1] = y2;