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.

519 lines
14 KiB

  1. // math for graphics utility routines and RC, from FreePCB
  2. #include <vector>
  3. #include <cmath>
  4. #include <float.h>
  5. #include <limits.h>
  6. #include <common.h>
  7. #include <cstdlib> // for abs function on ints
  8. #include <algorithm>
  9. #include <math_for_graphics.h>
  10. static bool InRange( double x, double xi, double xf );
  11. /* Function FindSegmentIntersections
  12. * find intersections between line segment (xi,yi) to (xf,yf)
  13. * and line segment (xi2,yi2) to (xf2,yf2)
  14. * returns true if intersection found
  15. */
  16. bool FindSegmentIntersections( int xi, int yi, int xf, int yf,
  17. int xi2, int yi2, int xf2, int yf2 )
  18. {
  19. if( std::max( xi, xf ) < std::min( xi2, xf2 )
  20. || std::min( xi, xf ) > std::max( xi2, xf2 )
  21. || std::max( yi, yf ) < std::min( yi2, yf2 )
  22. || std::min( yi, yf ) > std::max( yi2, yf2 ) )
  23. return false;
  24. return TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf,
  25. xi2, yi2, xf2, yf2 );
  26. }
  27. /* Function FindLineSegmentIntersection
  28. * find intersection between line y = a + bx and line segment (xi,yi) to (xf,yf)
  29. * if b > DBL_MAX/10, assume vertical line at x = a
  30. * return false if no intersection or true if intersect
  31. * return coords of intersections in *x1, *y1, *x2, *y2
  32. * if no intersection, returns min distance in dist
  33. */
  34. bool FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf,
  35. double* x1, double* y1, double* x2, double* y2,
  36. double* dist )
  37. {
  38. double xx = 0, yy = 0; // Init made to avoid C compil "uninitialized" warning
  39. bool bVert = false;
  40. if( b > DBL_MAX / 10.0 )
  41. bVert = true;
  42. if( xf != xi ) // non-vertical segment, get intersection
  43. {
  44. // horizontal or oblique straight segment
  45. // put into form y = c + dx;
  46. double d = (double) (yf - yi) / (double) (xf - xi);
  47. double c = yf - d * xf;
  48. if( bVert )
  49. {
  50. // if vertical line, easy
  51. if( InRange( a, xi, xf ) )
  52. {
  53. *x1 = a;
  54. *y1 = c + d * a;
  55. return 1;
  56. }
  57. else
  58. {
  59. if( dist )
  60. *dist = std::min( std::abs( a - xi ), std::abs( a - xf ) );
  61. return false;
  62. }
  63. }
  64. if( std::abs( b - d ) < 1E-12 )
  65. {
  66. // parallel lines
  67. if( dist )
  68. {
  69. *dist = GetPointToLineDistance( a, b, xi, xf );
  70. }
  71. return false; // lines parallel
  72. }
  73. // calculate intersection
  74. xx = (c - a) / (b - d);
  75. yy = a + b * (xx);
  76. // see if intersection is within the line segment
  77. if( yf == yi )
  78. {
  79. // horizontal line
  80. if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf) )
  81. return false;
  82. }
  83. else
  84. {
  85. // oblique line
  86. if( (xx>=xi && xx>xf) || (xx<=xi && xx<xf)
  87. || (yy>yi && yy>yf) || (yy<yi && yy<yf) )
  88. return false;
  89. }
  90. }
  91. else
  92. {
  93. // vertical line segment
  94. if( bVert )
  95. return false;
  96. xx = xi;
  97. yy = a + b * xx;
  98. if( (yy>=yi && yy>yf) || (yy<=yi && yy<yf) )
  99. return 0;
  100. }
  101. *x1 = xx;
  102. *y1 = yy;
  103. return true;
  104. }
  105. /*
  106. * Function TestForIntersectionOfStraightLineSegments
  107. * Test for intersection of line segments
  108. * If lines are parallel, returns false
  109. * If true, returns also intersection coords in x, y
  110. * if false, returns min. distance in dist (may be 0.0 if parallel)
  111. */
  112. bool TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f,
  113. int x2i, int y2i, int x2f, int y2f,
  114. int* x, int* y, double* d )
  115. {
  116. double a, b, dist;
  117. // first, test for intersection
  118. if( x1i == x1f && x2i == x2f )
  119. {
  120. // both segments are vertical, can't intersect
  121. }
  122. else if( y1i == y1f && y2i == y2f )
  123. {
  124. // both segments are horizontal, can't intersect
  125. }
  126. else if( x1i == x1f && y2i == y2f )
  127. {
  128. // first seg. vertical, second horizontal, see if they cross
  129. if( InRange( x1i, x2i, x2f )
  130. && InRange( y2i, y1i, y1f ) )
  131. {
  132. if( x )
  133. *x = x1i;
  134. if( y )
  135. *y = y2i;
  136. if( d )
  137. *d = 0.0;
  138. return true;
  139. }
  140. }
  141. else if( y1i == y1f && x2i == x2f )
  142. {
  143. // first seg. horizontal, second vertical, see if they cross
  144. if( InRange( y1i, y2i, y2f )
  145. && InRange( x2i, x1i, x1f ) )
  146. {
  147. if( x )
  148. *x = x2i;
  149. if( y )
  150. *y = y1i;
  151. if( d )
  152. *d = 0.0;
  153. return true;
  154. }
  155. }
  156. else if( x1i == x1f )
  157. {
  158. // first segment vertical, second oblique
  159. // get a and b for second line segment, so that y = a + bx;
  160. b = double( y2f - y2i ) / (x2f - x2i);
  161. a = (double) y2i - b * x2i;
  162. double x1, y1, x2, y2;
  163. int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
  164. &x1, &y1, &x2, &y2 );
  165. if( test )
  166. {
  167. if( InRange( y1, y1i, y1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
  168. {
  169. if( x )
  170. *x = KiROUND( x1 );
  171. if( y )
  172. *y = KiROUND( y1 );
  173. if( d )
  174. *d = 0.0;
  175. return true;
  176. }
  177. }
  178. }
  179. else if( y1i == y1f )
  180. {
  181. // first segment horizontal, second oblique
  182. // get a and b for second line segment, so that y = a + bx;
  183. b = double( y2f - y2i ) / (x2f - x2i);
  184. a = (double) y2i - b * x2i;
  185. double x1, y1, x2, y2;
  186. int test = FindLineSegmentIntersection( a, b, x1i, y1i, x1f, y1f,
  187. &x1, &y1, &x2, &y2 );
  188. if( test )
  189. {
  190. if( InRange( x1, x1i, x1f ) && InRange( x1, x2i, x2f ) && InRange( y1, y2i, y2f ) )
  191. {
  192. if( x )
  193. *x = KiROUND( x1 );
  194. if( y )
  195. *y = KiROUND( y1 );
  196. if( d )
  197. *d = 0.0;
  198. return true;
  199. }
  200. }
  201. }
  202. else if( x2i == x2f )
  203. {
  204. // second segment vertical, first oblique
  205. // get a and b for first line segment, so that y = a + bx;
  206. b = double( y1f - y1i ) / (x1f - x1i);
  207. a = (double) y1i - b * x1i;
  208. double x1, y1, x2, y2;
  209. int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
  210. &x1, &y1, &x2, &y2 );
  211. if( test )
  212. {
  213. if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) && InRange( y1, y2i, y2f ) )
  214. {
  215. if( x )
  216. *x = KiROUND( x1 );
  217. if( y )
  218. *y = KiROUND( y1 );
  219. if( d )
  220. *d = 0.0;
  221. return true;
  222. }
  223. }
  224. }
  225. else if( y2i == y2f )
  226. {
  227. // second segment horizontal, first oblique
  228. // get a and b for second line segment, so that y = a + bx;
  229. b = double( y1f - y1i ) / (x1f - x1i);
  230. a = (double) y1i - b * x1i;
  231. double x1, y1, x2, y2;
  232. int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
  233. &x1, &y1, &x2, &y2 );
  234. if( test )
  235. {
  236. if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
  237. {
  238. if( x )
  239. *x = KiROUND( x1 );
  240. if( y )
  241. *y = KiROUND( y1 );
  242. if( d )
  243. *d = 0.0;
  244. return true;
  245. }
  246. }
  247. }
  248. else
  249. {
  250. // both segments oblique
  251. if( long( y1f - y1i ) * (x2f - x2i) != long( y2f - y2i ) * (x1f - x1i) )
  252. {
  253. // not parallel, get a and b for first line segment, so that y = a + bx;
  254. b = double( y1f - y1i ) / (x1f - x1i);
  255. a = (double) y1i - b * x1i;
  256. double x1, y1, x2, y2;
  257. int test = FindLineSegmentIntersection( a, b, x2i, y2i, x2f, y2f,
  258. &x1, &y1, &x2, &y2 );
  259. // both segments oblique
  260. if( test )
  261. {
  262. if( InRange( x1, x1i, x1f ) && InRange( y1, y1i, y1f ) )
  263. {
  264. if( x )
  265. *x = KiROUND( x1 );
  266. if( y )
  267. *y = KiROUND( y1 );
  268. if( d )
  269. *d = 0.0;
  270. return true;
  271. }
  272. }
  273. }
  274. }
  275. // don't intersect, get shortest distance between each endpoint and the other line segment
  276. dist = GetPointToLineSegmentDistance( x1i, y1i, x2i, y2i, x2f, y2f );
  277. double xx = x1i;
  278. double yy = y1i;
  279. double dd = GetPointToLineSegmentDistance( x1f, y1f, x2i, y2i, x2f, y2f );
  280. if( dd < dist )
  281. {
  282. dist = dd;
  283. xx = x1f;
  284. yy = y1f;
  285. }
  286. dd = GetPointToLineSegmentDistance( x2i, y2i, x1i, y1i, x1f, y1f );
  287. if( dd < dist )
  288. {
  289. dist = dd;
  290. xx = x2i;
  291. yy = y2i;
  292. }
  293. dd = GetPointToLineSegmentDistance( x2f, y2f, x1i, y1i, x1f, y1f );
  294. if( dd < dist )
  295. {
  296. dist = dd;
  297. xx = x2f;
  298. yy = y2f;
  299. }
  300. if( x )
  301. *x = KiROUND( xx );
  302. if( y )
  303. *y = KiROUND( yy );
  304. if( d )
  305. *d = dist;
  306. return false;
  307. }
  308. /* Function GetClearanceBetweenSegments
  309. * Get clearance between 2 segments
  310. * Returns coordinates of the closest point between these 2 segments in x, y
  311. * If clearance > max_cl, just returns max_cl+1 and doesn't return x,y
  312. */
  313. int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int w1,
  314. int x2i, int y2i, int x2f, int y2f, int w2,
  315. int max_cl, int* x, int* y )
  316. {
  317. // check clearance between bounding rectangles
  318. int min_dist = max_cl + ( (w1 + w2) / 2 );
  319. if( std::min( x1i, x1f ) - std::max( x2i, x2f ) > min_dist )
  320. return max_cl+1;
  321. if( std::min( x2i, x2f ) - std::max( x1i, x1f ) > min_dist )
  322. return max_cl+1;
  323. if( std::min( y1i, y1f ) - std::max( y2i, y2f ) > min_dist )
  324. return max_cl+1;
  325. if( std::min( y2i, y2f ) - std::max( y1i, y1f ) > min_dist )
  326. return max_cl+1;
  327. int xx, yy;
  328. double dist;
  329. TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f,
  330. x2i, y2i, x2f, y2f, &xx, &yy, &dist );
  331. int d = KiROUND( dist ) - ((w1 + w2) / 2);
  332. if( d < 0 )
  333. d = 0;
  334. if( x )
  335. *x = xx;
  336. if( y )
  337. *y = yy;
  338. return d;
  339. }
  340. /* Function GetPointToLineDistance
  341. * Get min. distance from (x,y) to line y = a + bx
  342. * if b > DBL_MAX/10, assume vertical line at x = a
  343. * returns closest point on line in xpp, ypp
  344. */
  345. double GetPointToLineDistance( double a, double b, int x, int y, double* xpp, double* ypp )
  346. {
  347. if( b > DBL_MAX / 10 )
  348. {
  349. // vertical line
  350. if( xpp && ypp )
  351. {
  352. *xpp = a;
  353. *ypp = y;
  354. }
  355. return std::abs( a - x );
  356. }
  357. // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
  358. double d = -1.0 / b;
  359. double c = (double) y - d * x;
  360. // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
  361. double xp = (a - c) / (d - b);
  362. double yp = a + b * xp;
  363. if( xpp && ypp )
  364. {
  365. *xpp = xp;
  366. *ypp = yp;
  367. }
  368. // find distance
  369. return Distance( x, y, xp, yp );
  370. }
  371. /**
  372. * Function GetPointToLineSegmentDistance
  373. * Get distance between line segment and point
  374. * @param x,y = point
  375. * @param xi,yi Start point of the line segament
  376. * @param xf,yf End point of the line segment
  377. * @return the distance
  378. */
  379. double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf )
  380. {
  381. // test for vertical or horizontal segment
  382. if( xf==xi )
  383. {
  384. // vertical line segment
  385. if( InRange( y, yi, yf ) )
  386. return std::abs( x - xi );
  387. else
  388. return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
  389. }
  390. else if( yf==yi )
  391. {
  392. // horizontal line segment
  393. if( InRange( x, xi, xf ) )
  394. return std::abs( y - yi );
  395. else
  396. return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
  397. }
  398. else
  399. {
  400. // oblique segment
  401. // find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx
  402. double b = (double) (yf - yi) / (xf - xi);
  403. double a = (double) yi - b * xi;
  404. // find c,d such that (x,y) lies on y = c + dx where d=(-1/b)
  405. double d = -1.0 / b;
  406. double c = (double) y - d * x;
  407. // find nearest point to (x,y) on line through (xi,yi) to (xf,yf)
  408. double xp = (a - c) / (d - b);
  409. double yp = a + b * xp;
  410. // find distance
  411. if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) )
  412. return Distance( x, y, xp, yp );
  413. else
  414. return std::min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) );
  415. }
  416. }
  417. // test for value within range
  418. bool InRange( double x, double xi, double xf )
  419. {
  420. if( xf > xi )
  421. {
  422. if( x >= xi && x <= xf )
  423. return true;
  424. }
  425. else
  426. {
  427. if( x >= xf && x <= xi )
  428. return true;
  429. }
  430. return false;
  431. }