Anti-Grain Geometry Tutorial
agg_bezier_arc.cpp
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 // Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e.,
17 // 4, 7, 10, or 13 vertices.
18 //
19 //----------------------------------------------------------------------------
20 
21 
22 #include <cmath>
23 #include "agg_bezier_arc.h"
24 
25 
26 namespace agg
27 {
28 
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;
36 
37  //------------------------------------------------------------arc_to_bezier
38  void arc_to_bezier(double cx, double cy, double rx, double ry,
39  double start_angle, double sweep_angle,
40  double* curve)
41  {
42  double x0 = std::cos(sweep_angle / 2.0);
43  double y0 = std::sin(sweep_angle / 2.0);
44  double tx = (1.0 - x0) * 4.0 / 3.0;
45  double ty = y0 - tx * x0 / y0;
46  double px[4];
47  double py[4];
48  px[0] = x0;
49  py[0] = -y0;
50  px[1] = x0 + tx;
51  py[1] = -ty;
52  px[2] = x0 + tx;
53  py[2] = ty;
54  px[3] = x0;
55  py[3] = y0;
56 
57  double sn = std::sin(start_angle + sweep_angle / 2.0);
58  double cs = std::cos(start_angle + sweep_angle / 2.0);
59 
60  unsigned i;
61  for(i = 0; i < 4; i++)
62  {
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);
65  }
66  }
67 
68 
69 
70  //------------------------------------------------------------------------
71  void bezier_arc::init(double x, double y,
72  double rx, double ry,
73  double start_angle,
74  double sweep_angle)
75  {
76  start_angle = std::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;
79 
80  if(std::fabs(sweep_angle) < 1e-10)
81  {
82  m_num_vertices = 4;
83  m_cmd = path_cmd_line_to;
84  m_vertices[0] = x + rx * std::cos(start_angle);
85  m_vertices[1] = y + ry * std::sin(start_angle);
86  m_vertices[2] = x + rx * std::cos(start_angle + sweep_angle);
87  m_vertices[3] = y + ry * std::sin(start_angle + sweep_angle);
88  return;
89  }
90 
91  double total_sweep = 0.0;
92  double local_sweep = 0.0;
93  double prev_sweep;
94  m_num_vertices = 2;
95  m_cmd = path_cmd_curve4;
96  bool done = false;
97  do
98  {
99  if(sweep_angle < 0.0)
100  {
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)
105  {
106  local_sweep = sweep_angle - prev_sweep;
107  done = true;
108  }
109  }
110  else
111  {
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)
116  {
117  local_sweep = sweep_angle - prev_sweep;
118  done = true;
119  }
120  }
121 
122  arc_to_bezier(x, y, rx, ry,
123  start_angle,
124  local_sweep,
125  m_vertices + m_num_vertices - 2);
126 
127  m_num_vertices += 6;
128  start_angle += local_sweep;
129  }
130  while(!done && m_num_vertices < 26);
131  }
132 
133 
134 
135 
136  //--------------------------------------------------------------------
137  void bezier_arc_svg::init(double x0, double y0,
138  double rx, double ry,
139  double angle,
140  bool large_arc_flag,
141  bool sweep_flag,
142  double x2, double y2)
143  {
144  m_radii_ok = true;
145 
146  if(rx < 0.0) rx = -rx;
147  if(ry < 0.0) ry = -rx;
148 
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;
154 
155  double cos_a = std::cos(angle);
156  double sin_a = std::sin(angle);
157 
158  // Calculate (x1, y1)
159  //------------------------
160  double x1 = cos_a * dx2 + sin_a * dy2;
161  double y1 = -sin_a * dx2 + cos_a * dy2;
162 
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;
169 
170  // Check that radii are large enough
171  //------------------------
172  double radii_check = px1/prx + py1/pry;
173  if(radii_check > 1.0)
174  {
175  rx = std::sqrt(radii_check) * rx;
176  ry = std::sqrt(radii_check) * ry;
177  prx = rx * rx;
178  pry = ry * ry;
179  if(radii_check > 10.0) m_radii_ok = false;
180  }
181 
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 * std::sqrt((sq < 0) ? 0 : sq);
187  double cx1 = coef * ((rx * y1) / ry);
188  double cy1 = coef * -((ry * x1) / rx);
189 
190  //
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);
197 
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;
204  double p, n;
205 
206  // Calculate the angle start
207  //------------------------
208  n = std::sqrt(ux*ux + uy*uy);
209  p = ux; // (1 * ux) + (0 * uy)
210  sign = (uy < 0) ? -1.0 : 1.0;
211  double v = p / n;
212  if(v < -1.0) v = -1.0;
213  if(v > 1.0) v = 1.0;
214  double start_angle = sign * std::acos(v);
215 
216  // Calculate the sweep angle
217  //------------------------
218  n = std::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;
221  v = p / n;
222  if(v < -1.0) v = -1.0;
223  if(v > 1.0) v = 1.0;
224  double sweep_angle = sign * std::acos(v);
225  if(!sweep_flag && sweep_angle > 0)
226  {
227  sweep_angle -= pi * 2.0;
228  }
229  else
230  if (sweep_flag && sweep_angle < 0)
231  {
232  sweep_angle += pi * 2.0;
233  }
234 
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);
240 
241  for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2)
242  {
243  mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1);
244  }
245 
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)
251  {
252  m_arc.vertices()[m_arc.num_vertices() - 2] = x2;
253  m_arc.vertices()[m_arc.num_vertices() - 1] = y2;
254  }
255  }
256 
257 
258 }
Definition: agg_arc.cpp:24