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.

387 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2014-2017 KiCad Developers, see CHANGELOG.TXT for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /************************************/
  25. /* routines to handle bezier curves */
  26. /************************************/
  27. #include <fctsys.h>
  28. #include <bezier_curves.h>
  29. static inline double calc_sq_distance( int x1, int y1, int x2, int y2 )
  30. {
  31. int dx = x2 - x1;
  32. int dy = y2 - y1;
  33. return (double)dx * dx + (double)dy * dy;
  34. }
  35. static inline double sqrt_len( int dx, int dy )
  36. {
  37. return ((double)dx * dx) + ((double)dy * dy);
  38. }
  39. void BEZIER_POLY::GetPoly( std::vector<wxPoint>& aOutput, int aMinSegLen )
  40. {
  41. wxCHECK( !m_ctrlPts.empty(), /* void */ );
  42. m_minSegLen = std::max( 1, aMinSegLen );
  43. m_output = &aOutput;
  44. m_output->clear();
  45. m_output->push_back( wxPoint( m_ctrlPts.front() ) );
  46. // Only quadratic and cubic Bezier curves are handled
  47. if( m_ctrlPts.size() == 3 )
  48. recursiveBezier( m_ctrlPts[0].x, m_ctrlPts[0].y,
  49. m_ctrlPts[1].x, m_ctrlPts[1].y,
  50. m_ctrlPts[2].x, m_ctrlPts[2].y, 0 );
  51. else if( m_ctrlPts.size() == 4 )
  52. recursiveBezier( m_ctrlPts[0].x, m_ctrlPts[0].y,
  53. m_ctrlPts[1].x, m_ctrlPts[1].y,
  54. m_ctrlPts[2].x, m_ctrlPts[2].y,
  55. m_ctrlPts[3].x, m_ctrlPts[3].y, 0 );
  56. m_output->push_back( wxPoint( m_ctrlPts.back() ) );
  57. }
  58. void BEZIER_POLY::recursiveBezier( int x1, int y1, int x2, int y2,
  59. int x3, int y3, unsigned int level )
  60. {
  61. if( level > recursion_limit )
  62. return;
  63. // Calculate all the mid-points of the line segments
  64. //----------------------
  65. int x12 = (x1 + x2) / 2;
  66. int y12 = (y1 + y2) / 2;
  67. int x23 = (x2 + x3) / 2;
  68. int y23 = (y2 + y3) / 2;
  69. int x123 = (x12 + x23) / 2;
  70. int y123 = (y12 + y23) / 2;
  71. int dx = x3 - x1;
  72. int dy = y3 - y1;
  73. double d = fabs( ((double) (x2 - x3) * dy) - ((double) (y2 - y3) * dx ) );
  74. double da;
  75. if( d > curve_collinearity_epsilon )
  76. {
  77. // Regular case
  78. //-----------------
  79. if( d * d <= distance_tolerance_square * (dx * dx + dy * dy) )
  80. {
  81. // If the curvature doesn't exceed the distance_tolerance value
  82. // we tend to finish subdivisions.
  83. //----------------------
  84. if( angle_tolerance < curve_angle_tolerance_epsilon )
  85. {
  86. addSegment( wxPoint( x123, y123 ) );
  87. return;
  88. }
  89. // Angle & Cusp Condition
  90. //----------------------
  91. da = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) -
  92. atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) );
  93. if( da >=M_PI )
  94. da = 2 * M_PI - da;
  95. if( da < angle_tolerance )
  96. {
  97. // Finally we can stop the recursion
  98. //----------------------
  99. addSegment( wxPoint( x123, y123 ) );
  100. return;
  101. }
  102. }
  103. }
  104. else
  105. {
  106. // Collinear case
  107. //------------------
  108. da = sqrt_len(dx, dy);
  109. if( da == 0 )
  110. {
  111. d = calc_sq_distance( x1, y1, x2, y2 );
  112. }
  113. else
  114. {
  115. d = ( (double)(x2 - x1) * dx + (double)(y2 - y1) * dy ) / da;
  116. if( d > 0 && d < 1 )
  117. {
  118. // Simple collinear case, 1---2---3
  119. // We can leave just two endpoints
  120. return;
  121. }
  122. if( d <= 0 )
  123. d = calc_sq_distance( x2, y2, x1, y1 );
  124. else if( d >= 1 )
  125. d = calc_sq_distance( x2, y2, x3, y3 );
  126. else
  127. d = calc_sq_distance( x2, y2, x1 + (int) d * dx,
  128. y1 + (int) d * dy );
  129. }
  130. if( d < distance_tolerance_square )
  131. {
  132. addSegment( wxPoint( x2, y2 ) );
  133. return;
  134. }
  135. }
  136. // Continue subdivision
  137. //----------------------
  138. recursiveBezier( x1, y1, x12, y12, x123, y123, level + 1 );
  139. recursiveBezier( x123, y123, x23, y23, x3, y3, level + 1 );
  140. }
  141. void BEZIER_POLY::recursiveBezier( int x1, int y1, int x2, int y2,
  142. int x3, int y3, int x4, int y4, unsigned int level )
  143. {
  144. if( level > recursion_limit )
  145. return;
  146. // Calculate all the mid-points of the line segments
  147. //----------------------
  148. int x12 = (x1 + x2) / 2;
  149. int y12 = (y1 + y2) / 2;
  150. int x23 = (x2 + x3) / 2;
  151. int y23 = (y2 + y3) / 2;
  152. int x34 = (x3 + x4) / 2;
  153. int y34 = (y3 + y4) / 2;
  154. int x123 = (x12 + x23) / 2;
  155. int y123 = (y12 + y23) / 2;
  156. int x234 = (x23 + x34) / 2;
  157. int y234 = (y23 + y34) / 2;
  158. int x1234 = (x123 + x234) / 2;
  159. int y1234 = (y123 + y234) / 2;
  160. // Try to approximate the full cubic curve by a single straight line
  161. //------------------
  162. int dx = x4 - x1;
  163. int dy = y4 - y1;
  164. double d2 = fabs( (double) ( (x2 - x4) * dy - (y2 - y4) * dx ) );
  165. double d3 = fabs( (double) ( (x3 - x4) * dy - (y3 - y4) * dx ) );
  166. double da1, da2, k;
  167. switch( (int(d2 > curve_collinearity_epsilon) << 1) +
  168. int(d3 > curve_collinearity_epsilon) )
  169. {
  170. case 0:
  171. // All collinear OR p1==p4
  172. //----------------------
  173. k = dx * dx + dy * dy;
  174. if( k == 0 )
  175. {
  176. d2 = calc_sq_distance( x1, y1, x2, y2 );
  177. d3 = calc_sq_distance( x4, y4, x3, y3 );
  178. }
  179. else
  180. {
  181. k = 1 / k;
  182. da1 = x2 - x1;
  183. da2 = y2 - y1;
  184. d2 = k * (da1 * dx + da2 * dy);
  185. da1 = x3 - x1;
  186. da2 = y3 - y1;
  187. d3 = k * (da1 * dx + da2 * dy);
  188. if( d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1 )
  189. {
  190. // Simple collinear case, 1---2---3---4
  191. // We can leave just two endpoints
  192. return;
  193. }
  194. if( d2 <= 0 )
  195. d2 = calc_sq_distance( x2, y2, x1, y1 );
  196. else if( d2 >= 1 )
  197. d2 = calc_sq_distance( x2, y2, x4, y4 );
  198. else
  199. d2 = calc_sq_distance( x2, y2, x1 + (int) d2 * dx,
  200. y1 + (int) d2 * dy );
  201. if( d3 <= 0 )
  202. d3 = calc_sq_distance( x3, y3, x1, y1 );
  203. else if( d3 >= 1 )
  204. d3 = calc_sq_distance( x3, y3, x4, y4 );
  205. else
  206. d3 = calc_sq_distance( x3, y3, x1 + (int) d3 * dx,
  207. y1 + (int) d3 * dy );
  208. }
  209. if( d2 > d3 )
  210. {
  211. if( d2 < distance_tolerance_square )
  212. {
  213. addSegment( wxPoint( x2, y2 ) );
  214. return;
  215. }
  216. }
  217. else
  218. {
  219. if( d3 < distance_tolerance_square )
  220. {
  221. addSegment( wxPoint( x3, y3 ) );
  222. return;
  223. }
  224. }
  225. break;
  226. case 1:
  227. // p1,p2,p4 are collinear, p3 is significant
  228. //----------------------
  229. if( d3 * d3 <= distance_tolerance_square * sqrt_len(dx, dy) )
  230. {
  231. if( angle_tolerance < curve_angle_tolerance_epsilon )
  232. {
  233. addSegment( wxPoint( x23, y23 ) );
  234. return;
  235. }
  236. // Angle Condition
  237. //----------------------
  238. da1 = fabs( atan2( (double) ( y4 - y3 ), (double) ( x4 - x3 ) ) -
  239. atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) );
  240. if( da1 >= M_PI )
  241. da1 = 2 * M_PI - da1;
  242. if( da1 < angle_tolerance )
  243. {
  244. addSegment( wxPoint( x2, y2 ) );
  245. addSegment( wxPoint( x3, y3 ) );
  246. return;
  247. }
  248. if( cusp_limit != 0.0 )
  249. {
  250. if( da1 > cusp_limit )
  251. {
  252. addSegment( wxPoint( x3, y3 ) );
  253. return;
  254. }
  255. }
  256. }
  257. break;
  258. case 2:
  259. // p1,p3,p4 are collinear, p2 is significant
  260. //----------------------
  261. if( d2 * d2 <= distance_tolerance_square * sqrt_len(dx, dy) )
  262. {
  263. if( angle_tolerance < curve_angle_tolerance_epsilon )
  264. {
  265. addSegment( wxPoint( x23, y23 ) );
  266. return;
  267. }
  268. // Angle Condition
  269. //----------------------
  270. da1 = fabs( atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) ) -
  271. atan2( (double) ( y2 - y1 ), (double) ( x2 - x1 ) ) );
  272. if( da1 >= M_PI )
  273. da1 = 2 * M_PI - da1;
  274. if( da1 < angle_tolerance )
  275. {
  276. addSegment( wxPoint( x2, y2 ) );
  277. addSegment( wxPoint( x3, y3 ) );
  278. return;
  279. }
  280. if( cusp_limit != 0.0 )
  281. {
  282. if( da1 > cusp_limit )
  283. {
  284. addSegment( wxPoint( x2, y2 ) );
  285. return;
  286. }
  287. }
  288. }
  289. break;
  290. case 3:
  291. // Regular case
  292. //-----------------
  293. if( (d2 + d3) * (d2 + d3) <= distance_tolerance_square * sqrt_len(dx, dy) )
  294. {
  295. // If the curvature doesn't exceed the distance_tolerance value
  296. // we tend to finish subdivisions.
  297. //----------------------
  298. if( angle_tolerance < curve_angle_tolerance_epsilon )
  299. {
  300. addSegment( wxPoint( x23, y23 ) );
  301. return;
  302. }
  303. // Angle & Cusp Condition
  304. //----------------------
  305. k = atan2( (double) ( y3 - y2 ), (double) ( x3 - x2 ) );
  306. da1 = fabs( k - atan2( (double) ( y2 - y1 ),
  307. (double) ( x2 - x1 ) ) );
  308. da2 = fabs( atan2( (double) ( y4 - y3 ),
  309. (double) ( x4 - x3 ) ) - k );
  310. if( da1 >= M_PI )
  311. da1 = 2 * M_PI - da1;
  312. if( da2 >= M_PI )
  313. da2 = 2 * M_PI - da2;
  314. if( da1 + da2 < angle_tolerance )
  315. {
  316. // Finally we can stop the recursion
  317. //----------------------
  318. addSegment( wxPoint( x23, y23 ) );
  319. return;
  320. }
  321. if( cusp_limit != 0.0 )
  322. {
  323. if( da1 > cusp_limit )
  324. {
  325. addSegment( wxPoint( x2, y2 ) );
  326. return;
  327. }
  328. if( da2 > cusp_limit )
  329. {
  330. addSegment( wxPoint( x3, y3 ) );
  331. return;
  332. }
  333. }
  334. }
  335. break;
  336. }
  337. // Continue subdivision
  338. //----------------------
  339. recursiveBezier( x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1 );
  340. recursiveBezier( x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1 );
  341. }