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 <math.h>
23#include "agg_bezier_arc.h"
24
25
26namespace 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 = 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;
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 = sin(start_angle + sweep_angle / 2.0);
58        double cs = 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 = 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(fabs(sweep_angle) < 1e-10)
81        {
82            m_num_vertices = 4;
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);
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 = cos(angle);
156        double sin_a = 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 = sqrt(radii_check) * rx;
176            ry = 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 * 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 = 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 * acos(v);
215
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;
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 * 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}
259