xref: /haiku/src/libs/agg/src/agg_bezier_arc.cpp (revision e39da397f5ff79f2db9f9a3ddf1852b6710578af)
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 
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
arc_to_bezier(double cx,double cy,double rx,double ry,double start_angle,double sweep_angle,double * curve)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     //------------------------------------------------------------------------
init(double x,double y,double rx,double ry,double start_angle,double sweep_angle)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     //--------------------------------------------------------------------
init(double x0,double y0,double rx,double ry,double angle,bool large_arc_flag,bool sweep_flag,double x2,double y2)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