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.

509 lines
13 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016-2017 CERN
  5. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  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. #ifndef __POLY_GRID_PARTITION_H
  25. #define __POLY_GRID_PARTITION_H
  26. #include <geometry/seg.h>
  27. #include <geometry/shape_line_chain.h>
  28. #include <geometry/shape_rect.h>
  29. #include <vector>
  30. #include <algorithm>
  31. #include <unordered_map>
  32. #include <set>
  33. /**
  34. * Class POLY_GRID_PARTITION
  35. *
  36. * Provides a fast test for point inside polygon by splitting the edges
  37. * of the polygon into a rectangular grid.
  38. */
  39. class POLY_GRID_PARTITION
  40. {
  41. private:
  42. enum HASH_FLAG
  43. {
  44. LEAD_H = 1,
  45. LEAD_V = 2,
  46. TRAIL_H = 4,
  47. TRAIL_V = 8
  48. };
  49. using EDGE_LIST = std::vector<int>;
  50. template <class T>
  51. inline void hash_combine( std::size_t& seed, const T& v )
  52. {
  53. std::hash<T> hasher;
  54. seed ^= hasher( v ) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  55. }
  56. struct segsEqual
  57. {
  58. bool operator()( const SEG& a, const SEG& b ) const
  59. {
  60. return (a.A == b.A && a.B == b.B) || (a.A == b.B && a.B == b.A);
  61. }
  62. };
  63. struct segHash
  64. {
  65. std::size_t operator()( const SEG& a ) const
  66. {
  67. return a.A.x + a.B.x + a.A.y + a.B.y;
  68. }
  69. };
  70. const VECTOR2I grid2poly( const VECTOR2I& p ) const
  71. {
  72. int px = rescale( p.x, m_bbox.GetWidth(), m_gridSize ) + m_bbox.GetPosition().x;
  73. int py = rescale( p.y, m_bbox.GetHeight(), m_gridSize ) + m_bbox.GetPosition().y; // (int) floor( (double) p.y / m_gridSize * (double) m_bbox.GetHeight() + m_bbox.GetPosition().y );
  74. return VECTOR2I( px, py );
  75. }
  76. void stupid_test() const
  77. {
  78. for(int i = 0; i < 16;i++)
  79. assert( poly2gridX(grid2polyX(i)) == i);
  80. }
  81. int grid2polyX( int x ) const
  82. {
  83. return rescale( x, m_bbox.GetWidth(), m_gridSize ) + m_bbox.GetPosition().x;
  84. }
  85. int grid2polyY( int y ) const
  86. {
  87. return rescale( y, m_bbox.GetHeight(), m_gridSize ) + m_bbox.GetPosition().y;
  88. }
  89. const VECTOR2I poly2grid( const VECTOR2I& p ) const
  90. {
  91. int px = rescale( p.x - m_bbox.GetPosition().x, m_gridSize, m_bbox.GetWidth() );
  92. int py = rescale( p.y - m_bbox.GetPosition().y, m_gridSize, m_bbox.GetHeight() );
  93. if( px < 0 )
  94. px = 0;
  95. if( px >= m_gridSize )
  96. px = m_gridSize - 1;
  97. if( py < 0 )
  98. py = 0;
  99. if( py >= m_gridSize )
  100. py = m_gridSize - 1;
  101. return VECTOR2I( px, py );
  102. }
  103. int poly2gridX( int x ) const
  104. {
  105. int px = rescale( x - m_bbox.GetPosition().x, m_gridSize, m_bbox.GetWidth() );
  106. if( px < 0 )
  107. px = 0;
  108. if( px >= m_gridSize )
  109. px = m_gridSize - 1;
  110. return px;
  111. }
  112. int poly2gridY( int y ) const
  113. {
  114. int py = rescale( y - m_bbox.GetPosition().y, m_gridSize, m_bbox.GetHeight() );
  115. if( py < 0 )
  116. py = 0;
  117. if( py >= m_gridSize )
  118. py = m_gridSize - 1;
  119. return py;
  120. }
  121. void build( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize )
  122. {
  123. m_outline = aPolyOutline;
  124. //if (orientation(m_outline) < 0)
  125. // m_outline = m_outline.Reverse();
  126. m_bbox = m_outline.BBox();
  127. m_gridSize = gridSize;
  128. m_outline.SetClosed( true );
  129. m_grid.reserve( gridSize * gridSize );
  130. for( int y = 0; y < gridSize; y++ )
  131. {
  132. for( int x = 0; x < gridSize; x++ )
  133. {
  134. m_grid.push_back( EDGE_LIST() );
  135. }
  136. }
  137. VECTOR2I ref_v( 0, 1 );
  138. VECTOR2I ref_h( 0, 1 );
  139. m_flags.reserve( m_outline.SegmentCount() );
  140. std::unordered_map<SEG, int, segHash, segsEqual> edgeSet;
  141. for( int i = 0; i<m_outline.SegmentCount(); i++ )
  142. {
  143. SEG edge = m_outline.CSegment( i );
  144. if( edgeSet.find( edge ) == edgeSet.end() )
  145. {
  146. edgeSet[edge] = 1;
  147. }
  148. else
  149. {
  150. edgeSet[edge]++;
  151. }
  152. }
  153. for( int i = 0; i<m_outline.SegmentCount(); i++ )
  154. {
  155. auto edge = m_outline.CSegment( i );
  156. auto dir = edge.B - edge.A;
  157. int flags = 0;
  158. if ( dir.y == 0 )
  159. {
  160. flags = 0;
  161. }
  162. else if( edgeSet[edge] == 1 )
  163. {
  164. if( dir.Dot( ref_h ) < 0 )
  165. {
  166. flags |= LEAD_H;
  167. }
  168. else if( dir.Dot( ref_h ) > 0 )
  169. {
  170. flags |= TRAIL_H;
  171. }
  172. }
  173. m_flags.push_back( flags );
  174. if( edge.A.y == edge.B.y )
  175. continue;
  176. std::set<int> indices;
  177. indices.insert( m_gridSize * poly2gridY( edge.A.y ) + poly2gridX( edge.A.x ) );
  178. indices.insert( m_gridSize * poly2gridY( edge.B.y ) + poly2gridX( edge.B.x ) );
  179. if( edge.A.x > edge.B.x )
  180. std::swap( edge.A, edge.B );
  181. dir = edge.B - edge.A;
  182. if( dir.x != 0 )
  183. {
  184. int gx0 = poly2gridX( edge.A.x );
  185. int gx1 = poly2gridX( edge.B.x );
  186. for( int x = gx0; x <= gx1; x++ )
  187. {
  188. int px = grid2polyX( x );
  189. int py = ( edge.A.y + rescale( dir.y, px - edge.A.x, dir.x ) );
  190. int yy = poly2gridY( py );
  191. indices.insert( m_gridSize * yy + x );
  192. if( x > 0 )
  193. indices.insert( m_gridSize * yy + x - 1 );
  194. }
  195. }
  196. if( edge.A.y > edge.B.y )
  197. std::swap( edge.A, edge.B );
  198. dir = edge.B - edge.A;
  199. if( dir.y != 0 )
  200. {
  201. int gy0 = poly2gridY( edge.A.y );
  202. int gy1 = poly2gridY( edge.B.y );
  203. for( int y = gy0; y <= gy1; y++ )
  204. {
  205. int py = grid2polyY( y );
  206. int px = ( edge.A.x + rescale( dir.x, py - edge.A.y, dir.y ) );
  207. int xx = poly2gridX( px );
  208. indices.insert( m_gridSize * y + xx );
  209. if( y > 0 )
  210. indices.insert( m_gridSize * (y - 1) + xx );
  211. }
  212. }
  213. for( auto idx : indices )
  214. m_grid[idx].push_back( i );
  215. }
  216. }
  217. bool inRange( int v1, int v2, int x ) const
  218. {
  219. if( v1 < v2 )
  220. {
  221. return x >= v1 && x <= v2;
  222. }
  223. return x >= v2 && x <= v1;
  224. }
  225. struct SCAN_STATE
  226. {
  227. SCAN_STATE()
  228. {
  229. dist_prev = INT_MAX;
  230. dist_max = INT_MAX;
  231. nearest = -1;
  232. nearest_prev = -1;
  233. };
  234. int dist_prev;
  235. int dist_max;
  236. int nearest_prev;
  237. int nearest;
  238. };
  239. void scanCell( SCAN_STATE& state, const EDGE_LIST& cell, const VECTOR2I& aP, int cx, int cy ) const
  240. {
  241. int cx0 = grid2polyX(cx);
  242. int cx1 = grid2polyX(cx + 1);
  243. for( auto index : cell )
  244. {
  245. const SEG& edge = m_outline.CSegment( index );
  246. if( m_flags[index] == 0 )
  247. {
  248. if ( aP.y == edge.A.y && inRange( edge.A.x, edge.B.x, aP.x ) ) // we belong to the outline
  249. {
  250. state.nearest = index;
  251. state.dist_max = 0;
  252. return;
  253. } else {
  254. continue;
  255. }
  256. }
  257. if( inRange( edge.A.y, edge.B.y, aP.y ) )
  258. {
  259. int dist = 0;
  260. int x0;
  261. if( edge.A.y == aP.y )
  262. {
  263. x0 = edge.A.x;
  264. }
  265. else if( edge.B.y == aP.y )
  266. {
  267. x0 = edge.B.x;
  268. }
  269. else
  270. {
  271. x0 = edge.A.x + rescale( ( edge.B.x - edge.A.x ), (aP.y - edge.A.y), (edge.B.y - edge.A.y ) );
  272. }
  273. if( x0 < cx0 || x0 > cx1 )
  274. {
  275. continue;
  276. }
  277. dist = aP.x - x0;
  278. if( dist == 0 )
  279. {
  280. if( state.nearest_prev < 0 || state.nearest != index )
  281. {
  282. state.dist_prev = state.dist_max;
  283. state.nearest_prev = state.nearest;
  284. }
  285. state.nearest = index;
  286. state.dist_max = 0;
  287. return;
  288. }
  289. if( dist != 0 && std::abs( dist ) <= std::abs( state.dist_max ) )
  290. {
  291. if( state.nearest_prev < 0 || state.nearest != index )
  292. {
  293. state.dist_prev = state.dist_max;
  294. state.nearest_prev = state.nearest;
  295. }
  296. state.dist_max = dist;
  297. state.nearest = index;
  298. }
  299. }
  300. }
  301. }
  302. public:
  303. POLY_GRID_PARTITION( const SHAPE_LINE_CHAIN& aPolyOutline, int gridSize )
  304. {
  305. build( aPolyOutline, gridSize );
  306. }
  307. int containsPoint( const VECTOR2I& aP, bool debug = false ) const
  308. {
  309. const auto gridPoint = poly2grid( aP );
  310. if( !m_bbox.Contains( aP ) )
  311. return 0;
  312. SCAN_STATE state;
  313. const EDGE_LIST& cell = m_grid[ m_gridSize * gridPoint.y + gridPoint.x ];
  314. scanCell( state, cell, aP, gridPoint.x, gridPoint.y );
  315. if( state.nearest < 0 )
  316. {
  317. state = SCAN_STATE();
  318. for( int d = 1; d <= m_gridSize; d++ )
  319. {
  320. int xl = gridPoint.x - d;
  321. int xh = gridPoint.x + d;
  322. if( xl >= 0 )
  323. {
  324. const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xl ];
  325. scanCell( state, cell2, aP, xl, gridPoint.y );
  326. if( state.nearest >= 0 )
  327. break;
  328. }
  329. if( xh < m_gridSize )
  330. {
  331. const EDGE_LIST& cell2 = m_grid[ m_gridSize * gridPoint.y + xh ];
  332. scanCell( state, cell2, aP, xh, gridPoint.y );
  333. if( state.nearest >= 0 )
  334. break;
  335. }
  336. }
  337. }
  338. if( state.nearest < 0 )
  339. return 0;
  340. if( state.dist_max == 0 )
  341. return 1;
  342. // special case for diagonal 'slits', e.g. two segments that partially overlap each other.
  343. if( state.nearest_prev >= 0 && state.dist_max == state.dist_prev )
  344. {
  345. int d = std::abs( state.nearest_prev - state.nearest );
  346. if( (d == 1) && ( (m_flags[state.nearest_prev] & m_flags[state.nearest]) == 0 ) )
  347. {
  348. return 0;
  349. }
  350. }
  351. if( state.dist_max > 0 )
  352. {
  353. return m_flags[state.nearest] & LEAD_H ? 1 : 0;
  354. }
  355. else
  356. {
  357. return m_flags[state.nearest] & TRAIL_H ? 1 : 0;
  358. }
  359. }
  360. bool checkClearance( const VECTOR2I& aP, int aClearance )
  361. {
  362. int gx0 = poly2gridX( aP.x - aClearance - 1);
  363. int gx1 = poly2gridX( aP.x + aClearance + 1);
  364. int gy0 = poly2gridY( aP.y - aClearance - 1);
  365. int gy1 = poly2gridY( aP.y + aClearance + 1);
  366. using ecoord = VECTOR2I::extended_type;
  367. ecoord dist = (ecoord) aClearance * aClearance;
  368. for ( int gx = gx0; gx <= gx1; gx++ )
  369. {
  370. for ( int gy = gy0; gy <= gy1; gy++ )
  371. {
  372. const auto& cell = m_grid [ m_gridSize * gy + gx];
  373. for ( auto index : cell )
  374. {
  375. const auto& seg = m_outline.CSegment(index);
  376. if ( seg.SquaredDistance(aP) <= dist )
  377. return true;
  378. }
  379. }
  380. }
  381. return false;
  382. }
  383. int ContainsPoint( const VECTOR2I& aP, int aClearance = 0 ) // const
  384. {
  385. if( containsPoint(aP) )
  386. return 1;
  387. if( aClearance > 0 )
  388. return checkClearance ( aP, aClearance );
  389. return 0;
  390. }
  391. const BOX2I& BBox() const
  392. {
  393. return m_bbox;
  394. }
  395. private:
  396. int m_gridSize;
  397. SHAPE_LINE_CHAIN m_outline;
  398. BOX2I m_bbox;
  399. std::vector<int> m_flags;
  400. std::vector<EDGE_LIST> m_grid;
  401. };
  402. #endif