1 2 //---------------------------------------------------------------------------- 3 // Anti-Grain Geometry - Version 2.3 4 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) 5 // 6 // Permission to copy, use, modify, sell and distribute this software 7 // is granted provided this copyright notice appears in all copies. 8 // This software is provided "as is" without express or implied 9 // warranty, and with no claim as to its suitability for any purpose. 10 // 11 //---------------------------------------------------------------------------- 12 // Contact: mcseem (at) antigrain.com 13 // mcseemagg (at) yahoo.com 14 // http://www.antigrain.com 15 //---------------------------------------------------------------------------- 16 // 17 // Stroke math 18 // 19 //---------------------------------------------------------------------------- 20 #ifndef AGG_STROKE_MATH_INCLUDED 21 #define AGG_STROKE_MATH_INCLUDED 22 #include "agg_math.h" 23 #include "agg_vertex_sequence.h" 24 namespace agg 25 { 26 enum line_cap_e { 27 butt_cap, 28 square_cap, 29 round_cap 30 }; 31 enum line_join_e { 32 miter_join = 0, 33 miter_join_revert = 1, 34 miter_join_round = 4, 35 round_join = 2, 36 bevel_join = 3 37 }; 38 enum inner_join_e { 39 inner_bevel, 40 inner_miter, 41 inner_jag, 42 inner_round 43 }; 44 const FX_FLOAT stroke_theta = 1.0f / 1000.0f; 45 template<class VertexConsumer> 46 void stroke_calc_arc(VertexConsumer& out_vertices, 47 FX_FLOAT x, FX_FLOAT y, 48 FX_FLOAT dx1, FX_FLOAT dy1, 49 FX_FLOAT dx2, FX_FLOAT dy2, 50 FX_FLOAT width, 51 FX_FLOAT approximation_scale) 52 { 53 typedef typename VertexConsumer::value_type coord_type; 54 FX_FLOAT a1 = FXSYS_atan2(dy1, dx1); 55 FX_FLOAT a2 = FXSYS_atan2(dy2, dx2); 56 FX_FLOAT da = a1 - a2; 57 bool ccw = da > 0 && da < FX_PI; 58 if(width < 0) { 59 width = -width; 60 } 61 da = FXSYS_acos(FXSYS_Div(width, width + FXSYS_Div(1.0f / 8, approximation_scale))) * 2; 62 out_vertices.add(coord_type(x + dx1, y + dy1)); 63 if(!ccw) { 64 if(a1 > a2) { 65 a2 += 2 * FX_PI; 66 } 67 a2 -= da / 4; 68 a1 += da; 69 while(a1 < a2) { 70 out_vertices.add(coord_type(x + FXSYS_Mul(width, FXSYS_cos(a1)), 71 y + FXSYS_Mul(width, FXSYS_sin(a1)))); 72 a1 += da; 73 } 74 } else { 75 if(a1 < a2) { 76 a2 -= 2 * FX_PI; 77 } 78 a2 += da / 4; 79 a1 -= da; 80 while(a1 > a2) { 81 out_vertices.add(coord_type(x + FXSYS_Mul(width, FXSYS_cos(a1)), 82 y + FXSYS_Mul(width, FXSYS_sin(a1)))); 83 a1 -= da; 84 } 85 } 86 out_vertices.add(coord_type(x + dx2, y + dy2)); 87 } 88 template<class VertexConsumer> 89 void stroke_calc_miter(VertexConsumer& out_vertices, 90 const vertex_dist& v0, 91 const vertex_dist& v1, 92 const vertex_dist& v2, 93 FX_FLOAT dx1, FX_FLOAT dy1, 94 FX_FLOAT dx2, FX_FLOAT dy2, 95 FX_FLOAT width, 96 line_join_e line_join, 97 FX_FLOAT miter_limit, 98 FX_FLOAT approximation_scale) 99 { 100 typedef typename VertexConsumer::value_type coord_type; 101 FX_FLOAT xi = v1.x; 102 FX_FLOAT yi = v1.y; 103 bool miter_limit_exceeded = true; 104 if(calc_intersection(v0.x + dx1, v0.y - dy1, 105 v1.x + dx1, v1.y - dy1, 106 v1.x + dx2, v1.y - dy2, 107 v2.x + dx2, v2.y - dy2, 108 &xi, &yi)) { 109 FX_FLOAT d1 = calc_distance(v1.x, v1.y, xi, yi); 110 FX_FLOAT lim = FXSYS_Mul(width, miter_limit); 111 if(d1 <= lim) { 112 out_vertices.add(coord_type(xi, yi)); 113 miter_limit_exceeded = false; 114 } 115 } else { 116 FX_FLOAT x2 = v1.x + dx1; 117 FX_FLOAT y2 = v1.y - dy1; 118 if((FXSYS_Mul(x2 - v0.x, dy1) - FXSYS_Mul(v0.y - y2, dx1) < 0) != 119 (FXSYS_Mul(x2 - v2.x, dy1) - FXSYS_Mul(v2.y - y2, dx1) < 0)) { 120 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 121 miter_limit_exceeded = false; 122 } 123 } 124 if(miter_limit_exceeded) { 125 switch(line_join) { 126 case miter_join_revert: 127 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 128 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 129 break; 130 case miter_join_round: 131 stroke_calc_arc(out_vertices, 132 v1.x, v1.y, dx1, -dy1, dx2, -dy2, 133 width, approximation_scale); 134 break; 135 default: 136 out_vertices.add(coord_type(v1.x + dx1 + FXSYS_Mul(dy1, miter_limit), 137 v1.y - dy1 + FXSYS_Mul(dx1, miter_limit))); 138 out_vertices.add(coord_type(v1.x + dx2 - FXSYS_Mul(dy2, miter_limit), 139 v1.y - dy2 - FXSYS_Mul(dx2, miter_limit))); 140 break; 141 } 142 } 143 } 144 template<class VertexConsumer> 145 void stroke_calc_cap(VertexConsumer& out_vertices, 146 const vertex_dist& v0, 147 const vertex_dist& v1, 148 FX_FLOAT len, 149 line_cap_e line_cap, 150 FX_FLOAT width, 151 FX_FLOAT approximation_scale) 152 { 153 typedef typename VertexConsumer::value_type coord_type; 154 out_vertices.remove_all(); 155 FX_FLOAT dx1 = FXSYS_Div(v1.y - v0.y, len); 156 FX_FLOAT dy1 = FXSYS_Div(v1.x - v0.x, len); 157 FX_FLOAT dx2 = 0; 158 FX_FLOAT dy2 = 0; 159 dx1 = FXSYS_Mul(dx1, width); 160 dy1 = FXSYS_Mul(dy1, width); 161 if(line_cap != round_cap) { 162 if(line_cap == square_cap) { 163 dx2 = dy1; 164 dy2 = dx1; 165 } 166 out_vertices.add(coord_type(v0.x - dx1 - dx2, v0.y + dy1 - dy2)); 167 out_vertices.add(coord_type(v0.x + dx1 - dx2, v0.y - dy1 - dy2)); 168 } else { 169 FX_FLOAT a1 = FXSYS_atan2(dy1, -dx1); 170 FX_FLOAT a2 = a1 + FX_PI; 171 FX_FLOAT da = FXSYS_acos(FXSYS_Div(width, width + 172 FXSYS_Div(1.0f / 8, approximation_scale))) * 2; 173 out_vertices.add(coord_type(v0.x - dx1, v0.y + dy1)); 174 a1 += da; 175 a2 -= da / 4; 176 while(a1 < a2) { 177 out_vertices.add(coord_type(v0.x + FXSYS_Mul(width, FXSYS_cos(a1)), 178 v0.y + FXSYS_Mul(width, FXSYS_sin(a1)))); 179 a1 += da; 180 } 181 out_vertices.add(coord_type(v0.x + dx1, v0.y - dy1)); 182 } 183 } 184 template<class VertexConsumer> 185 void stroke_calc_join(VertexConsumer& out_vertices, 186 const vertex_dist& v0, 187 const vertex_dist& v1, 188 const vertex_dist& v2, 189 FX_FLOAT len1, 190 FX_FLOAT len2, 191 FX_FLOAT width, 192 line_join_e line_join, 193 inner_join_e inner_join, 194 FX_FLOAT miter_limit, 195 FX_FLOAT inner_miter_limit, 196 FX_FLOAT approximation_scale) 197 { 198 typedef typename VertexConsumer::value_type coord_type; 199 FX_FLOAT dx1, dy1, dx2, dy2; 200 dx1 = FXSYS_MulDiv(width, v1.y - v0.y, len1); 201 dy1 = FXSYS_MulDiv(width, v1.x - v0.x, len1); 202 dx2 = FXSYS_MulDiv(width, v2.y - v1.y, len2); 203 dy2 = FXSYS_MulDiv(width, v2.x - v1.x, len2); 204 out_vertices.remove_all(); 205 if(calc_point_location(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y) > 0) { 206 switch(inner_join) { 207 default: 208 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 209 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 210 break; 211 case inner_miter: 212 stroke_calc_miter(out_vertices, 213 v0, v1, v2, dx1, dy1, dx2, dy2, 214 width, 215 miter_join_revert, 216 inner_miter_limit, 217 1.0f); 218 break; 219 case inner_jag: 220 case inner_round: { 221 FX_FLOAT d = (dx1 - dx2) * (dx1 - dx2) + (dy1 - dy2) * (dy1 - dy2); 222 if(d < len1 * len1 && d < len2 * len2) { 223 stroke_calc_miter(out_vertices, 224 v0, v1, v2, dx1, dy1, dx2, dy2, 225 width, 226 miter_join_revert, 227 inner_miter_limit, 228 1.0f); 229 } else { 230 if(inner_join == inner_jag) { 231 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 232 out_vertices.add(coord_type(v1.x, v1.y )); 233 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 234 } else { 235 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 236 out_vertices.add(coord_type(v1.x, v1.y )); 237 stroke_calc_arc(out_vertices, 238 v1.x, v1.y, dx2, -dy2, dx1, -dy1, 239 width, approximation_scale); 240 out_vertices.add(coord_type(v1.x, v1.y )); 241 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 242 } 243 } 244 } 245 break; 246 } 247 } else { 248 switch(line_join) { 249 case miter_join: 250 case miter_join_revert: 251 case miter_join_round: 252 stroke_calc_miter(out_vertices, 253 v0, v1, v2, dx1, dy1, dx2, dy2, 254 width, 255 line_join, 256 miter_limit, 257 approximation_scale); 258 break; 259 case round_join: 260 stroke_calc_arc(out_vertices, 261 v1.x, v1.y, dx1, -dy1, dx2, -dy2, 262 width, approximation_scale); 263 break; 264 default: 265 out_vertices.add(coord_type(v1.x + dx1, v1.y - dy1)); 266 out_vertices.add(coord_type(v1.x + dx2, v1.y - dy2)); 267 break; 268 } 269 } 270 } 271 } 272 #endif 273