1 //---------------------------------------------------------------------------- 2 // Anti-Grain Geometry - Version 2.2 3 // Copyright (C) 2002-2004 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 // Stroke math 17 // 18 //---------------------------------------------------------------------------- 19 20 #ifndef AGG_STROKE_MATH_INCLUDED 21 #define AGG_STROKE_MATH_INCLUDED 22 23 #include "agg_math.h" 24 #include "agg_vertex_sequence.h" 25 26 namespace agg 27 { 28 //-------------------------------------------------------------line_cap_e 29 enum line_cap_e 30 { 31 butt_cap, 32 square_cap, 33 round_cap 34 }; 35 36 //------------------------------------------------------------line_join_e 37 enum line_join_e 38 { 39 miter_join, 40 miter_join_revert, 41 round_join, 42 bevel_join 43 }; 44 45 // Minimal angle to calculate round joins, less than 0.1 degree. 46 const double stroke_theta = 0.001; //----stroke_theta 47 48 49 //--------------------------------------------------------stroke_calc_arc 50 template<class VertexConsumer> 51 void stroke_calc_arc(VertexConsumer& out_vertices, 52 double x, double y, 53 double dx1, double dy1, 54 double dx2, double dy2, 55 double width, 56 double approximation_scale) 57 { 58 typedef typename VertexConsumer::value_type coord_type; 59 60 // Check if we actually need the arc 61 //----------------- 62 double dd = calc_distance(dx1, dy1, dx2, dy2); 63 if(dd < approximation_scale) 64 { 65 out_vertices.add(coord_type(x + dx1, y + dy1)); 66 if(dd > approximation_scale * 0.25) 67 { 68 out_vertices.add(coord_type(x + dx2, y + dy2)); 69 } 70 return; 71 } 72 73 double a1 = atan2(dy1, dx1); 74 double a2 = atan2(dy2, dx2); 75 double da = a1 - a2; 76 77 //if(fabs(da) < stroke_theta) 78 //{ 79 // out_vertices.add(coord_type(x + dx1, y + dy1)); 80 // //out_vertices.add(coord_type(x + dx2, y + dy2)); 81 // return; 82 //} 83 84 bool ccw = da > 0.0 && da < pi; 85 86 if(width < 0) width = -width; 87 da = fabs(1.0 / (width * approximation_scale)); 88 if(!ccw) 89 { 90 if(a1 > a2) a2 += 2 * pi; 91 while(a1 < a2) 92 { 93 out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width)); 94 a1 += da; 95 } 96 } 97 else 98 { 99 if(a1 < a2) a2 -= 2 * pi; 100 while(a1 > a2) 101 { 102 out_vertices.add(coord_type(x + cos(a1) * width, y + sin(a1) * width)); 103 a1 -= da; 104 } 105 } 106 out_vertices.add(coord_type(x + dx2, y + dy2)); 107 } 108 109 110 111 //-------------------------------------------------------stroke_calc_miter 112 template<class VertexConsumer> 113 void stroke_calc_miter(VertexConsumer& out_vertices, 114 const vertex_dist& v0, 115 const vertex_dist& v1, 116 const vertex_dist& v2, 117 double dx1, double dy1, 118 double dx2, double dy2, 119 double width, 120 bool revert_flag, 121 double miter_limit) 122 { 123 typedef typename VertexConsumer::value_type coord_type; 124 125 double xi = v1.x; 126 double yi = v1.y; 127 128 if(!calc_intersection(v0.x + dx1, v0.y - dy1, 129 v1.x + dx1, v1.y - dy1, 130 v1.x + dx2, v1.y - dy2, 131 v2.x + dx2, v2.y - dy2, 132 &xi, &yi)) 133 { 134 // The calculation didn't succeed, most probaly 135 // the three points lie one straight line 136 //---------------- 137 if(calc_distance(dx1, -dy1, dx2, -dy2) < width * 0.025) 138 { 139 // This case means that the next segment continues 140 // the previous one (straight line) 141 //----------------- 142 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 143 } 144 else 145 { 146 // This case means that the next segment goes back 147 //----------------- 148 if(revert_flag) 149 { 150 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 151 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 152 } 153 else 154 { 155 // If no miter-revert, calcuate new dx1, dy1, dx2, dy2 156 out_vertices.add(coord_type(v1.x + dx1 + dy1 * miter_limit, 157 v1.y - dy1 + dx1 * miter_limit)); 158 out_vertices.add(coord_type(v1.x + dx2 - dy2 * miter_limit, 159 v1.y - dy2 - dx2 * miter_limit)); 160 } 161 } 162 } 163 else 164 { 165 double d1 = calc_distance(v1.x, v1.y, xi, yi); 166 double lim = width * miter_limit; 167 if(d1 > lim) 168 { 169 // Miter limit exceeded 170 //------------------------ 171 if(revert_flag) 172 { 173 // For the compatibility with SVG, PDF, etc, 174 // we use a simple bevel join instead of 175 // "smart" bevel 176 //------------------- 177 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 178 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 179 } 180 else 181 { 182 // Smart bevel that cuts the miter at the limit point 183 //------------------- 184 d1 = lim / d1; 185 double x1 = v1.x + dx1; 186 double y1 = v1.y - dy1; 187 double x2 = v1.x + dx2; 188 double y2 = v1.y - dy2; 189 190 x1 += (xi - x1) * d1; 191 y1 += (yi - y1) * d1; 192 x2 += (xi - x2) * d1; 193 y2 += (yi - y2) * d1; 194 out_vertices.add(coord_type(x1, y1)); 195 out_vertices.add(coord_type(x2, y2)); 196 } 197 } 198 else 199 { 200 // Inside the miter limit 201 //--------------------- 202 out_vertices.add(coord_type(xi, yi)); 203 } 204 } 205 } 206 207 208 209 210 211 212 //--------------------------------------------------------stroke_calc_cap 213 template<class VertexConsumer> 214 void stroke_calc_cap(VertexConsumer& out_vertices, 215 const vertex_dist& v0, 216 const vertex_dist& v1, 217 double len, 218 line_cap_e line_cap, 219 double width, 220 double approximation_scale) 221 { 222 typedef typename VertexConsumer::value_type coord_type; 223 224 out_vertices.remove_all(); 225 226 double dx1 = width * (v1.y - v0.y) / len; 227 double dy1 = width * (v1.x - v0.x) / len; 228 double dx2 = 0; 229 double dy2 = 0; 230 231 if(line_cap == square_cap) 232 { 233 dx2 = dy1; 234 dy2 = dx1; 235 } 236 237 if(line_cap == round_cap) 238 { 239 double a1 = atan2(dy1, -dx1); 240 double a2 = a1 + pi; 241 double da = fabs(1.0 / (width * approximation_scale)); 242 while(a1 < a2) 243 { 244 out_vertices.add(coord_type(v0.x + cos(a1) * width, 245 v0.y + sin(a1) * width)); 246 a1 += da; 247 } 248 out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1)); 249 } 250 else 251 { 252 out_vertices.add(coord_type(v0.x - dx1 - dx2, v0.y + dy1 - dy2)); 253 out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2)); 254 } 255 } 256 257 258 259 //-------------------------------------------------------stroke_calc_join 260 template<class VertexConsumer> 261 void stroke_calc_join(VertexConsumer& out_vertices, 262 const vertex_dist& v0, 263 const vertex_dist& v1, 264 const vertex_dist& v2, 265 double len1, 266 double len2, 267 double width, 268 line_join_e line_join, 269 line_join_e inner_line_join, 270 double miter_limit, 271 double inner_miter_limit, 272 double approximation_scale) 273 { 274 typedef typename VertexConsumer::value_type coord_type; 275 276 double dx1, dy1, dx2, dy2; 277 278 dx1 = width * (v1.y - v0.y) / len1; 279 dy1 = width * (v1.x - v0.x) / len1; 280 281 dx2 = width * (v2.y - v1.y) / len2; 282 dy2 = width * (v2.x - v1.x) / len2; 283 284 out_vertices.remove_all(); 285 286 if(calc_point_location(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y) > 0.0) 287 { 288 // Inner join 289 //--------------- 290 stroke_calc_miter(out_vertices, 291 v0, v1, v2, dx1, dy1, dx2, dy2, 292 width, 293 inner_line_join == miter_join_revert, 294 inner_miter_limit); 295 } 296 else 297 { 298 // Outer join 299 //--------------- 300 switch(line_join) 301 { 302 case miter_join: 303 stroke_calc_miter(out_vertices, 304 v0, v1, v2, dx1, dy1, dx2, dy2, 305 width, 306 false, 307 miter_limit); 308 break; 309 310 case miter_join_revert: 311 stroke_calc_miter(out_vertices, 312 v0, v1, v2, dx1, dy1, dx2, dy2, 313 width, 314 true, 315 miter_limit); 316 break; 317 318 case round_join: 319 stroke_calc_arc(out_vertices, 320 v1.x, v1.y, dx1, -dy1, dx2, -dy2, 321 width, approximation_scale); 322 break; 323 324 default: // Bevel join 325 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 326 if(calc_distance(dx1, dy1, dx2, dy2) > approximation_scale * 0.25) 327 { 328 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 329 } 330 break; 331 } 332 } 333 } 334 335 336 337 338 } 339 340 #endif 341