agg_math_stroke.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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. // Stroke math
  17. //
  18. //----------------------------------------------------------------------------
  19. #ifndef AGG_STROKE_MATH_INCLUDED
  20. #define AGG_STROKE_MATH_INCLUDED
  21. #include "agg_math.h"
  22. #include "agg_vertex_sequence.h"
  23. namespace agg
  24. {
  25. //-------------------------------------------------------------line_cap_e
  26. enum line_cap_e
  27. {
  28. butt_cap,
  29. square_cap,
  30. round_cap
  31. };
  32. //------------------------------------------------------------line_join_e
  33. enum line_join_e
  34. {
  35. miter_join = 0,
  36. miter_join_revert = 1,
  37. round_join = 2,
  38. bevel_join = 3,
  39. miter_join_round = 4
  40. };
  41. //-----------------------------------------------------------inner_join_e
  42. enum inner_join_e
  43. {
  44. inner_bevel,
  45. inner_miter,
  46. inner_jag,
  47. inner_round
  48. };
  49. //------------------------------------------------------------math_stroke
  50. template<class VertexConsumer> class math_stroke
  51. {
  52. public:
  53. typedef typename VertexConsumer::value_type coord_type;
  54. math_stroke();
  55. void line_cap(line_cap_e lc) { m_line_cap = lc; }
  56. void line_join(line_join_e lj) { m_line_join = lj; }
  57. void inner_join(inner_join_e ij) { m_inner_join = ij; }
  58. line_cap_e line_cap() const { return m_line_cap; }
  59. line_join_e line_join() const { return m_line_join; }
  60. inner_join_e inner_join() const { return m_inner_join; }
  61. void width(double w);
  62. void miter_limit(double ml) { m_miter_limit = ml; }
  63. void miter_limit_theta(double t);
  64. void inner_miter_limit(double ml) { m_inner_miter_limit = ml; }
  65. void approximation_scale(double as) { m_approx_scale = as; }
  66. double width() const { return m_width * 2.0; }
  67. double miter_limit() const { return m_miter_limit; }
  68. double inner_miter_limit() const { return m_inner_miter_limit; }
  69. double approximation_scale() const { return m_approx_scale; }
  70. void calc_cap(VertexConsumer& vc,
  71. const vertex_dist& v0,
  72. const vertex_dist& v1,
  73. double len);
  74. void calc_join(VertexConsumer& vc,
  75. const vertex_dist& v0,
  76. const vertex_dist& v1,
  77. const vertex_dist& v2,
  78. double len1,
  79. double len2);
  80. private:
  81. AGG_INLINE void add_vertex(VertexConsumer& vc, double x, double y)
  82. {
  83. vc.add(coord_type(x, y));
  84. }
  85. void calc_arc(VertexConsumer& vc,
  86. double x, double y,
  87. double dx1, double dy1,
  88. double dx2, double dy2);
  89. void calc_miter(VertexConsumer& vc,
  90. const vertex_dist& v0,
  91. const vertex_dist& v1,
  92. const vertex_dist& v2,
  93. double dx1, double dy1,
  94. double dx2, double dy2,
  95. line_join_e lj,
  96. double mlimit,
  97. double dbevel);
  98. double m_width;
  99. double m_width_abs;
  100. double m_width_eps;
  101. int m_width_sign;
  102. double m_miter_limit;
  103. double m_inner_miter_limit;
  104. double m_approx_scale;
  105. line_cap_e m_line_cap;
  106. line_join_e m_line_join;
  107. inner_join_e m_inner_join;
  108. };
  109. //-----------------------------------------------------------------------
  110. template<class VC> math_stroke<VC>::math_stroke() :
  111. m_width(0.5),
  112. m_width_abs(0.5),
  113. m_width_eps(0.5/1024.0),
  114. m_width_sign(1),
  115. m_miter_limit(4.0),
  116. m_inner_miter_limit(1.01),
  117. m_approx_scale(1.0),
  118. m_line_cap(butt_cap),
  119. m_line_join(miter_join),
  120. m_inner_join(inner_miter)
  121. {
  122. }
  123. //-----------------------------------------------------------------------
  124. template<class VC> void math_stroke<VC>::width(double w)
  125. {
  126. m_width = w * 0.5;
  127. if(m_width < 0)
  128. {
  129. m_width_abs = -m_width;
  130. m_width_sign = -1;
  131. }
  132. else
  133. {
  134. m_width_abs = m_width;
  135. m_width_sign = 1;
  136. }
  137. m_width_eps = m_width / 1024.0;
  138. }
  139. //-----------------------------------------------------------------------
  140. template<class VC> void math_stroke<VC>::miter_limit_theta(double t)
  141. {
  142. m_miter_limit = 1.0 / sin(t * 0.5) ;
  143. }
  144. //-----------------------------------------------------------------------
  145. template<class VC>
  146. void math_stroke<VC>::calc_arc(VC& vc,
  147. double x, double y,
  148. double dx1, double dy1,
  149. double dx2, double dy2)
  150. {
  151. double a1 = atan2(dy1 * m_width_sign, dx1 * m_width_sign);
  152. double a2 = atan2(dy2 * m_width_sign, dx2 * m_width_sign);
  153. double da = a1 - a2;
  154. int i, n;
  155. da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
  156. add_vertex(vc, x + dx1, y + dy1);
  157. if(m_width_sign > 0)
  158. {
  159. if(a1 > a2) a2 += 2 * pi;
  160. n = int((a2 - a1) / da);
  161. da = (a2 - a1) / (n + 1);
  162. a1 += da;
  163. for(i = 0; i < n; i++)
  164. {
  165. add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
  166. a1 += da;
  167. }
  168. }
  169. else
  170. {
  171. if(a1 < a2) a2 -= 2 * pi;
  172. n = int((a1 - a2) / da);
  173. da = (a1 - a2) / (n + 1);
  174. a1 -= da;
  175. for(i = 0; i < n; i++)
  176. {
  177. add_vertex(vc, x + cos(a1) * m_width, y + sin(a1) * m_width);
  178. a1 -= da;
  179. }
  180. }
  181. add_vertex(vc, x + dx2, y + dy2);
  182. }
  183. //-----------------------------------------------------------------------
  184. template<class VC>
  185. void math_stroke<VC>::calc_miter(VC& vc,
  186. const vertex_dist& v0,
  187. const vertex_dist& v1,
  188. const vertex_dist& v2,
  189. double dx1, double dy1,
  190. double dx2, double dy2,
  191. line_join_e lj,
  192. double mlimit,
  193. double dbevel)
  194. {
  195. double xi = v1.x;
  196. double yi = v1.y;
  197. double di = 1;
  198. double lim = m_width_abs * mlimit;
  199. bool miter_limit_exceeded = true; // Assume the worst
  200. bool intersection_failed = true; // Assume the worst
  201. if(calc_intersection(v0.x + dx1, v0.y - dy1,
  202. v1.x + dx1, v1.y - dy1,
  203. v1.x + dx2, v1.y - dy2,
  204. v2.x + dx2, v2.y - dy2,
  205. &xi, &yi))
  206. {
  207. // Calculation of the intersection succeeded
  208. //---------------------
  209. di = calc_distance(v1.x, v1.y, xi, yi);
  210. if(di <= lim)
  211. {
  212. // Inside the miter limit
  213. //---------------------
  214. add_vertex(vc, xi, yi);
  215. miter_limit_exceeded = false;
  216. }
  217. intersection_failed = false;
  218. }
  219. else
  220. {
  221. // Calculation of the intersection failed, most probably
  222. // the three points lie one straight line.
  223. // First check if v0 and v2 lie on the opposite sides of vector:
  224. // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
  225. // to the line determined by vertices v0 and v1.
  226. // This condition determines whether the next line segments continues
  227. // the previous one or goes back.
  228. //----------------
  229. double x2 = v1.x + dx1;
  230. double y2 = v1.y - dy1;
  231. if((cross_product(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0.0) ==
  232. (cross_product(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0.0))
  233. {
  234. // This case means that the next segment continues
  235. // the previous one (straight line)
  236. //-----------------
  237. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  238. miter_limit_exceeded = false;
  239. }
  240. }
  241. if(miter_limit_exceeded)
  242. {
  243. // Miter limit exceeded
  244. //------------------------
  245. switch(lj)
  246. {
  247. case miter_join_revert:
  248. // For the compatibility with SVG, PDF, etc,
  249. // we use a simple bevel join instead of
  250. // "smart" bevel
  251. //-------------------
  252. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  253. add_vertex(vc, v1.x + dx2, v1.y - dy2);
  254. break;
  255. case miter_join_round:
  256. calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
  257. break;
  258. default:
  259. // If no miter-revert, calculate new dx1, dy1, dx2, dy2
  260. //----------------
  261. if(intersection_failed)
  262. {
  263. mlimit *= m_width_sign;
  264. add_vertex(vc, v1.x + dx1 + dy1 * mlimit,
  265. v1.y - dy1 + dx1 * mlimit);
  266. add_vertex(vc, v1.x + dx2 - dy2 * mlimit,
  267. v1.y - dy2 - dx2 * mlimit);
  268. }
  269. else
  270. {
  271. double x1 = v1.x + dx1;
  272. double y1 = v1.y - dy1;
  273. double x2 = v1.x + dx2;
  274. double y2 = v1.y - dy2;
  275. di = (lim - dbevel) / (di - dbevel);
  276. add_vertex(vc, x1 + (xi - x1) * di,
  277. y1 + (yi - y1) * di);
  278. add_vertex(vc, x2 + (xi - x2) * di,
  279. y2 + (yi - y2) * di);
  280. }
  281. break;
  282. }
  283. }
  284. }
  285. //--------------------------------------------------------stroke_calc_cap
  286. template<class VC>
  287. void math_stroke<VC>::calc_cap(VC& vc,
  288. const vertex_dist& v0,
  289. const vertex_dist& v1,
  290. double len)
  291. {
  292. vc.remove_all();
  293. double dx1 = (v1.y - v0.y) / len;
  294. double dy1 = (v1.x - v0.x) / len;
  295. double dx2 = 0;
  296. double dy2 = 0;
  297. dx1 *= m_width;
  298. dy1 *= m_width;
  299. if(m_line_cap != round_cap)
  300. {
  301. if(m_line_cap == square_cap)
  302. {
  303. dx2 = dy1 * m_width_sign;
  304. dy2 = dx1 * m_width_sign;
  305. }
  306. add_vertex(vc, v0.x - dx1 - dx2, v0.y + dy1 - dy2);
  307. add_vertex(vc, v0.x + dx1 - dx2, v0.y - dy1 - dy2);
  308. }
  309. else
  310. {
  311. double da = acos(m_width_abs / (m_width_abs + 0.125 / m_approx_scale)) * 2;
  312. double a1;
  313. int i;
  314. int n = int(pi / da);
  315. da = pi / (n + 1);
  316. add_vertex(vc, v0.x - dx1, v0.y + dy1);
  317. if(m_width_sign > 0)
  318. {
  319. a1 = atan2(dy1, -dx1);
  320. a1 += da;
  321. for(i = 0; i < n; i++)
  322. {
  323. add_vertex(vc, v0.x + cos(a1) * m_width,
  324. v0.y + sin(a1) * m_width);
  325. a1 += da;
  326. }
  327. }
  328. else
  329. {
  330. a1 = atan2(-dy1, dx1);
  331. a1 -= da;
  332. for(i = 0; i < n; i++)
  333. {
  334. add_vertex(vc, v0.x + cos(a1) * m_width,
  335. v0.y + sin(a1) * m_width);
  336. a1 -= da;
  337. }
  338. }
  339. add_vertex(vc, v0.x + dx1, v0.y - dy1);
  340. }
  341. }
  342. //-----------------------------------------------------------------------
  343. template<class VC>
  344. void math_stroke<VC>::calc_join(VC& vc,
  345. const vertex_dist& v0,
  346. const vertex_dist& v1,
  347. const vertex_dist& v2,
  348. double len1,
  349. double len2)
  350. {
  351. double dx1 = m_width * (v1.y - v0.y) / len1;
  352. double dy1 = m_width * (v1.x - v0.x) / len1;
  353. double dx2 = m_width * (v2.y - v1.y) / len2;
  354. double dy2 = m_width * (v2.x - v1.x) / len2;
  355. vc.remove_all();
  356. double cp = cross_product(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
  357. if ((cp > agg::vertex_dist_epsilon && m_width > 0) ||
  358. (cp < -agg::vertex_dist_epsilon && m_width < 0))
  359. {
  360. // Inner join
  361. //---------------
  362. double limit = ((len1 < len2) ? len1 : len2) / m_width_abs;
  363. if(limit < m_inner_miter_limit)
  364. {
  365. limit = m_inner_miter_limit;
  366. }
  367. switch(m_inner_join)
  368. {
  369. default: // inner_bevel
  370. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  371. add_vertex(vc, v1.x + dx2, v1.y - dy2);
  372. break;
  373. case inner_miter:
  374. calc_miter(vc,
  375. v0, v1, v2, dx1, dy1, dx2, dy2,
  376. miter_join_revert,
  377. limit, 0);
  378. break;
  379. case inner_jag:
  380. case inner_round:
  381. cp = (dx1-dx2) * (dx1-dx2) + (dy1-dy2) * (dy1-dy2);
  382. if(cp < len1 * len1 && cp < len2 * len2)
  383. {
  384. calc_miter(vc,
  385. v0, v1, v2, dx1, dy1, dx2, dy2,
  386. miter_join_revert,
  387. limit, 0);
  388. }
  389. else
  390. {
  391. if(m_inner_join == inner_jag)
  392. {
  393. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  394. add_vertex(vc, v1.x, v1.y );
  395. add_vertex(vc, v1.x + dx2, v1.y - dy2);
  396. }
  397. else
  398. {
  399. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  400. add_vertex(vc, v1.x, v1.y );
  401. calc_arc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
  402. add_vertex(vc, v1.x, v1.y );
  403. add_vertex(vc, v1.x + dx2, v1.y - dy2);
  404. }
  405. }
  406. break;
  407. }
  408. }
  409. else
  410. {
  411. // Outer join
  412. //---------------
  413. // Calculate the distance between v1 and
  414. // the central point of the bevel line segment
  415. //---------------
  416. double dx = (dx1 + dx2) / 2;
  417. double dy = (dy1 + dy2) / 2;
  418. double dbevel = sqrt(dx * dx + dy * dy);
  419. if(m_line_join == round_join || m_line_join == bevel_join)
  420. {
  421. // This is an optimization that reduces the number of points
  422. // in cases of almost collinear segments. If there's no
  423. // visible difference between bevel and miter joins we'd rather
  424. // use miter join because it adds only one point instead of two.
  425. //
  426. // Here we calculate the middle point between the bevel points
  427. // and then, the distance between v1 and this middle point.
  428. // At outer joins this distance always less than stroke width,
  429. // because it's actually the height of an isosceles triangle of
  430. // v1 and its two bevel points. If the difference between this
  431. // width and this value is small (no visible bevel) we can
  432. // add just one point.
  433. //
  434. // The constant in the expression makes the result approximately
  435. // the same as in round joins and caps. You can safely comment
  436. // out this entire "if".
  437. //-------------------
  438. if(m_approx_scale * (m_width_abs - dbevel) < m_width_eps)
  439. {
  440. if(calc_intersection(v0.x + dx1, v0.y - dy1,
  441. v1.x + dx1, v1.y - dy1,
  442. v1.x + dx2, v1.y - dy2,
  443. v2.x + dx2, v2.y - dy2,
  444. &dx, &dy))
  445. {
  446. add_vertex(vc, dx, dy);
  447. }
  448. else
  449. {
  450. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  451. }
  452. return;
  453. }
  454. }
  455. switch(m_line_join)
  456. {
  457. case miter_join:
  458. case miter_join_revert:
  459. case miter_join_round:
  460. calc_miter(vc,
  461. v0, v1, v2, dx1, dy1, dx2, dy2,
  462. m_line_join,
  463. m_miter_limit,
  464. dbevel);
  465. break;
  466. case round_join:
  467. calc_arc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
  468. break;
  469. default: // Bevel join
  470. add_vertex(vc, v1.x + dx1, v1.y - dy1);
  471. add_vertex(vc, v1.x + dx2, v1.y - dy2);
  472. break;
  473. }
  474. }
  475. }
  476. }
  477. #endif