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