You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
13 KiB

  1. /************************************/
  2. /* routines to handle bezier curves */
  3. /************************************/
  4. #include <fctsys.h>
  5. #include <bezier_curves.h>
  6. #define add_segment(segment) if(s_bezier_Points_Buffer[s_bezier_Points_Buffer.size()-1] != segment) s_bezier_Points_Buffer.push_back(segment);
  7. // Local variables:
  8. static std::vector<wxPoint> s_bezier_Points_Buffer;
  9. static int bezier_recursion_limit = 12;
  10. static double bezier_approximation_scale = 0.5; // 1
  11. static double bezier_curve_collinearity_epsilon = 1e-30;
  12. static double bezier_curve_angle_tolerance_epsilon = 0.0001;
  13. static double bezier_distance_tolerance_square; // derived by approximation_scale
  14. static double bezier_angle_tolerance = 0.0;
  15. static double bezier_cusp_limit = 0.0;
  16. // Local functions:
  17. static void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level );
  18. static void recursive_bezier( int x1,
  19. int y1,
  20. int x2,
  21. int y2,
  22. int x3,
  23. int y3,
  24. int x4,
  25. int y4,
  26. int level );
  27. /***********************************************************************************/
  28. std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3, wxPoint c4 )
  29. {
  30. return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4.x, c4.y );
  31. }
  32. std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3 )
  33. {
  34. return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y );
  35. }
  36. inline double calc_sq_distance( int x1, int y1, int x2, int y2 )
  37. {
  38. int dx = x2 - x1;
  39. int dy = y2 - y1;
  40. return (double)dx * dx + (double)dy * dy;
  41. }
  42. inline double sqrt_len( int dx, int dy )
  43. {
  44. return ((double)dx * dx) + ((double)dy * dy);
  45. }
  46. std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3 )
  47. {
  48. s_bezier_Points_Buffer.clear();
  49. bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale;
  50. bezier_distance_tolerance_square *= bezier_distance_tolerance_square;
  51. s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) );
  52. recursive_bezier( x1, y1, x2, y2, x3, y3, 0 );
  53. s_bezier_Points_Buffer.push_back( wxPoint( x3, y3 ) );
  54. wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() );
  55. return s_bezier_Points_Buffer;
  56. }
  57. std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
  58. {
  59. s_bezier_Points_Buffer.clear();
  60. bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale;
  61. bezier_distance_tolerance_square *= bezier_distance_tolerance_square;
  62. s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) );
  63. recursive_bezier( x1, y1, x2, y2, x3, y3, x4, y4, 0 );
  64. s_bezier_Points_Buffer.push_back( wxPoint( x4, y4 ) );
  65. wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() );
  66. return s_bezier_Points_Buffer;
  67. }
  68. void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level )
  69. {
  70. if( abs( level ) > bezier_recursion_limit )
  71. {
  72. return;
  73. }
  74. // Calculate all the mid-points of the line segments
  75. //----------------------
  76. int x12 = (x1 + x2) / 2;
  77. int y12 = (y1 + y2) / 2;
  78. int x23 = (x2 + x3) / 2;
  79. int y23 = (y2 + y3) / 2;
  80. int x123 = (x12 + x23) / 2;
  81. int y123 = (y12 + y23) / 2;
  82. int dx = x3 - x1;
  83. int dy = y3 - y1;
  84. double d = fabs( ((double) (x2 - x3) * dy) - ((double) (y2 - y3) * dx ) );
  85. double da;
  86. if( d > bezier_curve_collinearity_epsilon )
  87. {
  88. // Regular case
  89. //-----------------
  90. if( d * d <= bezier_distance_tolerance_square * (dx * dx + dy * dy) )
  91. {
  92. // If the curvature doesn't exceed the distance_tolerance value
  93. // we tend to finish subdivisions.
  94. //----------------------
  95. if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
  96. {
  97. add_segment( wxPoint( x123, y123 ) );
  98. return;
  99. }
  100. // Angle & Cusp Condition
  101. //----------------------
  102. da = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) -
  103. atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) );
  104. if( da >=M_PI )
  105. da = 2 * M_PI - da;
  106. if( da < bezier_angle_tolerance )
  107. {
  108. // Finally we can stop the recursion
  109. //----------------------
  110. add_segment( wxPoint( x123, y123 ) );
  111. return;
  112. }
  113. }
  114. }
  115. else
  116. {
  117. // Collinear case
  118. //------------------
  119. da = sqrt_len(dx, dy);
  120. if( da == 0 )
  121. {
  122. d = calc_sq_distance( x1, y1, x2, y2 );
  123. }
  124. else
  125. {
  126. d = ( (double)(x2 - x1) * dx + (double)(y2 - y1) * dy ) / da;
  127. if( d > 0 && d < 1 )
  128. {
  129. // Simple collinear case, 1---2---3
  130. // We can leave just two endpoints
  131. return;
  132. }
  133. if( d <= 0 )
  134. d = calc_sq_distance( x2, y2, x1, y1 );
  135. else if( d >= 1 )
  136. d = calc_sq_distance( x2, y2, x3, y3 );
  137. else
  138. d = calc_sq_distance( x2, y2, x1 + (int) d * dx,
  139. y1 + (int) d * dy );
  140. }
  141. if( d < bezier_distance_tolerance_square )
  142. {
  143. add_segment( wxPoint( x2, y2 ) );
  144. return;
  145. }
  146. }
  147. // Continue subdivision
  148. //----------------------
  149. recursive_bezier( x1, y1, x12, y12, x123, y123, level + 1 );
  150. recursive_bezier( x123, y123, x23, y23, x3, y3, -(level + 1) );
  151. }
  152. void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int level )
  153. {
  154. if( abs( level ) > bezier_recursion_limit )
  155. {
  156. return;
  157. }
  158. // Calculate all the mid-points of the line segments
  159. //----------------------
  160. int x12 = (x1 + x2) / 2;
  161. int y12 = (y1 + y2) / 2;
  162. int x23 = (x2 + x3) / 2;
  163. int y23 = (y2 + y3) / 2;
  164. int x34 = (x3 + x4) / 2;
  165. int y34 = (y3 + y4) / 2;
  166. int x123 = (x12 + x23) / 2;
  167. int y123 = (y12 + y23) / 2;
  168. int x234 = (x23 + x34) / 2;
  169. int y234 = (y23 + y34) / 2;
  170. int x1234 = (x123 + x234) / 2;
  171. int y1234 = (y123 + y234) / 2;
  172. // Try to approximate the full cubic curve by a single straight line
  173. //------------------
  174. int dx = x4 - x1;
  175. int dy = y4 - y1;
  176. double d2 = fabs( (double) ( (x2 - x4) * dy - (y2 - y4) * dx ) );
  177. double d3 = fabs( (double) ( (x3 - x4) * dy - (y3 - y4) * dx ) );
  178. double da1, da2, k;
  179. switch( (int(d2 > bezier_curve_collinearity_epsilon) << 1) +
  180. int(d3 > bezier_curve_collinearity_epsilon) )
  181. {
  182. case 0:
  183. // All collinear OR p1==p4
  184. //----------------------
  185. k = dx * dx + dy * dy;
  186. if( k == 0 )
  187. {
  188. d2 = calc_sq_distance( x1, y1, x2, y2 );
  189. d3 = calc_sq_distance( x4, y4, x3, y3 );
  190. }
  191. else
  192. {
  193. k = 1 / k;
  194. da1 = x2 - x1;
  195. da2 = y2 - y1;
  196. d2 = k * (da1 * dx + da2 * dy);
  197. da1 = x3 - x1;
  198. da2 = y3 - y1;
  199. d3 = k * (da1 * dx + da2 * dy);
  200. if( d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 )
  201. {
  202. // Simple collinear case, 1---2---3---4
  203. // We can leave just two endpoints
  204. return;
  205. }
  206. if( d2 <= 0 )
  207. d2 = calc_sq_distance( x2, y2, x1, y1 );
  208. else if( d2 >= 1 )
  209. d2 = calc_sq_distance( x2, y2, x4, y4 );
  210. else
  211. d2 = calc_sq_distance( x2, y2, x1 + (int) d2 * dx,
  212. y1 + (int) d2 * dy );
  213. if( d3 <= 0 )
  214. d3 = calc_sq_distance( x3, y3, x1, y1 );
  215. else if( d3 >= 1 )
  216. d3 = calc_sq_distance( x3, y3, x4, y4 );
  217. else
  218. d3 = calc_sq_distance( x3, y3, x1 + (int) d3 * dx,
  219. y1 + (int) d3 * dy );
  220. }
  221. if( d2 > d3 )
  222. {
  223. if( d2 < bezier_distance_tolerance_square )
  224. {
  225. add_segment( wxPoint( x2, y2 ) );
  226. return;
  227. }
  228. }
  229. else
  230. {
  231. if( d3 < bezier_distance_tolerance_square )
  232. {
  233. add_segment( wxPoint( x3, y3 ) );
  234. return;
  235. }
  236. }
  237. break;
  238. case 1:
  239. // p1,p2,p4 are collinear, p3 is significant
  240. //----------------------
  241. if( d3 * d3 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
  242. {
  243. if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
  244. {
  245. add_segment( wxPoint( x23, y23 ) );
  246. return;
  247. }
  248. // Angle Condition
  249. //----------------------
  250. da1 = fabs( atan2( (double) ( y4 - y3 ), (double) ( x4 - x3 ) ) -
  251. atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) );
  252. if( da1 >= M_PI )
  253. da1 = 2 * M_PI - da1;
  254. if( da1 < bezier_angle_tolerance )
  255. {
  256. add_segment( wxPoint( x2, y2 ) );
  257. add_segment( wxPoint( x3, y3 ) );
  258. return;
  259. }
  260. if( bezier_cusp_limit != 0.0 )
  261. {
  262. if( da1 > bezier_cusp_limit )
  263. {
  264. add_segment( wxPoint( x3, y3 ) );
  265. return;
  266. }
  267. }
  268. }
  269. break;
  270. case 2:
  271. // p1,p3,p4 are collinear, p2 is significant
  272. //----------------------
  273. if( d2 * d2 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
  274. {
  275. if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
  276. {
  277. add_segment( wxPoint( x23, y23 ) );
  278. return;
  279. }
  280. // Angle Condition
  281. //----------------------
  282. da1 = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) -
  283. atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) );
  284. if( da1 >= M_PI )
  285. da1 = 2 * M_PI - da1;
  286. if( da1 < bezier_angle_tolerance )
  287. {
  288. add_segment( wxPoint( x2, y2 ) );
  289. add_segment( wxPoint( x3, y3 ) );
  290. return;
  291. }
  292. if( bezier_cusp_limit != 0.0 )
  293. {
  294. if( da1 > bezier_cusp_limit )
  295. {
  296. add_segment( wxPoint( x2, y2 ) );
  297. return;
  298. }
  299. }
  300. }
  301. break;
  302. case 3:
  303. // Regular case
  304. //-----------------
  305. if( (d2 + d3) * (d2 + d3) <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
  306. {
  307. // If the curvature doesn't exceed the distance_tolerance value
  308. // we tend to finish subdivisions.
  309. //----------------------
  310. if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
  311. {
  312. add_segment( wxPoint( x23, y23 ) );
  313. return;
  314. }
  315. // Angle & Cusp Condition
  316. //----------------------
  317. k = atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) );
  318. da1 = fabs( k - atan2( (double) ( y2 - y1 ),
  319. (double) ( x2 - x1 ) ) );
  320. da2 = fabs( atan2( (double) ( y4 - y3 ),
  321. (double) ( x4 - x3 ) ) - k );
  322. if( da1 >= M_PI )
  323. da1 = 2 * M_PI - da1;
  324. if( da2 >= M_PI )
  325. da2 = 2 * M_PI - da2;
  326. if( da1 + da2 < bezier_angle_tolerance )
  327. {
  328. // Finally we can stop the recursion
  329. //----------------------
  330. add_segment( wxPoint( x23, y23 ) );
  331. return;
  332. }
  333. if( bezier_cusp_limit != 0.0 )
  334. {
  335. if( da1 > bezier_cusp_limit )
  336. {
  337. add_segment( wxPoint( x2, y2 ) );
  338. return;
  339. }
  340. if( da2 > bezier_cusp_limit )
  341. {
  342. add_segment( wxPoint( x3, y3 ) );
  343. return;
  344. }
  345. }
  346. }
  347. break;
  348. }
  349. // Continue subdivision
  350. //----------------------
  351. recursive_bezier( x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1 );
  352. recursive_bezier( x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1 );
  353. }