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.

938 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 The 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 <pcb_shape.h>
  35. #include <wx/log.h>
  36. #ifdef PROFILE
  37. #include <core/profile.h>
  38. #endif
  39. bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
  40. {
  41. markItemNetAsDirty( aItem );
  42. switch( aItem->Type() )
  43. {
  44. case PCB_FOOTPRINT_T:
  45. for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
  46. {
  47. m_itemMap[pad].MarkItemsAsInvalid();
  48. m_itemMap.erase( pad );
  49. }
  50. m_itemList.SetDirty( true );
  51. break;
  52. case PCB_PAD_T:
  53. case PCB_TRACE_T:
  54. case PCB_ARC_T:
  55. case PCB_VIA_T:
  56. case PCB_ZONE_T:
  57. case PCB_SHAPE_T:
  58. m_itemMap[aItem].MarkItemsAsInvalid();
  59. m_itemMap.erase ( aItem );
  60. m_itemList.SetDirty( true );
  61. break;
  62. default:
  63. return false;
  64. }
  65. // Once we delete an item, it may connect between lists, so mark both as potentially invalid
  66. m_itemList.SetHasInvalid( true );
  67. return true;
  68. }
  69. void CN_CONNECTIVITY_ALGO::markItemNetAsDirty( const BOARD_ITEM* aItem )
  70. {
  71. if( aItem->IsConnected() )
  72. {
  73. const BOARD_CONNECTED_ITEM* citem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
  74. MarkNetAsDirty( citem->GetNetCode() );
  75. }
  76. else
  77. {
  78. if( aItem->Type() == PCB_FOOTPRINT_T )
  79. {
  80. const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
  81. for( PAD* pad : footprint->Pads() )
  82. MarkNetAsDirty( pad->GetNetCode() );
  83. }
  84. }
  85. }
  86. bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
  87. {
  88. if( !aItem->IsOnCopperLayer() )
  89. return false;
  90. auto alreadyAdded =
  91. [this]( BOARD_ITEM* item )
  92. {
  93. auto it = m_itemMap.find( item );
  94. if( it == m_itemMap.end() )
  95. return false;
  96. // Don't be fooled by an empty ITEM_MAP_ENTRY auto-created by operator[].
  97. return !it->second.GetItems().empty();
  98. };
  99. switch( aItem->Type() )
  100. {
  101. case PCB_NETINFO_T:
  102. MarkNetAsDirty( static_cast<NETINFO_ITEM*>( aItem )->GetNetCode() );
  103. break;
  104. case PCB_FOOTPRINT_T:
  105. {
  106. if( static_cast<FOOTPRINT*>( aItem )->GetAttributes() & FP_JUST_ADDED )
  107. return false;
  108. for( PAD* pad : static_cast<FOOTPRINT*>( aItem )->Pads() )
  109. {
  110. if( alreadyAdded( pad ) )
  111. return false;
  112. add( m_itemList, pad );
  113. }
  114. break;
  115. }
  116. case PCB_PAD_T:
  117. {
  118. if( FOOTPRINT* fp = aItem->GetParentFootprint() )
  119. {
  120. if( fp->GetAttributes() & FP_JUST_ADDED )
  121. return false;
  122. }
  123. if( alreadyAdded( aItem ) )
  124. return false;
  125. add( m_itemList, static_cast<PAD*>( aItem ) );
  126. break;
  127. }
  128. case PCB_TRACE_T:
  129. if( alreadyAdded( aItem ) )
  130. return false;
  131. add( m_itemList, static_cast<PCB_TRACK*>( aItem ) );
  132. break;
  133. case PCB_ARC_T:
  134. if( alreadyAdded( aItem ) )
  135. return false;
  136. add( m_itemList, static_cast<PCB_ARC*>( aItem ) );
  137. break;
  138. case PCB_VIA_T:
  139. if( alreadyAdded( aItem ) )
  140. return false;
  141. add( m_itemList, static_cast<PCB_VIA*>( aItem ) );
  142. break;
  143. case PCB_SHAPE_T:
  144. if( alreadyAdded( aItem ) )
  145. return false;
  146. if( !IsCopperLayer( aItem->GetLayer() ) )
  147. return false;
  148. add( m_itemList, static_cast<PCB_SHAPE*>( aItem ) );
  149. break;
  150. case PCB_ZONE_T:
  151. {
  152. ZONE* zone = static_cast<ZONE*>( aItem );
  153. if( alreadyAdded( aItem ) )
  154. return false;
  155. m_itemMap[zone] = ITEM_MAP_ENTRY();
  156. // Don't check for connections on layers that only exist in the zone but
  157. // were disabled in the board
  158. BOARD* board = zone->GetBoard();
  159. LSET layerset = board->GetEnabledLayers() & zone->GetLayerSet();
  160. layerset.RunOnLayers(
  161. [&]( PCB_LAYER_ID layer )
  162. {
  163. for( CN_ITEM* zitem : m_itemList.Add( zone, layer ) )
  164. m_itemMap[zone].Link( zitem );
  165. } );
  166. }
  167. break;
  168. default:
  169. return false;
  170. }
  171. markItemNetAsDirty( aItem );
  172. return true;
  173. }
  174. void CN_CONNECTIVITY_ALGO::RemoveInvalidRefs()
  175. {
  176. for( CN_ITEM* item : m_itemList )
  177. item->RemoveInvalidRefs();
  178. }
  179. void CN_CONNECTIVITY_ALGO::searchConnections()
  180. {
  181. std::lock_guard lock( m_mutex );
  182. #ifdef PROFILE
  183. PROF_TIMER garbage_collection( "garbage-collection" );
  184. #endif
  185. std::vector<CN_ITEM*> garbage;
  186. garbage.reserve( 1024 );
  187. m_parentConnectivityData->RemoveInvalidRefs();
  188. if( m_isLocal )
  189. m_globalConnectivityData->RemoveInvalidRefs();
  190. m_itemList.RemoveInvalidItems( garbage );
  191. for( CN_ITEM* item : garbage )
  192. delete item;
  193. #ifdef PROFILE
  194. garbage_collection.Show();
  195. PROF_TIMER search_basic( "search-basic" );
  196. #endif
  197. thread_pool& tp = GetKiCadThreadPool();
  198. std::vector<CN_ITEM*> dirtyItems;
  199. std::copy_if( m_itemList.begin(), m_itemList.end(), std::back_inserter( dirtyItems ),
  200. [] ( CN_ITEM* aItem )
  201. {
  202. return aItem->Dirty();
  203. } );
  204. if( m_progressReporter )
  205. {
  206. m_progressReporter->SetMaxProgress( dirtyItems.size() );
  207. if( !m_progressReporter->KeepRefreshing() )
  208. return;
  209. }
  210. if( m_itemList.IsDirty() )
  211. {
  212. std::vector<std::future<size_t>> returns( dirtyItems.size() );
  213. auto conn_lambda =
  214. [&dirtyItems]( size_t aItem, CN_LIST* aItemList,
  215. PROGRESS_REPORTER* aReporter) -> size_t
  216. {
  217. if( aReporter && aReporter->IsCancelled() )
  218. return 0;
  219. CN_VISITOR visitor( dirtyItems[aItem] );
  220. aItemList->FindNearby( dirtyItems[aItem], visitor );
  221. if( aReporter )
  222. aReporter->AdvanceProgress();
  223. return 1;
  224. };
  225. for( size_t ii = 0; ii < dirtyItems.size(); ++ii )
  226. returns[ii] = tp.submit( conn_lambda, ii, &m_itemList, m_progressReporter );
  227. for( const std::future<size_t>& ret : returns )
  228. {
  229. // Here we balance returns with a 250ms timeout to allow UI updating
  230. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  231. while( status != std::future_status::ready )
  232. {
  233. if( m_progressReporter )
  234. m_progressReporter->KeepRefreshing();
  235. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  236. }
  237. }
  238. if( m_progressReporter )
  239. m_progressReporter->KeepRefreshing();
  240. }
  241. #ifdef PROFILE
  242. search_basic.Show();
  243. #endif
  244. m_itemList.ClearDirtyFlags();
  245. }
  246. const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode )
  247. {
  248. return SearchClusters( aMode, ( aMode == CSM_PROPAGATE ), -1 );
  249. }
  250. const CN_CONNECTIVITY_ALGO::CLUSTERS
  251. CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode, bool aExcludeZones, int aSingleNet )
  252. {
  253. bool withinAnyNet = ( aMode != CSM_PROPAGATE );
  254. std::deque<CN_ITEM*> Q;
  255. std::set<CN_ITEM*> item_set;
  256. CLUSTERS clusters;
  257. if( m_itemList.IsDirty() )
  258. searchConnections();
  259. std::set<CN_ITEM*> visited;
  260. auto addToSearchList =
  261. [&item_set, withinAnyNet, aSingleNet, &aExcludeZones]( CN_ITEM *aItem )
  262. {
  263. if( withinAnyNet && aItem->Net() <= 0 )
  264. return;
  265. if( !aItem->Valid() )
  266. return;
  267. if( aSingleNet >=0 && aItem->Net() != aSingleNet )
  268. return;
  269. if( aExcludeZones && aItem->Parent()->Type() == PCB_ZONE_T )
  270. return;
  271. item_set.insert( aItem );
  272. };
  273. std::for_each( m_itemList.begin(), m_itemList.end(), addToSearchList );
  274. if( m_progressReporter && m_progressReporter->IsCancelled() )
  275. return CLUSTERS();
  276. while( !item_set.empty() )
  277. {
  278. std::shared_ptr<CN_CLUSTER> cluster = std::make_shared<CN_CLUSTER>();
  279. CN_ITEM* root;
  280. auto it = item_set.begin();
  281. while( it != item_set.end() && visited.contains( *it ) )
  282. it = item_set.erase( item_set.begin() );
  283. if( it == item_set.end() )
  284. break;
  285. root = *it;
  286. visited.insert( root );
  287. Q.clear();
  288. Q.push_back( root );
  289. while( Q.size() )
  290. {
  291. CN_ITEM* current = Q.front();
  292. Q.pop_front();
  293. cluster->Add( current );
  294. for( CN_ITEM* n : current->ConnectedItems() )
  295. {
  296. if( withinAnyNet && n->Net() != root->Net() )
  297. continue;
  298. if( aExcludeZones && n->Parent()->Type() == PCB_ZONE_T )
  299. continue;
  300. if( !visited.contains( n ) && n->Valid() )
  301. {
  302. visited.insert( n );
  303. Q.push_back( n );
  304. }
  305. }
  306. }
  307. clusters.push_back( cluster );
  308. }
  309. if( m_progressReporter && m_progressReporter->IsCancelled() )
  310. return CLUSTERS();
  311. std::sort( clusters.begin(), clusters.end(),
  312. []( const std::shared_ptr<CN_CLUSTER>& a, const std::shared_ptr<CN_CLUSTER>& b )
  313. {
  314. return a->OriginNet() < b->OriginNet();
  315. } );
  316. return clusters;
  317. }
  318. void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
  319. {
  320. // Generate CN_ZONE_LAYERs for each island on each layer of each zone
  321. //
  322. std::vector<CN_ZONE_LAYER*> zitems;
  323. for( ZONE* zone : aBoard->Zones() )
  324. {
  325. if( zone->IsOnCopperLayer() )
  326. {
  327. m_itemMap[zone] = ITEM_MAP_ENTRY();
  328. markItemNetAsDirty( zone );
  329. // Don't check for connections on layers that only exist in the zone but
  330. // were disabled in the board
  331. BOARD* board = zone->GetBoard();
  332. LSET layerset = board->GetEnabledLayers() & zone->GetLayerSet() & LSET::AllCuMask();
  333. layerset.RunOnLayers(
  334. [&]( PCB_LAYER_ID layer )
  335. {
  336. for( int j = 0; j < zone->GetFilledPolysList( layer )->OutlineCount(); j++ )
  337. zitems.push_back( new CN_ZONE_LAYER( zone, layer, j ) );
  338. } );
  339. }
  340. }
  341. // Setup progress metrics
  342. //
  343. int progressDelta = 50;
  344. double size = 0.0;
  345. size += zitems.size(); // Once for building RTrees
  346. size += zitems.size(); // Once for adding to connectivity
  347. size += aBoard->Tracks().size();
  348. size += aBoard->Drawings().size();
  349. for( FOOTPRINT* footprint : aBoard->Footprints() )
  350. size += footprint->Pads().size();
  351. size *= 1.5; // Our caller gets the other third of the progress bar
  352. progressDelta = std::max( progressDelta, (int) size / 4 );
  353. auto report =
  354. [&]( int progress )
  355. {
  356. if( aReporter && ( progress % progressDelta ) == 0 )
  357. {
  358. aReporter->SetCurrentProgress( progress / size );
  359. aReporter->KeepRefreshing( false );
  360. }
  361. };
  362. // Generate RTrees for CN_ZONE_LAYER items (in parallel)
  363. //
  364. thread_pool& tp = GetKiCadThreadPool();
  365. std::vector<std::future<size_t>> returns( zitems.size() );
  366. auto cache_zones =
  367. [aReporter]( CN_ZONE_LAYER* aZoneLayer ) -> size_t
  368. {
  369. if( aReporter && aReporter->IsCancelled() )
  370. return 0;
  371. aZoneLayer->BuildRTree();
  372. if( aReporter )
  373. aReporter->AdvanceProgress();
  374. return 1;
  375. };
  376. for( size_t ii = 0; ii < zitems.size(); ++ii )
  377. returns[ii] = tp.submit( cache_zones, zitems[ii] );
  378. for( const std::future<size_t>& ret : returns )
  379. {
  380. std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  381. while( status != std::future_status::ready )
  382. {
  383. if( aReporter )
  384. aReporter->KeepRefreshing();
  385. status = ret.wait_for( std::chrono::milliseconds( 250 ) );
  386. }
  387. }
  388. // Add CN_ZONE_LAYERS, tracks, and pads to connectivity
  389. //
  390. int ii = zitems.size();
  391. for( CN_ZONE_LAYER* zitem : zitems )
  392. {
  393. m_itemList.Add( zitem );
  394. m_itemMap[ zitem->Parent() ].Link( zitem );
  395. report( ++ii );
  396. }
  397. for( PCB_TRACK* tv : aBoard->Tracks() )
  398. {
  399. Add( tv );
  400. report( ++ii );
  401. }
  402. for( FOOTPRINT* footprint : aBoard->Footprints() )
  403. {
  404. for( PAD* pad : footprint->Pads() )
  405. {
  406. Add( pad );
  407. report( ++ii );
  408. }
  409. }
  410. for( BOARD_ITEM* drawing : aBoard->Drawings() )
  411. {
  412. if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( drawing ) )
  413. {
  414. if( shape->IsOnCopperLayer() )
  415. Add( shape );
  416. }
  417. report( ++ii );
  418. }
  419. if( aReporter )
  420. {
  421. aReporter->SetCurrentProgress( (double) ii / (double) size );
  422. aReporter->KeepRefreshing( false );
  423. }
  424. }
  425. void CN_CONNECTIVITY_ALGO::LocalBuild( std::shared_ptr<CONNECTIVITY_DATA> aGlobalConnectivity,
  426. const std::vector<BOARD_ITEM*>& aLocalItems )
  427. {
  428. m_isLocal = true;
  429. m_globalConnectivityData = aGlobalConnectivity;
  430. for( BOARD_ITEM* item : aLocalItems )
  431. {
  432. switch( item->Type() )
  433. {
  434. case PCB_TRACE_T:
  435. case PCB_ARC_T:
  436. case PCB_VIA_T:
  437. case PCB_PAD_T:
  438. case PCB_FOOTPRINT_T:
  439. case PCB_SHAPE_T:
  440. Add( item );
  441. break;
  442. default:
  443. break;
  444. }
  445. }
  446. }
  447. void CN_CONNECTIVITY_ALGO::propagateConnections( BOARD_COMMIT* aCommit )
  448. {
  449. for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
  450. {
  451. if( cluster->IsConflicting() )
  452. {
  453. // Conflicting pads in cluster: we don't know the user's intent so best to do
  454. // nothing.
  455. wxLogTrace( wxT( "CN" ), wxT( "Conflicting pads in cluster %p; skipping propagation" ),
  456. cluster.get() );
  457. }
  458. else if( cluster->HasValidNet() )
  459. {
  460. // Propagate from the origin (will be a pad if there are any, or another item if
  461. // there are no pads).
  462. int n_changed = 0;
  463. for( CN_ITEM* item : *cluster )
  464. {
  465. if( item->Valid() && item->CanChangeNet()
  466. && 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. if( n_changed )
  477. {
  478. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p: net: %d %s" ),
  479. cluster.get(),
  480. cluster->OriginNet(),
  481. (const char*) cluster->OriginNetName().c_str() );
  482. }
  483. else
  484. {
  485. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p: no changeable items to propagate to" ),
  486. cluster.get() );
  487. }
  488. }
  489. else
  490. {
  491. wxLogTrace( wxT( "CN" ), wxT( "Cluster %p: connected to unused net" ),
  492. cluster.get() );
  493. }
  494. }
  495. }
  496. void CN_CONNECTIVITY_ALGO::PropagateNets( BOARD_COMMIT* aCommit )
  497. {
  498. m_connClusters = SearchClusters( CSM_PROPAGATE );
  499. propagateConnections( aCommit );
  500. }
  501. void CN_CONNECTIVITY_ALGO::FillIsolatedIslandsMap(
  502. std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>>& aMap,
  503. bool aConnectivityAlreadyRebuilt )
  504. {
  505. int progressDelta = 50;
  506. int ii = 0;
  507. progressDelta = std::max( progressDelta, (int) aMap.size() / 4 );
  508. if( !aConnectivityAlreadyRebuilt )
  509. {
  510. for( const auto& [ zone, islands ] : aMap )
  511. {
  512. Remove( zone );
  513. Add( zone );
  514. ii++;
  515. if( m_progressReporter && ( ii % progressDelta ) == 0 )
  516. {
  517. m_progressReporter->SetCurrentProgress( (double) ii / (double) aMap.size() );
  518. m_progressReporter->KeepRefreshing( false );
  519. }
  520. if( m_progressReporter && m_progressReporter->IsCancelled() )
  521. return;
  522. }
  523. }
  524. m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
  525. for( auto& [ zone, zoneIslands ] : aMap )
  526. {
  527. for( auto& [ layer, layerIslands ] : zoneIslands )
  528. {
  529. if( zone->GetFilledPolysList( layer )->IsEmpty() )
  530. continue;
  531. for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
  532. {
  533. for( CN_ITEM* item : *cluster )
  534. {
  535. if( item->Parent() == zone && item->GetBoardLayer() == layer )
  536. {
  537. CN_ZONE_LAYER* z = static_cast<CN_ZONE_LAYER*>( item );
  538. if( cluster->IsOrphaned() )
  539. layerIslands.m_IsolatedOutlines.push_back( z->SubpolyIndex() );
  540. else if( z->HasSingleConnection() )
  541. layerIslands.m_SingleConnectionOutlines.push_back( z->SubpolyIndex() );
  542. }
  543. }
  544. }
  545. }
  546. }
  547. }
  548. const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
  549. {
  550. m_ratsnestClusters = SearchClusters( CSM_RATSNEST );
  551. return m_ratsnestClusters;
  552. }
  553. void CN_CONNECTIVITY_ALGO::MarkNetAsDirty( int aNet )
  554. {
  555. if( aNet < 0 )
  556. return;
  557. if( (int) m_dirtyNets.size() <= aNet )
  558. {
  559. int lastNet = m_dirtyNets.size() - 1;
  560. if( lastNet < 0 )
  561. lastNet = 0;
  562. m_dirtyNets.resize( aNet + 1 );
  563. for( int i = lastNet; i < aNet + 1; i++ )
  564. m_dirtyNets[i] = true;
  565. }
  566. m_dirtyNets[aNet] = true;
  567. }
  568. void CN_VISITOR::checkZoneItemConnection( CN_ZONE_LAYER* aZoneLayer, CN_ITEM* aItem )
  569. {
  570. PCB_LAYER_ID layer = aZoneLayer->GetLayer();
  571. BOARD_CONNECTED_ITEM* item = aItem->Parent();
  572. if( !item->IsOnLayer( layer ) )
  573. return;
  574. auto connect =
  575. [&]()
  576. {
  577. aZoneLayer->Connect( aItem );
  578. aItem->Connect( aZoneLayer );
  579. };
  580. // Try quick checks first...
  581. if( item->Type() == PCB_PAD_T )
  582. {
  583. PAD* pad = static_cast<PAD*>( item );
  584. if( pad->ConditionallyFlashed( layer )
  585. && pad->GetZoneLayerOverride( layer ) == ZLO_FORCE_NO_ZONE_CONNECTION )
  586. {
  587. return;
  588. }
  589. }
  590. else if( item->Type() == PCB_VIA_T )
  591. {
  592. PCB_VIA* via = static_cast<PCB_VIA*>( item );
  593. if( via->ConditionallyFlashed( layer )
  594. && via->GetZoneLayerOverride( layer ) == ZLO_FORCE_NO_ZONE_CONNECTION )
  595. {
  596. return;
  597. }
  598. }
  599. for( int i = 0; i < aItem->AnchorCount(); ++i )
  600. {
  601. if( aZoneLayer->ContainsPoint( aItem->GetAnchor( i ) ) )
  602. {
  603. connect();
  604. return;
  605. }
  606. }
  607. if( item->Type() == PCB_VIA_T || item->Type() == PCB_PAD_T )
  608. {
  609. // As long as the pad/via crosses the zone layer, check for the full effective shape
  610. // We check for the overlapping layers above
  611. if( aZoneLayer->Collide( item->GetEffectiveShape( layer, FLASHING::ALWAYS_FLASHED ).get() ) )
  612. connect();
  613. return;
  614. }
  615. if( aZoneLayer->Collide( item->GetEffectiveShape( layer ).get() ) )
  616. connect();
  617. }
  618. void CN_VISITOR::checkZoneZoneConnection( CN_ZONE_LAYER* aZoneLayerA, CN_ZONE_LAYER* aZoneLayerB )
  619. {
  620. const ZONE* zoneA = static_cast<const ZONE*>( aZoneLayerA->Parent() );
  621. const ZONE* zoneB = static_cast<const ZONE*>( aZoneLayerB->Parent() );
  622. const BOX2I& boxA = aZoneLayerA->BBox();
  623. const BOX2I& boxB = aZoneLayerB->BBox();
  624. PCB_LAYER_ID layer = aZoneLayerA->GetLayer();
  625. if( aZoneLayerB->GetLayer() != layer )
  626. return;
  627. if( !boxA.Intersects( boxB ) )
  628. return;
  629. const SHAPE_LINE_CHAIN& outline =
  630. zoneA->GetFilledPolysList( layer )->COutline( aZoneLayerA->SubpolyIndex() );
  631. for( int i = 0; i < outline.PointCount(); i++ )
  632. {
  633. if( !boxB.Contains( outline.CPoint( i ) ) )
  634. continue;
  635. if( aZoneLayerB->ContainsPoint( outline.CPoint( i ) ) )
  636. {
  637. aZoneLayerA->Connect( aZoneLayerB );
  638. aZoneLayerB->Connect( aZoneLayerA );
  639. return;
  640. }
  641. }
  642. const SHAPE_LINE_CHAIN& outline2 =
  643. zoneB->GetFilledPolysList( layer )->COutline( aZoneLayerB->SubpolyIndex() );
  644. for( int i = 0; i < outline2.PointCount(); i++ )
  645. {
  646. if( !boxA.Contains( outline2.CPoint( i ) ) )
  647. continue;
  648. if( aZoneLayerA->ContainsPoint( outline2.CPoint( i ) ) )
  649. {
  650. aZoneLayerA->Connect( aZoneLayerB );
  651. aZoneLayerB->Connect( aZoneLayerA );
  652. return;
  653. }
  654. }
  655. }
  656. bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
  657. {
  658. const BOARD_CONNECTED_ITEM* parentA = aCandidate->Parent();
  659. const BOARD_CONNECTED_ITEM* parentB = m_item->Parent();
  660. if( !aCandidate->Valid() || !m_item->Valid() )
  661. return true;
  662. if( parentA == parentB )
  663. return true;
  664. // Don't connect items in different nets that can't be changed
  665. if( !aCandidate->CanChangeNet() && !m_item->CanChangeNet() && aCandidate->Net() != m_item->Net() )
  666. return true;
  667. // If both m_item and aCandidate are marked dirty, they will both be searched
  668. // Since we are reciprocal in our connection, we arbitrarily pick one of the connections
  669. // to conduct the expensive search
  670. if( aCandidate->Dirty() && aCandidate < m_item )
  671. return true;
  672. // We should handle zone-zone connection separately
  673. if ( parentA->Type() == PCB_ZONE_T && parentB->Type() == PCB_ZONE_T )
  674. {
  675. checkZoneZoneConnection( static_cast<CN_ZONE_LAYER*>( m_item ),
  676. static_cast<CN_ZONE_LAYER*>( aCandidate ) );
  677. return true;
  678. }
  679. if( parentA->Type() == PCB_ZONE_T )
  680. {
  681. checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( aCandidate ), m_item );
  682. return true;
  683. }
  684. if( parentB->Type() == PCB_ZONE_T )
  685. {
  686. checkZoneItemConnection( static_cast<CN_ZONE_LAYER*>( m_item ), aCandidate );
  687. return true;
  688. }
  689. LSET commonLayers = parentA->GetLayerSet() & parentB->GetLayerSet();
  690. for( size_t ii = 0; ii < commonLayers.size(); ++ii )
  691. {
  692. if( commonLayers.test( ii ) )
  693. {
  694. PCB_LAYER_ID layer = PCB_LAYER_ID( ii );
  695. FLASHING flashingA = FLASHING::NEVER_FLASHED;
  696. FLASHING flashingB = FLASHING::NEVER_FLASHED;
  697. if( parentA->Type() == PCB_PAD_T )
  698. {
  699. if( !static_cast<const PAD*>( parentA )->ConditionallyFlashed( layer ) )
  700. flashingA = FLASHING::ALWAYS_FLASHED;
  701. }
  702. else if( parentA->Type() == PCB_VIA_T )
  703. {
  704. if( !static_cast<const PCB_VIA*>( parentA )->ConditionallyFlashed( layer ) )
  705. flashingA = FLASHING::ALWAYS_FLASHED;
  706. }
  707. if( parentB->Type() == PCB_PAD_T )
  708. {
  709. if( !static_cast<const PAD*>( parentB )->ConditionallyFlashed( layer ) )
  710. flashingB = FLASHING::ALWAYS_FLASHED;
  711. }
  712. else if( parentB->Type() == PCB_VIA_T )
  713. {
  714. if( !static_cast<const PCB_VIA*>( parentB )->ConditionallyFlashed( layer ) )
  715. flashingB = FLASHING::ALWAYS_FLASHED;
  716. }
  717. if( parentA->GetEffectiveShape( layer, flashingA )->Collide(
  718. parentB->GetEffectiveShape( layer, flashingB ).get() ) )
  719. {
  720. m_item->Connect( aCandidate );
  721. aCandidate->Connect( m_item );
  722. return true;
  723. }
  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. }