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.

588 lines
18 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  5. * Copyright (C) 2020 CERN
  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 3
  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-3.0.html
  20. * or you may search the http://www.gnu.org website for the version 3 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 DRC_RTREE_H_
  25. #define DRC_RTREE_H_
  26. #include <board_item.h>
  27. #include <pad.h>
  28. #include <pcb_text.h>
  29. #include <memory>
  30. #include <unordered_set>
  31. #include <set>
  32. #include <vector>
  33. #include <geometry/rtree.h>
  34. #include <geometry/shape.h>
  35. #include <geometry/shape_segment.h>
  36. #include <math/vector2d.h>
  37. #include "geometry/shape_null.h"
  38. #include "board.h"
  39. /**
  40. * Implement an R-tree for fast spatial and layer indexing of connectable items.
  41. * Non-owning.
  42. */
  43. class DRC_RTREE
  44. {
  45. public:
  46. struct ITEM_WITH_SHAPE
  47. {
  48. ITEM_WITH_SHAPE( BOARD_ITEM *aParent, const SHAPE* aShape,
  49. std::shared_ptr<SHAPE> aParentShape = nullptr ) :
  50. parent( aParent ),
  51. shape( aShape ),
  52. shapeStorage( nullptr ),
  53. parentShape( std::move( aParentShape ) )
  54. {};
  55. ITEM_WITH_SHAPE( BOARD_ITEM *aParent, const std::shared_ptr<SHAPE>& aShape,
  56. std::shared_ptr<SHAPE> aParentShape = nullptr ) :
  57. parent( aParent ),
  58. shape( aShape.get() ),
  59. shapeStorage( aShape ),
  60. parentShape( std::move( aParentShape ) )
  61. {};
  62. BOARD_ITEM* parent;
  63. const SHAPE* shape;
  64. std::shared_ptr<SHAPE> shapeStorage;
  65. std::shared_ptr<SHAPE> parentShape;
  66. };
  67. private:
  68. using drc_rtree = RTree<ITEM_WITH_SHAPE*, int, 2, double>;
  69. public:
  70. DRC_RTREE()
  71. {
  72. for( int layer : LSET::AllLayersMask().Seq() )
  73. m_tree[layer] = new drc_rtree();
  74. m_count = 0;
  75. }
  76. ~DRC_RTREE()
  77. {
  78. for( drc_rtree* tree : m_tree )
  79. {
  80. for( DRC_RTREE::ITEM_WITH_SHAPE* el : *tree )
  81. delete el;
  82. delete tree;
  83. }
  84. }
  85. /**
  86. * Insert an item into the tree on a particular layer with an optional worst clearance.
  87. */
  88. void Insert( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aWorstClearance = 0 )
  89. {
  90. Insert( aItem, aLayer, aLayer, aWorstClearance );
  91. }
  92. /**
  93. * Insert an item into the tree on a particular layer with a worst clearance. Allows the
  94. * source layer to be different from the tree layer.
  95. */
  96. void Insert( BOARD_ITEM* aItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer,
  97. int aWorstClearance )
  98. {
  99. wxCHECK( aTargetLayer != UNDEFINED_LAYER, /* void */ );
  100. if( ( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
  101. && !static_cast<PCB_TEXT*>( aItem )->IsVisible() )
  102. {
  103. return;
  104. }
  105. std::vector<const SHAPE*> subshapes;
  106. std::shared_ptr<SHAPE> shape = aItem->GetEffectiveShape( aRefLayer );
  107. if( shape->HasIndexableSubshapes() )
  108. shape->GetIndexableSubshapes( subshapes );
  109. else
  110. subshapes.push_back( shape.get() );
  111. for( const SHAPE* subshape : subshapes )
  112. {
  113. if( dynamic_cast<const SHAPE_NULL*>( subshape ) )
  114. continue;
  115. BOX2I bbox = subshape->BBox();
  116. bbox.Inflate( aWorstClearance );
  117. const int mmin[2] = { bbox.GetX(), bbox.GetY() };
  118. const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
  119. ITEM_WITH_SHAPE* itemShape = new ITEM_WITH_SHAPE( aItem, subshape, shape );
  120. m_tree[aTargetLayer]->Insert( mmin, mmax, itemShape );
  121. m_count++;
  122. }
  123. if( aItem->Type() == PCB_PAD_T && aItem->HasHole() )
  124. {
  125. std::shared_ptr<SHAPE_SEGMENT> hole = aItem->GetEffectiveHoleShape();
  126. BOX2I bbox = hole->BBox();
  127. bbox.Inflate( aWorstClearance );
  128. const int mmin[2] = { bbox.GetX(), bbox.GetY() };
  129. const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() };
  130. ITEM_WITH_SHAPE* itemShape = new ITEM_WITH_SHAPE( aItem, hole, shape );
  131. m_tree[aTargetLayer]->Insert( mmin, mmax, itemShape );
  132. m_count++;
  133. }
  134. }
  135. /**
  136. * Remove all items from the RTree.
  137. */
  138. void clear()
  139. {
  140. for( auto tree : m_tree )
  141. tree->RemoveAll();
  142. m_count = 0;
  143. }
  144. bool CheckColliding( SHAPE* aRefShape, PCB_LAYER_ID aTargetLayer, int aClearance = 0,
  145. std::function<bool( BOARD_ITEM*)> aFilter = nullptr ) const
  146. {
  147. BOX2I box = aRefShape->BBox();
  148. box.Inflate( aClearance );
  149. int min[2] = { box.GetX(), box.GetY() };
  150. int max[2] = { box.GetRight(), box.GetBottom() };
  151. int count = 0;
  152. auto visit =
  153. [&] ( ITEM_WITH_SHAPE* aItem ) -> bool
  154. {
  155. if( !aFilter || aFilter( aItem->parent ) )
  156. {
  157. int actual;
  158. if( aRefShape->Collide( aItem->shape, aClearance, &actual ) )
  159. {
  160. count++;
  161. return false;
  162. }
  163. }
  164. return true;
  165. };
  166. this->m_tree[aTargetLayer]->Search( min, max, visit );
  167. return count > 0;
  168. }
  169. /**
  170. * This is a fast test which essentially does bounding-box overlap given a worst-case
  171. * clearance. It's used when looking up the specific item-to-item clearance might be
  172. * expensive and should be deferred till we know we have a possible hit.
  173. */
  174. int QueryColliding( BOARD_ITEM* aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer,
  175. std::function<bool( BOARD_ITEM* )> aFilter = nullptr,
  176. std::function<bool( BOARD_ITEM* )> aVisitor = nullptr,
  177. int aClearance = 0 ) const
  178. {
  179. // keep track of BOARD_ITEMs that have already been found to collide (some items might
  180. // be built of COMPOUND/triangulated shapes and a single subshape collision means we have
  181. // a hit)
  182. std::unordered_set<BOARD_ITEM*> collidingCompounds;
  183. // keep track of results of client filter so we don't ask more than once for compound
  184. // shapes
  185. std::unordered_map<BOARD_ITEM*, bool> filterResults;
  186. BOX2I box = aRefItem->GetBoundingBox();
  187. box.Inflate( aClearance );
  188. int min[2] = { box.GetX(), box.GetY() };
  189. int max[2] = { box.GetRight(), box.GetBottom() };
  190. std::shared_ptr<SHAPE> refShape = aRefItem->GetEffectiveShape( aRefLayer );
  191. int count = 0;
  192. auto visit =
  193. [&]( ITEM_WITH_SHAPE* aItem ) -> bool
  194. {
  195. if( aItem->parent == aRefItem )
  196. return true;
  197. if( collidingCompounds.find( aItem->parent ) != collidingCompounds.end() )
  198. return true;
  199. bool filtered;
  200. auto it = filterResults.find( aItem->parent );
  201. if( it == filterResults.end() )
  202. {
  203. filtered = aFilter && !aFilter( aItem->parent );
  204. filterResults[ aItem->parent ] = filtered;
  205. }
  206. else
  207. {
  208. filtered = it->second;
  209. }
  210. if( filtered )
  211. return true;
  212. if( refShape->Collide( aItem->shape, aClearance ) )
  213. {
  214. collidingCompounds.insert( aItem->parent );
  215. count++;
  216. if( aVisitor )
  217. return aVisitor( aItem->parent );
  218. }
  219. return true;
  220. };
  221. this->m_tree[aTargetLayer]->Search( min, max, visit );
  222. return count;
  223. }
  224. /**
  225. * This one is for tessellated items. (All shapes in the tree will be from a single
  226. * BOARD_ITEM.)
  227. * It checks all items in the bbox overlap to find the minimal actual distance and
  228. * position.
  229. */
  230. bool QueryColliding( const BOX2I& aBox, SHAPE* aRefShape, PCB_LAYER_ID aLayer, int aClearance,
  231. int* aActual, VECTOR2I* aPos ) const
  232. {
  233. BOX2I bbox = aBox;
  234. bbox.Inflate( aClearance );
  235. int min[2] = { bbox.GetX(), bbox.GetY() };
  236. int max[2] = { bbox.GetRight(), bbox.GetBottom() };
  237. bool collision = false;
  238. int actual = INT_MAX;
  239. VECTOR2I pos;
  240. auto visit =
  241. [&]( ITEM_WITH_SHAPE* aItem ) -> bool
  242. {
  243. int curActual;
  244. VECTOR2I curPos;
  245. if( aRefShape->Collide( aItem->shape, aClearance, &curActual, &curPos ) )
  246. {
  247. collision = true;
  248. if( curActual < actual )
  249. {
  250. actual = curActual;
  251. pos = curPos;
  252. }
  253. // Stop looking after we have a true collision
  254. if( actual <= 0 )
  255. return false;
  256. }
  257. return true;
  258. };
  259. this->m_tree[aLayer]->Search( min, max, visit );
  260. if( collision )
  261. {
  262. if( aActual )
  263. *aActual = std::max( 0, actual );
  264. if( aPos )
  265. *aPos = pos;
  266. return true;
  267. }
  268. return false;
  269. }
  270. /**
  271. * Quicker version of above that just reports a raw yes/no.
  272. */
  273. bool QueryColliding( const BOX2I& aBox, SHAPE* aRefShape, PCB_LAYER_ID aLayer ) const
  274. {
  275. SHAPE_POLY_SET* poly = dynamic_cast<SHAPE_POLY_SET*>( aRefShape );
  276. int min[2] = { aBox.GetX(), aBox.GetY() };
  277. int max[2] = { aBox.GetRight(), aBox.GetBottom() };
  278. bool collision = false;
  279. // Special case the polygon case. Otherwise we'll call its Collide() method which will
  280. // triangulate it as well and then do triangle/triangle collisions. This ends up being
  281. // *much* slower than 3 segment Collide()s and a PointInside().
  282. auto polyVisitor =
  283. [&]( ITEM_WITH_SHAPE* aItem ) -> bool
  284. {
  285. const SHAPE* shape = aItem->shape;
  286. wxASSERT( dynamic_cast<const SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI*>( shape ) );
  287. auto tri = static_cast<const SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI*>( shape );
  288. const SHAPE_LINE_CHAIN& outline = poly->Outline( 0 );
  289. for( int ii = 0; ii < (int) tri->GetSegmentCount(); ++ii )
  290. {
  291. if( outline.Collide( tri->GetSegment( ii ) ) )
  292. {
  293. collision = true;
  294. return false;
  295. }
  296. }
  297. // Also must check for poly being completely inside the triangle
  298. if( tri->PointInside( outline.CPoint( 0 ) ) )
  299. {
  300. collision = true;
  301. return false;
  302. }
  303. return true;
  304. };
  305. auto visitor =
  306. [&]( ITEM_WITH_SHAPE* aItem ) -> bool
  307. {
  308. if( aRefShape->Collide( aItem->shape, 0 ) )
  309. {
  310. collision = true;
  311. return false;
  312. }
  313. return true;
  314. };
  315. if( poly && poly->OutlineCount() == 1 && poly->HoleCount( 0 ) == 0 )
  316. this->m_tree[aLayer]->Search( min, max, polyVisitor );
  317. else
  318. this->m_tree[aLayer]->Search( min, max, visitor );
  319. return collision;
  320. }
  321. /**
  322. * Gets the BOARD_ITEMs that overlap the specified point/layer
  323. * @param aPt Position on the tree
  324. * @param aLayer Layer to search
  325. * @return vector of overlapping BOARD_ITEMS*
  326. */
  327. std::unordered_set<BOARD_ITEM*> GetObjectsAt( const VECTOR2I& aPt, PCB_LAYER_ID aLayer,
  328. int aClearance = 0 )
  329. {
  330. std::unordered_set<BOARD_ITEM*> retval;
  331. int min[2] = { aPt.x - aClearance, aPt.y - aClearance };
  332. int max[2] = { aPt.x + aClearance, aPt.y + aClearance };
  333. auto visitor =
  334. [&]( ITEM_WITH_SHAPE* aItem ) -> bool
  335. {
  336. retval.insert( aItem->parent );
  337. return true;
  338. };
  339. m_tree[aLayer]->Search( min, max, visitor );
  340. return retval;
  341. }
  342. typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> LAYER_PAIR;
  343. struct PAIR_INFO
  344. {
  345. PAIR_INFO( LAYER_PAIR aPair, ITEM_WITH_SHAPE* aRef, ITEM_WITH_SHAPE* aTest ) :
  346. layerPair( aPair ),
  347. refItem( aRef ),
  348. testItem( aTest )
  349. { };
  350. LAYER_PAIR layerPair;
  351. ITEM_WITH_SHAPE* refItem;
  352. ITEM_WITH_SHAPE* testItem;
  353. };
  354. int QueryCollidingPairs( DRC_RTREE* aRefTree, std::vector<LAYER_PAIR> aLayerPairs,
  355. std::function<bool( const LAYER_PAIR&, ITEM_WITH_SHAPE*,
  356. ITEM_WITH_SHAPE*, bool* aCollision )> aVisitor,
  357. int aMaxClearance,
  358. std::function<bool(int, int )> aProgressReporter ) const
  359. {
  360. std::vector<PAIR_INFO> pairsToVisit;
  361. for( LAYER_PAIR& layerPair : aLayerPairs )
  362. {
  363. const PCB_LAYER_ID refLayer = layerPair.first;
  364. const PCB_LAYER_ID targetLayer = layerPair.second;
  365. for( ITEM_WITH_SHAPE* refItem : aRefTree->OnLayer( refLayer ) )
  366. {
  367. BOX2I box = refItem->shape->BBox();
  368. box.Inflate( aMaxClearance );
  369. int min[2] = { box.GetX(), box.GetY() };
  370. int max[2] = { box.GetRight(), box.GetBottom() };
  371. auto visit =
  372. [&]( ITEM_WITH_SHAPE* aItemToTest ) -> bool
  373. {
  374. // don't collide items against themselves
  375. if( aItemToTest->parent == refItem->parent )
  376. return true;
  377. pairsToVisit.emplace_back( layerPair, refItem, aItemToTest );
  378. return true;
  379. };
  380. this->m_tree[targetLayer]->Search( min, max, visit );
  381. };
  382. }
  383. // keep track of BOARD_ITEMs pairs that have been already found to collide (some items
  384. // might be build of COMPOUND/triangulated shapes and a single subshape collision
  385. // means we have a hit)
  386. std::unordered_map<PTR_PTR_CACHE_KEY, int> collidingCompounds;
  387. int progress = 0;
  388. int count = pairsToVisit.size();
  389. for( const PAIR_INFO& pair : pairsToVisit )
  390. {
  391. if( !aProgressReporter( progress++, count ) )
  392. break;
  393. BOARD_ITEM* a = pair.refItem->parent;
  394. BOARD_ITEM* b = pair.testItem->parent;
  395. // store canonical order so we don't collide in both directions (a:b and b:a)
  396. if( static_cast<void*>( a ) > static_cast<void*>( b ) )
  397. std::swap( a, b );
  398. // don't report multiple collisions for compound or triangulated shapes
  399. if( collidingCompounds.count( { a, b } ) )
  400. continue;
  401. bool collisionDetected = false;
  402. if( !aVisitor( pair.layerPair, pair.refItem, pair.testItem, &collisionDetected ) )
  403. break;
  404. if( collisionDetected )
  405. collidingCompounds[ { a, b } ] = 1;
  406. }
  407. return 0;
  408. }
  409. /**
  410. * Return the number of items in the tree.
  411. *
  412. * @return number of elements in the tree.
  413. */
  414. size_t size() const
  415. {
  416. return m_count;
  417. }
  418. bool empty() const
  419. {
  420. return m_count == 0;
  421. }
  422. using iterator = typename drc_rtree::Iterator;
  423. /**
  424. * The DRC_LAYER struct provides a layer-specific auto-range iterator to the RTree. Using
  425. * this struct, one can write lines like:
  426. *
  427. * for( auto item : rtree.OnLayer( In1_Cu ) )
  428. *
  429. * and iterate over only the RTree items that are on In1
  430. */
  431. struct DRC_LAYER
  432. {
  433. DRC_LAYER( drc_rtree* aTree ) : layer_tree( aTree )
  434. {
  435. m_rect = { { INT_MIN, INT_MIN }, { INT_MAX, INT_MAX } };
  436. };
  437. DRC_LAYER( drc_rtree* aTree, const BOX2I& aRect ) : layer_tree( aTree )
  438. {
  439. m_rect = { { aRect.GetX(), aRect.GetY() },
  440. { aRect.GetRight(), aRect.GetBottom() } };
  441. };
  442. drc_rtree::Rect m_rect;
  443. drc_rtree* layer_tree;
  444. iterator begin()
  445. {
  446. return layer_tree->begin( m_rect );
  447. }
  448. iterator end()
  449. {
  450. return layer_tree->end( m_rect );
  451. }
  452. };
  453. DRC_LAYER OnLayer( PCB_LAYER_ID aLayer ) const
  454. {
  455. return DRC_LAYER( m_tree[int( aLayer )] );
  456. }
  457. DRC_LAYER Overlapping( PCB_LAYER_ID aLayer, const VECTOR2I& aPoint, int aAccuracy = 0 ) const
  458. {
  459. BOX2I rect( aPoint, VECTOR2I( 0, 0 ) );
  460. rect.Inflate( aAccuracy );
  461. return DRC_LAYER( m_tree[int( aLayer )], rect );
  462. }
  463. DRC_LAYER Overlapping( PCB_LAYER_ID aLayer, const BOX2I& aRect ) const
  464. {
  465. return DRC_LAYER( m_tree[int( aLayer )], aRect );
  466. }
  467. private:
  468. drc_rtree* m_tree[PCB_LAYER_ID_COUNT];
  469. size_t m_count;
  470. };
  471. #endif /* DRC_RTREE_H_ */