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.

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