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.

932 lines
26 KiB

5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016-2018 CERN
  5. * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <algorithm>
  27. #include <future>
  28. #include <mutex>
  29. #include <connectivity/connectivity_algo.h>
  30. #include <progress_reporter.h>
  31. #include <geometry/geometry_utils.h>
  32. #include <board_commit.h>
  33. #include <thread_pool.h>
  34. #include <wx/log.h>
  35. #ifdef PROFILE
  36. #include <profile.h>
  37. #endif
  38. bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
  39. {
  40. markItemNetAsDirty( aItem );
  41. switch( aItem->Type() )
  42. {
  43. case PCB_FOOTPRINT_T:
  44. for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
  45. {
  46. m_itemMap[pad].MarkItemsAsInvalid();
  47. m_itemMap.erase( pad );
  48. }
  49. m_itemList.SetDirty( true );
  50. break;
  51. case PCB_PAD_T:
  52. m_itemMap[aItem].MarkItemsAsInvalid();
  53. m_itemMap.erase( aItem );
  54. m_itemList.SetDirty( true );
  55. break;
  56. case PCB_TRACE_T:
  57. case PCB_ARC_T:
  58. m_itemMap[aItem].MarkItemsAsInvalid();
  59. m_itemMap.erase( aItem );
  60. m_itemList.SetDirty( true );
  61. break;
  62. case PCB_VIA_T:
  63. m_itemMap[aItem].MarkItemsAsInvalid();
  64. m_itemMap.erase( aItem );
  65. m_itemList.SetDirty( true );
  66. break;
  67. case PCB_ZONE_T:
  68. m_itemMap[aItem].MarkItemsAsInvalid();
  69. m_itemMap.erase ( aItem );
  70. m_itemList.SetDirty( true );
  71. break;
  72. default:
  73. return false;
  74. }
  75. // Once we delete an item, it may connect between lists, so mark both as potentially invalid
  76. m_itemList.SetHasInvalid( true );
  77. return true;
  78. }
  79. void CN_CONNECTIVITY_ALGO::markItemNetAsDirty( const BOARD_ITEM* aItem )
  80. {
  81. if( aItem->IsConnected() )
  82. {
  83. const BOARD_CONNECTED_ITEM* citem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
  84. MarkNetAsDirty( citem->GetNetCode() );
  85. }
  86. else
  87. {
  88. if( aItem->Type() == PCB_FOOTPRINT_T )
  89. {
  90. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
  91. for( PAD* pad : footprint->Pads() )
  92. MarkNetAsDirty( pad->GetNetCode() );
  93. }
  94. }
  95. }
  96. bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
  97. {
  98. if( !aItem->IsOnCopperLayer() )
  99. return false;
  100. switch( aItem->Type() )
  101. {
  102. case PCB_NETINFO_T:
  103. MarkNetAsDirty( static_cast<NETINFO_ITEM*>( aItem )->GetNetCode() );
  104. break;
  105. case PCB_FOOTPRINT_T:
  106. {
  107. if( static_cast<FOOTPRINT*>( aItem )->GetAttributes() & FP_JUST_ADDED )
  108. return false;
  109. for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
  110. {
  111. if( m_itemMap.find( pad ) != m_itemMap.end() )
  112. return false;
  113. add( m_itemList, pad );
  114. }
  115. break;
  116. }
  117. case PCB_PAD_T:
  118. {
  119. if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aItem->GetParentFootprint() ) )
  120. {
  121. if( fp->GetAttributes() & FP_JUST_ADDED )
  122. return false;
  123. }
  124. if( m_itemMap.find( aItem ) != m_itemMap.end() )
  125. return false;
  126. add( m_itemList, static_cast<PAD*>( aItem ) );
  127. break;
  128. }
  129. case PCB_TRACE_T:
  130. if( m_itemMap.find( aItem ) != m_itemMap.end() )
  131. return false;
  132. add( m_itemList, static_cast<PCB_TRACK*>( aItem ) );
  133. break;
  134. case PCB_ARC_T:
  135. if( m_itemMap.find( aItem ) != m_itemMap.end() )
  136. return false;
  137. add( m_itemList, static_cast<PCB_ARC*>( aItem ) );
  138. break;
  139. case PCB_VIA_T:
  140. if( m_itemMap.find( aItem ) != m_itemMap.end() )
  141. return false;
  142. add( m_itemList, static_cast<PCB_VIA*>( aItem ) );
  143. break;
  144. case PCB_ZONE_T:
  145. {
  146. ZONE* zone = static_cast<ZONE*>( aItem );
  147. if( m_itemMap.find( aItem ) != m_itemMap.end() )
  148. return false;
  149. m_itemMap[zone] = ITEM_MAP_ENTRY();
  150. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  151. {
  152. for( CN_ITEM* zitem : m_itemList.Add( zone, layer ) )
  153. m_itemMap[zone].Link( zitem );
  154. }
  155. }
  156. break;
  157. default:
  158. return false;
  159. }
  160. markItemNetAsDirty( aItem );
  161. return true;
  162. }
  163. void CN_CONNECTIVITY_ALGO::searchConnections()
  164. {
  165. #ifdef PROFILE
  166. PROF_COUNTER garbage_collection( "garbage-collection" );
  167. #endif
  168. std::vector<CN_ITEM*> garbage;
  169. garbage.reserve( 1024 );
  170. m_itemList.RemoveInvalidItems( garbage );
  171. for( CN_ITEM* item : garbage )
  172. delete item;
  173. #ifdef PROFILE
  174. garbage_collection.Show();
  175. PROF_COUNTER search_basic( "search-basic" );
  176. #endif
  177. thread_pool& tp = GetKiCadThreadPool();
  178. std::vector<CN_ITEM*> dirtyItems;
  179. std::copy_if( m_itemList.begin(), m_itemList.end(), std::back_inserter( dirtyItems ),
  180. [] ( CN_ITEM* aItem )
  181. {
  182. return aItem->Dirty();
  183. } );
  184. if( m_progressReporter )
  185. {
  186. m_progressReporter->SetMaxProgress( dirtyItems.size() );
  187. if( !m_progressReporter->KeepRefreshing() )
  188. return;
  189. }
  190. if( m_itemList.IsDirty() )
  191. {
  192. std::vector<std::future<size_t>> returns( dirtyItems.size() );
  193. auto conn_lambda =
  194. [&dirtyItems]( size_t aItem, CN_LIST* aItemList,
  195. PROGRESS_REPORTER* aReporter) -> size_t
  196. {
  197. if( aReporter && aReporter->IsCancelled() )
  198. return 0;
  199. CN_VISITOR visitor( dirtyItems[aItem] );
  200. aItemList->FindNearby( dirtyItems[aItem], visitor );
  201. if( aReporter )
  202. aReporter->AdvanceProgress();
  203. return 1;
  204. };
  205. for( size_t ii = 0; ii < dirtyItems.size(); ++ii )
  206. returns[ii] = tp.submit( conn_lambda, ii, &m_itemList, m_progressReporter );
  207. for( const std::future<size_t>& retval : returns )
  208. {
  209. // Here we balance returns with a 250ms timeout to allow UI updating
  210. std::future_status status;
  211. do
  212. {
  213. if( m_progressReporter )
  214. m_progressReporter->KeepRefreshing();
  215. status = retval.wait_for( std::chrono::milliseconds( 250 ) );
  216. }
  217. while( status != std::future_status::ready );
  218. }
  219. if( m_progressReporter )
  220. m_progressReporter->KeepRefreshing();
  221. }
  222. #ifdef PROFILE
  223. search_basic.Show();
  224. #endif
  225. m_itemList.ClearDirtyFlags();
  226. }
  227. const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode )
  228. {
  229. if( aMode == CSM_PROPAGATE )
  230. {
  231. return SearchClusters( aMode,
  232. { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_FOOTPRINT_T },
  233. -1 );
  234. }
  235. else
  236. {
  237. return SearchClusters( aMode,
  238. { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_T, PCB_FOOTPRINT_T },
  239. -1 );
  240. }
  241. }
  242. const CN_CONNECTIVITY_ALGO::CLUSTERS
  243. CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode,
  244. const std::initializer_list<KICAD_T>& aTypes,
  245. int aSingleNet, CN_ITEM* rootItem )
  246. {
  247. bool withinAnyNet = ( aMode != CSM_PROPAGATE );
  248. std::deque<CN_ITEM*> Q;
  249. std::set<CN_ITEM*> item_set;
  250. CLUSTERS clusters;
  251. if( m_itemList.IsDirty() )
  252. searchConnections();
  253. auto addToSearchList =
  254. [&item_set, withinAnyNet, aSingleNet, &aTypes, rootItem ]( CN_ITEM *aItem )
  255. {
  256. if( withinAnyNet && aItem->Net() <= 0 )
  257. return;
  258. if( !aItem->Valid() )
  259. return;
  260. if( aSingleNet >=0 && aItem->Net() != aSingleNet )
  261. return;
  262. bool found = false;
  263. for( KICAD_T type : aTypes )
  264. {
  265. if( aItem->Parent()->Type() == type )
  266. {
  267. found = true;
  268. break;
  269. }
  270. }
  271. if( !found && aItem != rootItem )
  272. return;
  273. aItem->SetVisited( false );
  274. item_set.insert( aItem );
  275. };
  276. std::for_each( m_itemList.begin(), m_itemList.end(), addToSearchList );
  277. if( m_progressReporter && m_progressReporter->IsCancelled() )
  278. return CLUSTERS();
  279. while( !item_set.empty() )
  280. {
  281. std::shared_ptr<CN_CLUSTER> cluster = std::make_shared<CN_CLUSTER>();
  282. CN_ITEM* root;
  283. auto it = item_set.begin();
  284. while( it != item_set.end() && (*it)->Visited() )
  285. it = item_set.erase( item_set.begin() );
  286. if( it == item_set.end() )
  287. break;
  288. root = *it;
  289. root->SetVisited( true );
  290. Q.clear();
  291. Q.push_back( root );
  292. while( Q.size() )
  293. {
  294. CN_ITEM* current = Q.front();
  295. Q.pop_front();
  296. cluster->Add( current );
  297. for( CN_ITEM* n : current->ConnectedItems() )
  298. {
  299. if( withinAnyNet && n->Net() != root->Net() )
  300. continue;
  301. if( !n->Visited() && n->Valid() )
  302. {
  303. n->SetVisited( true );
  304. Q.push_back( n );
  305. }
  306. }
  307. }
  308. clusters.push_back( cluster );
  309. }
  310. if( m_progressReporter && m_progressReporter->IsCancelled() )
  311. return CLUSTERS();
  312. std::sort( clusters.begin(), clusters.end(),
  313. []( const std::shared_ptr<CN_CLUSTER>& a, const std::shared_ptr<CN_CLUSTER>& b )
  314. {
  315. return a->OriginNet() < b->OriginNet();
  316. } );
  317. return clusters;
  318. }
  319. void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
  320. {
  321. // Generate CN_ZONE_LAYERs for each island on each layer of each zone
  322. //
  323. std::vector<CN_ZONE_LAYER*> zitems;
  324. for( ZONE* zone : aBoard->Zones() )
  325. {
  326. if( zone->IsOnCopperLayer() )
  327. {
  328. m_itemMap[zone] = ITEM_MAP_ENTRY();
  329. markItemNetAsDirty( zone );
  330. for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
  331. {
  332. if( IsCopperLayer( layer ) )
  333. {
  334. for( int j = 0; j < zone->GetFilledPolysList( layer )->OutlineCount(); j++ )
  335. zitems.push_back( new CN_ZONE_LAYER( zone, layer, j ) );
  336. }
  337. }
  338. }
  339. }
  340. // Setup progress metrics
  341. //
  342. int progressDelta = 50;
  343. double size = 0.0;
  344. size += zitems.size(); // Once for building RTrees
  345. size += zitems.size(); // Once for adding to connectivity
  346. size += aBoard->Tracks().size();
  347. for( FOOTPRINT* footprint : aBoard->Footprints() )
  348. size += footprint->Pads().size();
  349. size *= 1.5; // Our caller gets the other third of the progress bar
  350. progressDelta = std::max( progressDelta, (int) size / 4 );
  351. auto report =
  352. [&]( int progress )
  353. {
  354. if( aReporter && ( progress % progressDelta ) == 0 )
  355. {
  356. aReporter->SetCurrentProgress( progress / size );
  357. aReporter->KeepRefreshing( false );
  358. }
  359. };
  360. // Generate RTrees for CN_ZONE_LAYER items (in parallel)
  361. //
  362. thread_pool& tp = GetKiCadThreadPool();
  363. std::vector<std::future<size_t>> returns( zitems.size() );
  364. auto cache_zones =
  365. [aReporter]( CN_ZONE_LAYER* aZoneLayer ) -> size_t
  366. {
  367. if( aReporter && aReporter->IsCancelled() )
  368. return 0;
  369. aZoneLayer->BuildRTree();
  370. if( aReporter )
  371. aReporter->AdvanceProgress();
  372. return 1;
  373. };
  374. for( size_t ii = 0; ii < zitems.size(); ++ii )
  375. returns[ii] = tp.submit( cache_zones, zitems[ii] );
  376. for( const std::future<size_t>& retval : returns )
  377. {
  378. std::future_status status;
  379. do
  380. {
  381. if( aReporter )
  382. aReporter->KeepRefreshing();
  383. status = retval.wait_for( std::chrono::milliseconds( 250 ) );
  384. }
  385. while( status != std::future_status::ready );
  386. }
  387. // Add CN_ZONE_LAYERS, tracks, and pads to connectivity
  388. //
  389. int ii = zitems.size();
  390. for( CN_ZONE_LAYER* zitem : zitems )
  391. {
  392. m_itemList.Add( zitem );
  393. m_itemMap[ zitem->Parent() ].Link( zitem );
  394. report( ++ii );
  395. }
  396. for( PCB_TRACK* tv : aBoard->Tracks() )
  397. {
  398. Add( tv );
  399. report( ++ii );
  400. }
  401. for( FOOTPRINT* footprint : aBoard->Footprints() )
  402. {
  403. for( PAD* pad : footprint->Pads() )
  404. {
  405. Add( pad );
  406. report( ++ii );
  407. }
  408. }
  409. if( aReporter )
  410. {
  411. aReporter->SetCurrentProgress( (double) ii / (double) size );
  412. aReporter->KeepRefreshing( false );
  413. }
  414. }
  415. void CN_CONNECTIVITY_ALGO::LocalBuild( const std::vector<BOARD_ITEM*>& aItems )
  416. {
  417. for( BOARD_ITEM* item : aItems )
  418. {
  419. switch( item->Type() )
  420. {
  421. case PCB_TRACE_T:
  422. case PCB_ARC_T:
  423. case PCB_VIA_T:
  424. case PCB_PAD_T:
  425. case PCB_FOOTPRINT_T:
  426. Add( item );
  427. break;
  428. default:
  429. break;
  430. }
  431. }
  432. }
  433. void CN_CONNECTIVITY_ALGO::propagateConnections( BOARD_COMMIT* aCommit, PROPAGATE_MODE aMode )
  434. {
  435. bool skipConflicts = ( aMode == PROPAGATE_MODE::SKIP_CONFLICTS );
  436. wxLogTrace( wxT( "CN" ), wxT( "propagateConnections: propagate skip conflicts? %d" ),
  437. skipConflicts );
  438. for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
  439. {
  440. if( skipConflicts && cluster->IsConflicting() )
  441. {
  442. wxLogTrace( wxT( "CN" ), wxT( "Conflicting nets in cluster %p; skipping update" ),
  443. cluster.get() );
  444. }
  445. else if( cluster->IsOrphaned() )
  446. {
  447. wxLogTrace( wxT( "CN" ), wxT( "Skipping orphaned cluster %p [net: %s]" ),
  448. cluster.get(),
  449. (const char*) cluster->OriginNetName().c_str() );
  450. }
  451. else if( cluster->HasValidNet() )
  452. {
  453. if( cluster->IsConflicting() )
  454. {
  455. wxLogTrace( wxT( "CN" ), wxT( "Conflicting nets in cluster %p; chose %d (%s)" ),
  456. cluster.get(),
  457. cluster->OriginNet(),
  458. cluster->OriginNetName() );
  459. }
  460. // normal cluster: just propagate from the pads
  461. int n_changed = 0;
  462. for( CN_ITEM* item : *cluster )
  463. {
  464. if( item->CanChangeNet() )
  465. {
  466. if( item->Valid() && item->Parent()->GetNetCode() != cluster->OriginNet() )
  467. {
  468. MarkNetAsDirty( item->Parent()->GetNetCode() );
  469. MarkNetAsDirty( cluster->OriginNet() );
  470. if( aCommit )
  471. aCommit->Modify( item->Parent() );
  472. item->Parent()->SetNetCode( cluster->OriginNet() );
  473. n_changed++;
  474. }
  475. }
  476. }
  477. if( n_changed )
  478. {
  479. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p : net : %d %s" ),
  480. cluster.get(),
  481. cluster->OriginNet(),
  482. (const char*) cluster->OriginNetName().c_str() );
  483. }
  484. else
  485. {
  486. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p : nothing to propagate" ),
  487. cluster.get() );
  488. }
  489. }
  490. else
  491. {
  492. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p : connected to unused net" ),
  493. cluster.get() );
  494. }
  495. }
  496. }
  497. void CN_CONNECTIVITY_ALGO::PropagateNets( BOARD_COMMIT* aCommit, PROPAGATE_MODE aMode )
  498. {
  499. m_connClusters = SearchClusters( CSM_PROPAGATE );
  500. propagateConnections( aCommit, aMode );
  501. }
  502. void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID aLayer,
  503. std::vector<int>& aIslands )
  504. {
  505. if( aZone->GetFilledPolysList( aLayer )->IsEmpty() )
  506. return;
  507. aIslands.clear();
  508. Remove( aZone );
  509. Add( aZone );
  510. m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
  511. for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
  512. {
  513. if( cluster->Contains( aZone ) && cluster->IsOrphaned() )
  514. {
  515. for( CN_ITEM* z : *cluster )
  516. {
  517. if( z->Parent() == aZone && z->Layer() == aLayer )
  518. aIslands.push_back( static_cast<CN_ZONE_LAYER*>(z)->SubpolyIndex() );
  519. }
  520. }
  521. }
  522. wxLogTrace( wxT( "CN" ), wxT( "Found %u isolated islands\n" ), (unsigned) aIslands.size() );
  523. }
  524. void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
  525. bool aConnectivityAlreadyRebuilt )
  526. {
  527. int progressDelta = 50;
  528. int ii = 0;
  529. progressDelta = std::max( progressDelta, (int) aZones.size() / 4 );
  530. if( !aConnectivityAlreadyRebuilt )
  531. {
  532. for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
  533. {
  534. Remove( z.m_zone );
  535. Add( z.m_zone );
  536. ii++;
  537. if( m_progressReporter && ( ii % progressDelta ) == 0 )
  538. {
  539. m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
  540. m_progressReporter->KeepRefreshing( false );
  541. }
  542. if( m_progressReporter && m_progressReporter->IsCancelled() )
  543. return;
  544. }
  545. }
  546. m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
  547. for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : aZones )
  548. {
  549. for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
  550. {
  551. if( zone.m_zone->GetFilledPolysList( layer )->IsEmpty() )
  552. continue;
  553. for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
  554. {
  555. if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
  556. {
  557. for( CN_ITEM* z : *cluster )
  558. {
  559. if( z->Parent() == zone.m_zone && z->Layer() == layer )
  560. {
  561. zone.m_islands[layer].push_back(
  562. static_cast<CN_ZONE_LAYER*>( z )->SubpolyIndex() );
  563. }
  564. }
  565. }
  566. }
  567. }
  568. }
  569. }
  570. const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
  571. {
  572. m_ratsnestClusters = SearchClusters( CSM_RATSNEST );
  573. return m_ratsnestClusters;
  574. }
  575. void CN_CONNECTIVITY_ALGO::MarkNetAsDirty( int aNet )
  576. {
  577. if( aNet < 0 )
  578. return;
  579. if( (int) m_dirtyNets.size() <= aNet )
  580. {
  581. int lastNet = m_dirtyNets.size() - 1;
  582. if( lastNet < 0 )
  583. lastNet = 0;
  584. m_dirtyNets.resize( aNet + 1 );
  585. for( int i = lastNet; i < aNet + 1; i++ )
  586. m_dirtyNets[i] = true;
  587. }
  588. m_dirtyNets[aNet] = true;
  589. }
  590. void CN_VISITOR::checkZoneItemConnection( CN_ZONE_LAYER* aZoneLayer, CN_ITEM* aItem )
  591. {
  592. if( aZoneLayer->Net() != aItem->Net() && !aItem->CanChangeNet() )
  593. return;
  594. PCB_LAYER_ID layer = aZoneLayer->GetLayer();
  595. if( !aItem->Parent()->IsOnLayer( layer ) )
  596. return;
  597. auto connect =
  598. [&]()
  599. {
  600. aZoneLayer->Connect( aItem );
  601. aItem->Connect( aZoneLayer );
  602. };
  603. // Try quick checks first...
  604. for( int i = 0; i < aItem->AnchorCount(); ++i )
  605. {
  606. if( aZoneLayer->ContainsPoint( aItem->GetAnchor( i ) ) )
  607. {
  608. connect();
  609. return;
  610. }
  611. }
  612. if( aItem->Parent()->Type() == PCB_VIA_T || aItem->Parent()->Type() == PCB_PAD_T )
  613. {
  614. // As long as the pad/via crosses the zone layer, check for the full effective shape
  615. // We check for the overlapping layers above
  616. if( aZoneLayer->Collide( aItem->Parent()->GetEffectiveShape( layer, FLASHING::ALWAYS_FLASHED ).get() ) )
  617. connect();
  618. return;
  619. }
  620. if( aZoneLayer->Collide( aItem->Parent()->GetEffectiveShape( layer ).get() ) )
  621. connect();
  622. }
  623. void CN_VISITOR::checkZoneZoneConnection( CN_ZONE_LAYER* aZoneLayerA, CN_ZONE_LAYER* aZoneLayerB )
  624. {
  625. const ZONE* zoneA = static_cast<const ZONE*>( aZoneLayerA->Parent() );
  626. const ZONE* zoneB = static_cast<const ZONE*>( aZoneLayerB->Parent() );
  627. if( aZoneLayerB->Net() != aZoneLayerA->Net() )
  628. return; // we only test zones belonging to the same net
  629. const BOX2I& boxA = aZoneLayerA->BBox();
  630. const BOX2I& boxB = aZoneLayerB->BBox();
  631. PCB_LAYER_ID layer = aZoneLayerA->GetLayer();
  632. if( aZoneLayerB->GetLayer() != layer )
  633. return;
  634. if( !boxA.Intersects( boxB ) )
  635. return;
  636. const SHAPE_LINE_CHAIN& outline =
  637. zoneA->GetFilledPolysList( layer )->COutline( aZoneLayerA->SubpolyIndex() );
  638. for( int i = 0; i < outline.PointCount(); i++ )
  639. {
  640. if( !boxB.Contains( outline.CPoint( i ) ) )
  641. continue;
  642. if( aZoneLayerB->ContainsPoint( outline.CPoint( i ) ) )
  643. {
  644. aZoneLayerA->Connect( aZoneLayerB );
  645. aZoneLayerB->Connect( aZoneLayerA );
  646. return;
  647. }
  648. }
  649. const SHAPE_LINE_CHAIN& outline2 =
  650. zoneB->GetFilledPolysList( layer )->COutline( aZoneLayerB->SubpolyIndex() );
  651. for( int i = 0; i < outline2.PointCount(); i++ )
  652. {
  653. if( !boxA.Contains( outline2.CPoint( i ) ) )
  654. continue;
  655. if( aZoneLayerA->ContainsPoint( outline2.CPoint( i ) ) )
  656. {
  657. aZoneLayerA->Connect( aZoneLayerB );
  658. aZoneLayerB->Connect( aZoneLayerA );
  659. return;
  660. }
  661. }
  662. }
  663. bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
  664. {
  665. const BOARD_CONNECTED_ITEM* parentA = aCandidate->Parent();
  666. const BOARD_CONNECTED_ITEM* parentB = m_item->Parent();
  667. if( !aCandidate->Valid() || !m_item->Valid() )
  668. return true;
  669. if( parentA == parentB )
  670. return true;
  671. // If both m_item and aCandidate are marked dirty, they will both be searched
  672. // Since we are reciprocal in our connection, we arbitrarily pick one of the connections
  673. // to conduct the expensive search
  674. if( aCandidate->Dirty() && aCandidate < m_item )
  675. return true;
  676. // We should handle zone-zone connection separately
  677. if ( parentA->Type() == PCB_ZONE_T && parentB->Type() == PCB_ZONE_T )
  678. {
  679. checkZoneZoneConnection( static_cast<CN_ZONE_LAYER*>( m_item ),
  680. static_cast<CN_ZONE_LAYER*>( aCandidate ) );
  681. return true;
  682. }
  683. if( parentA->Type() == PCB_ZONE_T )
  684. {
  685. checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( aCandidate ), m_item );
  686. return true;
  687. }
  688. if( parentB->Type() == PCB_ZONE_T )
  689. {
  690. checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( m_item ), aCandidate );
  691. return true;
  692. }
  693. LSET commonLayers = parentA->GetLayerSet() & parentB->GetLayerSet();
  694. for( PCB_LAYER_ID layer : commonLayers.Seq() )
  695. {
  696. FLASHING flashingA = FLASHING::NEVER_FLASHED;
  697. FLASHING flashingB = FLASHING::NEVER_FLASHED;
  698. if( const PAD* pad = dyn_cast<const PAD*>( parentA ) )
  699. {
  700. if( !pad->GetRemoveUnconnected() || ( ( layer == F_Cu || layer == B_Cu ) && pad->GetKeepTopBottom() ) )
  701. flashingA = FLASHING::ALWAYS_FLASHED;
  702. }
  703. else if( const PCB_VIA* via = dyn_cast<const PCB_VIA*>( parentA ) )
  704. {
  705. if( !via->GetRemoveUnconnected() || ( ( layer == F_Cu || layer == B_Cu ) && via->GetKeepTopBottom() ) )
  706. flashingA = FLASHING::ALWAYS_FLASHED;
  707. }
  708. if( const PAD* pad = dyn_cast<const PAD*>( parentB ) )
  709. {
  710. if( !pad->GetRemoveUnconnected() || ( ( layer == F_Cu || layer == B_Cu ) && pad->GetKeepTopBottom() ) )
  711. flashingB = FLASHING::ALWAYS_FLASHED;
  712. }
  713. else if( const PCB_VIA* via = dyn_cast<const PCB_VIA*>( parentB ) )
  714. {
  715. if( !via->GetRemoveUnconnected() || ( ( layer == F_Cu || layer == B_Cu ) && via->GetKeepTopBottom() ) )
  716. flashingB = FLASHING::ALWAYS_FLASHED;
  717. }
  718. if( parentA->GetEffectiveShape( layer, flashingA )->Collide(
  719. parentB->GetEffectiveShape( layer, flashingB ).get() ) )
  720. {
  721. m_item->Connect( aCandidate );
  722. aCandidate->Connect( m_item );
  723. return true;
  724. }
  725. }
  726. return true;
  727. };
  728. void CN_CONNECTIVITY_ALGO::Clear()
  729. {
  730. m_ratsnestClusters.clear();
  731. m_connClusters.clear();
  732. m_itemMap.clear();
  733. m_itemList.Clear();
  734. }
  735. void CN_CONNECTIVITY_ALGO::SetProgressReporter( PROGRESS_REPORTER* aReporter )
  736. {
  737. m_progressReporter = aReporter;
  738. }