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.

1028 lines
25 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016-2018 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. #include <connectivity_algo.h>
  25. #include <widgets/progress_reporter.h>
  26. #include <geometry/geometry_utils.h>
  27. #include <thread>
  28. #include <mutex>
  29. #ifdef PROFILE
  30. #include <profile.h>
  31. #endif
  32. #ifdef USE_OPENMP
  33. #include <omp.h>
  34. #endif /* USE_OPENMP */
  35. using namespace std::placeholders;
  36. bool operator<( const CN_ANCHOR_PTR& a, const CN_ANCHOR_PTR& b )
  37. {
  38. if( a->Pos().x == b->Pos().x )
  39. return a->Pos().y < b->Pos().y;
  40. else
  41. return a->Pos().x < b->Pos().x;
  42. }
  43. bool CN_ANCHOR::IsDirty() const
  44. {
  45. return m_item->Dirty();
  46. }
  47. CN_CLUSTER::CN_CLUSTER()
  48. {
  49. m_items.reserve( 64 );
  50. m_originPad = nullptr;
  51. m_originNet = -1;
  52. m_conflicting = false;
  53. }
  54. CN_CLUSTER::~CN_CLUSTER()
  55. {
  56. }
  57. wxString CN_CLUSTER::OriginNetName() const
  58. {
  59. if( !m_originPad || !m_originPad->Valid() )
  60. return "<none>";
  61. else
  62. return m_originPad->Parent()->GetNetname();
  63. }
  64. bool CN_CLUSTER::Contains( const CN_ITEM* aItem )
  65. {
  66. return std::find( m_items.begin(), m_items.end(), aItem ) != m_items.end();
  67. }
  68. bool CN_CLUSTER::Contains( const BOARD_CONNECTED_ITEM* aItem )
  69. {
  70. for( auto item : m_items )
  71. {
  72. if( item->Valid() && item->Parent() == aItem )
  73. return true;
  74. }
  75. return false;
  76. }
  77. void CN_ITEM::Dump()
  78. {
  79. printf(" valid: %d, connected: \n", !!Valid());
  80. for( auto i : m_connected )
  81. {
  82. TRACK* t = static_cast<TRACK*>( i->Parent() );
  83. printf( " - %p %d\n", t, t->Type() );
  84. }
  85. }
  86. void CN_CLUSTER::Dump()
  87. {
  88. for( auto item : m_items )
  89. {
  90. wxLogTrace( "CN", " - item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
  91. item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
  92. printf( "- item : %p bitem : %p type : %d inet %s\n", item, item->Parent(),
  93. item->Parent()->Type(), (const char*) item->Parent()->GetNetname().c_str() );
  94. item->Dump();
  95. }
  96. }
  97. void CN_CLUSTER::Add( CN_ITEM* item )
  98. {
  99. m_items.push_back( item );
  100. if( m_originNet < 0 )
  101. {
  102. m_originNet = item->Net();
  103. }
  104. if( item->Parent()->Type() == PCB_PAD_T )
  105. {
  106. if( !m_originPad )
  107. {
  108. m_originPad = item;
  109. m_originNet = item->Net();
  110. }
  111. if( m_originPad && item->Net() != m_originNet )
  112. {
  113. m_conflicting = true;
  114. }
  115. }
  116. }
  117. CN_CONNECTIVITY_ALGO::CN_CONNECTIVITY_ALGO()
  118. {
  119. }
  120. CN_CONNECTIVITY_ALGO::~CN_CONNECTIVITY_ALGO()
  121. {
  122. Clear();
  123. }
  124. bool CN_CONNECTIVITY_ALGO::Remove( BOARD_ITEM* aItem )
  125. {
  126. markItemNetAsDirty( aItem );
  127. switch( aItem->Type() )
  128. {
  129. case PCB_MODULE_T:
  130. for( auto pad : static_cast<MODULE*>( aItem ) -> Pads() )
  131. {
  132. m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( pad ) ].MarkItemsAsInvalid();
  133. m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( pad ) );
  134. }
  135. m_itemList.SetDirty( true );
  136. break;
  137. case PCB_PAD_T:
  138. m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
  139. m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
  140. m_itemList.SetDirty( true );
  141. break;
  142. case PCB_TRACE_T:
  143. m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
  144. m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
  145. m_itemList.SetDirty( true );
  146. break;
  147. case PCB_VIA_T:
  148. m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
  149. m_itemMap.erase( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
  150. m_itemList.SetDirty( true );
  151. break;
  152. case PCB_ZONE_AREA_T:
  153. case PCB_ZONE_T:
  154. {
  155. m_itemMap[ static_cast<BOARD_CONNECTED_ITEM*>( aItem ) ].MarkItemsAsInvalid();
  156. m_itemMap.erase ( static_cast<BOARD_CONNECTED_ITEM*>( aItem ) );
  157. m_itemList.SetDirty( true );
  158. break;
  159. }
  160. default:
  161. return false;
  162. }
  163. // Once we delete an item, it may connect between lists, so mark both as potentially invalid
  164. m_itemList.SetHasInvalid( true );
  165. return true;
  166. }
  167. void CN_CONNECTIVITY_ALGO::markItemNetAsDirty( const BOARD_ITEM* aItem )
  168. {
  169. if( aItem->IsConnected() )
  170. {
  171. auto citem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
  172. MarkNetAsDirty( citem->GetNetCode() );
  173. }
  174. else
  175. {
  176. if( aItem->Type() == PCB_MODULE_T )
  177. {
  178. auto mod = static_cast <const MODULE*>( aItem );
  179. for( D_PAD* pad = mod->PadsList(); pad; pad = pad->Next() )
  180. MarkNetAsDirty( pad->GetNetCode() );
  181. }
  182. }
  183. }
  184. bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
  185. {
  186. if( !IsCopperLayer( aItem->GetLayer() ) )
  187. return false;
  188. markItemNetAsDirty ( aItem );
  189. switch( aItem->Type() )
  190. {
  191. case PCB_NETINFO_T:
  192. {
  193. MarkNetAsDirty( static_cast<NETINFO_ITEM*>( aItem )->GetNet() );
  194. break;
  195. }
  196. case PCB_MODULE_T:
  197. for( auto pad : static_cast<MODULE*>( aItem ) -> Pads() )
  198. {
  199. if( m_itemMap.find( pad ) != m_itemMap.end() )
  200. return false;
  201. add( m_itemList, pad );
  202. }
  203. break;
  204. case PCB_PAD_T:
  205. if( m_itemMap.find ( static_cast<D_PAD*>( aItem ) ) != m_itemMap.end() )
  206. return false;
  207. add( m_itemList, static_cast<D_PAD*>( aItem ) );
  208. break;
  209. case PCB_TRACE_T:
  210. {
  211. if( m_itemMap.find( static_cast<TRACK*>( aItem ) ) != m_itemMap.end() )
  212. return false;
  213. add( m_itemList, static_cast<TRACK*>( aItem ) );
  214. break;
  215. }
  216. case PCB_VIA_T:
  217. if( m_itemMap.find( static_cast<VIA*>( aItem ) ) != m_itemMap.end() )
  218. return false;
  219. add( m_itemList, static_cast<VIA*>( aItem ) );
  220. break;
  221. case PCB_ZONE_AREA_T:
  222. case PCB_ZONE_T:
  223. {
  224. auto zone = static_cast<ZONE_CONTAINER*>( aItem );
  225. if( m_itemMap.find( static_cast<ZONE_CONTAINER*>( aItem ) ) != m_itemMap.end() )
  226. return false;
  227. m_itemMap[zone] = ITEM_MAP_ENTRY();
  228. for( auto zitem : m_itemList.Add( zone ) )
  229. m_itemMap[zone].Link(zitem);
  230. break;
  231. }
  232. default:
  233. return false;
  234. }
  235. return true;
  236. }
  237. void CN_CONNECTIVITY_ALGO::searchConnections()
  238. {
  239. #ifdef CONNECTIVITY_DEBUG
  240. printf("Search start\n");
  241. #endif
  242. #ifdef PROFILE
  243. PROF_COUNTER garbage_collection( "garbage-collection" );
  244. #endif
  245. std::vector<CN_ITEM*> garbage;
  246. garbage.reserve( 1024 );
  247. m_itemList.RemoveInvalidItems( garbage );
  248. for( auto item : garbage )
  249. delete item;
  250. #ifdef PROFILE
  251. garbage_collection.Show();
  252. PROF_COUNTER search_cnt( "search-connections" );
  253. PROF_COUNTER search_basic( "search-basic" );
  254. #endif
  255. if( m_progressReporter )
  256. {
  257. m_progressReporter->SetMaxProgress( m_itemList.IsDirty() ? m_itemList.Size() : 0 );
  258. }
  259. #ifdef USE_OPENMP
  260. #pragma omp parallel num_threads( std::max( omp_get_num_procs(), 2 ) )
  261. {
  262. if( omp_get_thread_num() == 0 && m_progressReporter )
  263. m_progressReporter->KeepRefreshing( true );
  264. #endif
  265. if( m_itemList.IsDirty() )
  266. {
  267. #ifdef USE_OPENMP
  268. #pragma omp parallel for
  269. #endif
  270. for( int i = 0; i < m_itemList.Size(); i++ )
  271. {
  272. auto item = m_itemList[i];
  273. if( item->Dirty() )
  274. {
  275. CN_VISITOR visitor( item, &m_listLock );
  276. m_itemList.FindNearby( item, visitor );
  277. }
  278. if( m_progressReporter )
  279. m_progressReporter->AdvanceProgress();
  280. }
  281. }
  282. #ifdef PROFILE
  283. search_basic.Show();
  284. #endif
  285. #ifdef USE_OPENMP
  286. }
  287. #endif
  288. m_itemList.ClearDirtyFlags();
  289. #ifdef CONNECTIVITY_DEBUG
  290. printf("Search end\n");
  291. #endif
  292. #ifdef PROFILE
  293. search_cnt.Show();
  294. #endif
  295. }
  296. void CN_ITEM::RemoveInvalidRefs()
  297. {
  298. for( auto it = m_connected.begin(); it != m_connected.end(); )
  299. {
  300. if( !(*it)->Valid() )
  301. it = m_connected.erase( it );
  302. else
  303. ++it;
  304. }
  305. }
  306. void CN_LIST::RemoveInvalidItems( std::vector<CN_ITEM*>& aGarbage )
  307. {
  308. if( !m_hasInvalid )
  309. return;
  310. auto lastItem = std::remove_if(m_items.begin(), m_items.end(), [&aGarbage] ( CN_ITEM* item )
  311. {
  312. if( !item->Valid() )
  313. {
  314. aGarbage.push_back ( item );
  315. return true;
  316. }
  317. return false;
  318. } );
  319. m_items.resize( lastItem - m_items.begin() );
  320. // fixme: mem leaks
  321. for( auto item : m_items )
  322. item->RemoveInvalidRefs();
  323. for( auto item : aGarbage )
  324. m_index.Remove( item );
  325. m_hasInvalid = false;
  326. }
  327. bool CN_CONNECTIVITY_ALGO::isDirty() const
  328. {
  329. return m_itemList.IsDirty();
  330. }
  331. const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode )
  332. {
  333. constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_PAD_T, PCB_VIA_T, PCB_ZONE_AREA_T, PCB_MODULE_T, EOT };
  334. constexpr KICAD_T no_zones[] = { PCB_TRACE_T, PCB_PAD_T, PCB_VIA_T, PCB_MODULE_T, EOT };
  335. if( aMode == CSM_PROPAGATE )
  336. return SearchClusters( aMode, no_zones, -1 );
  337. else
  338. return SearchClusters( aMode, types, -1 );
  339. }
  340. const CN_CONNECTIVITY_ALGO::CLUSTERS CN_CONNECTIVITY_ALGO::SearchClusters( CLUSTER_SEARCH_MODE aMode,
  341. const KICAD_T aTypes[], int aSingleNet )
  342. {
  343. bool withinAnyNet = ( aMode != CSM_PROPAGATE );
  344. std::deque<CN_ITEM*> Q;
  345. CN_ITEM* head = nullptr;
  346. CLUSTERS clusters;
  347. if( isDirty() )
  348. searchConnections();
  349. auto addToSearchList = [&head, withinAnyNet, aSingleNet, aTypes] ( CN_ITEM *aItem )
  350. {
  351. if( withinAnyNet && aItem->Net() <= 0 )
  352. return;
  353. if( !aItem->Valid() )
  354. return;
  355. if( aSingleNet >=0 && aItem->Net() != aSingleNet )
  356. return;
  357. bool found = false;
  358. for( int i = 0; aTypes[i] != EOT; i++ )
  359. {
  360. if( aItem->Parent()->Type() == aTypes[i] )
  361. {
  362. found = true;
  363. break;
  364. }
  365. }
  366. if( !found )
  367. return;
  368. aItem->ListClear();
  369. aItem->SetVisited( false );
  370. if( !head )
  371. head = aItem;
  372. else
  373. head->ListInsert( aItem );
  374. };
  375. std::for_each( m_itemList.begin(), m_itemList.end(), addToSearchList );
  376. while( head )
  377. {
  378. CN_CLUSTER_PTR cluster ( new CN_CLUSTER() );
  379. Q.clear();
  380. CN_ITEM* root = head;
  381. root->SetVisited ( true );
  382. head = root->ListRemove();
  383. Q.push_back( root );
  384. while( Q.size() )
  385. {
  386. CN_ITEM* current = Q.front();
  387. Q.pop_front();
  388. cluster->Add( current );
  389. for( auto n : current->ConnectedItems() )
  390. {
  391. if( withinAnyNet && n->Net() != root->Net() )
  392. continue;
  393. if( !n->Visited() && n->Valid() )
  394. {
  395. n->SetVisited( true );
  396. Q.push_back( n );
  397. head = n->ListRemove();
  398. }
  399. }
  400. }
  401. clusters.push_back( cluster );
  402. }
  403. std::sort( clusters.begin(), clusters.end(), []( CN_CLUSTER_PTR a, CN_CLUSTER_PTR b ) {
  404. return a->OriginNet() < b->OriginNet();
  405. } );
  406. #ifdef CONNECTIVITY_DEBUG
  407. printf("Active clusters: %d\n", clusters.size() );
  408. for( auto cl : clusters )
  409. {
  410. printf( "Net %d\n", cl->OriginNet() );
  411. cl->Dump();
  412. }
  413. #endif
  414. return clusters;
  415. }
  416. void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard )
  417. {
  418. for( int i = 0; i<aBoard->GetAreaCount(); i++ )
  419. {
  420. auto zone = aBoard->GetArea( i );
  421. Add( zone );
  422. }
  423. for( auto tv : aBoard->Tracks() )
  424. Add( tv );
  425. for( auto mod : aBoard->Modules() )
  426. {
  427. for( auto pad : mod->Pads() )
  428. Add( pad );
  429. }
  430. /*wxLogTrace( "CN", "zones : %lu, pads : %lu vias : %lu tracks : %lu\n",
  431. m_zoneList.Size(), m_padList.Size(),
  432. m_viaList.Size(), m_trackList.Size() );*/
  433. }
  434. void CN_CONNECTIVITY_ALGO::Build( const std::vector<BOARD_ITEM*>& aItems )
  435. {
  436. for( auto item : aItems )
  437. {
  438. switch( item->Type() )
  439. {
  440. case PCB_TRACE_T:
  441. case PCB_VIA_T:
  442. case PCB_ZONE_T:
  443. case PCB_PAD_T:
  444. Add( item );
  445. break;
  446. case PCB_MODULE_T:
  447. {
  448. for( auto pad : static_cast<MODULE*>( item )->Pads() )
  449. {
  450. Add( pad );
  451. }
  452. break;
  453. }
  454. default:
  455. break;
  456. }
  457. }
  458. }
  459. void CN_CONNECTIVITY_ALGO::propagateConnections()
  460. {
  461. for( const auto& cluster : m_connClusters )
  462. {
  463. if( cluster->IsConflicting() )
  464. {
  465. wxLogTrace( "CN", "Conflicting nets in cluster %p\n", cluster.get() );
  466. }
  467. else if( cluster->IsOrphaned() )
  468. {
  469. wxLogTrace( "CN", "Skipping orphaned cluster %p [net: %s]\n", cluster.get(),
  470. (const char*) cluster->OriginNetName().c_str() );
  471. }
  472. else if( cluster->HasValidNet() )
  473. {
  474. // normal cluster: just propagate from the pads
  475. int n_changed = 0;
  476. for( auto item : *cluster )
  477. {
  478. if( item->CanChangeNet() )
  479. {
  480. if( item->Valid() && item->Parent()->GetNetCode() != cluster->OriginNet() )
  481. {
  482. MarkNetAsDirty( item->Parent()->GetNetCode() );
  483. MarkNetAsDirty( cluster->OriginNet() );
  484. item->Parent()->SetNetCode( cluster->OriginNet() );
  485. n_changed++;
  486. }
  487. }
  488. }
  489. if( n_changed )
  490. wxLogTrace( "CN", "Cluster %p : net : %d %s\n", cluster.get(),
  491. cluster->OriginNet(), (const char*) cluster->OriginNetName().c_str() );
  492. else
  493. wxLogTrace( "CN", "Cluster %p : nothing to propagate\n", cluster.get() );
  494. }
  495. else
  496. {
  497. wxLogTrace( "CN", "Cluster %p : connected to unused net\n", cluster.get() );
  498. }
  499. }
  500. }
  501. void CN_CONNECTIVITY_ALGO::PropagateNets()
  502. {
  503. m_connClusters = SearchClusters( CSM_PROPAGATE );
  504. propagateConnections();
  505. }
  506. void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands )
  507. {
  508. if( aZone->GetFilledPolysList().IsEmpty() )
  509. return;
  510. aIslands.clear();
  511. Remove( aZone );
  512. Add( aZone );
  513. m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
  514. for( const auto& cluster : m_connClusters )
  515. {
  516. if( cluster->Contains( aZone ) && cluster->IsOrphaned() )
  517. {
  518. for( auto z : *cluster )
  519. {
  520. if( z->Parent() == aZone )
  521. {
  522. aIslands.push_back( static_cast<CN_ZONE*>(z)->SubpolyIndex() );
  523. }
  524. }
  525. }
  526. }
  527. wxLogTrace( "CN", "Found %u isolated islands\n", (unsigned)aIslands.size() );
  528. }
  529. void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
  530. {
  531. for ( auto& z : aZones )
  532. Remove( z.m_zone );
  533. for ( auto& z : aZones )
  534. {
  535. if( !z.m_zone->GetFilledPolysList().IsEmpty() )
  536. Add( z.m_zone );
  537. }
  538. m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
  539. for ( auto& zone : aZones )
  540. {
  541. if( zone.m_zone->GetFilledPolysList().IsEmpty() )
  542. continue;
  543. for( const auto& cluster : m_connClusters )
  544. {
  545. if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
  546. {
  547. for( auto z : *cluster )
  548. {
  549. if( z->Parent() == zone.m_zone )
  550. {
  551. zone.m_islands.push_back( static_cast<CN_ZONE*>(z)->SubpolyIndex() );
  552. }
  553. }
  554. }
  555. }
  556. }
  557. }
  558. const CN_CONNECTIVITY_ALGO::CLUSTERS& CN_CONNECTIVITY_ALGO::GetClusters()
  559. {
  560. m_ratsnestClusters = SearchClusters( CSM_RATSNEST );
  561. return m_ratsnestClusters;
  562. }
  563. void CN_CONNECTIVITY_ALGO::MarkNetAsDirty( int aNet )
  564. {
  565. if( aNet < 0 )
  566. return;
  567. if( (int) m_dirtyNets.size() <= aNet )
  568. {
  569. int lastNet = m_dirtyNets.size() - 1;
  570. if( lastNet < 0 )
  571. lastNet = 0;
  572. m_dirtyNets.resize( aNet + 1 );
  573. for( int i = lastNet; i < aNet + 1; i++ )
  574. m_dirtyNets[i] = true;
  575. }
  576. m_dirtyNets[aNet] = true;
  577. }
  578. void CN_VISITOR::checkZoneItemConnection( CN_ZONE* aZone, CN_ITEM* aItem )
  579. {
  580. auto zoneItem = static_cast<CN_ZONE*> ( aZone );
  581. if( zoneItem->Net() != aItem->Net() && !aItem->CanChangeNet() )
  582. return;
  583. if( zoneItem->ContainsPoint( aItem->GetAnchor( 0 ) ) ||
  584. ( aItem->Parent()->Type() == PCB_TRACE_T &&
  585. zoneItem->ContainsPoint( aItem->GetAnchor( 1 ) ) ) )
  586. {
  587. std::lock_guard<std::mutex> lock( *m_listLock );
  588. CN_ITEM::Connect( zoneItem, aItem );
  589. }
  590. }
  591. void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
  592. {
  593. const auto refParent = static_cast<const ZONE_CONTAINER*>( aZoneA->Parent() );
  594. const auto testedParent = static_cast<const ZONE_CONTAINER*>( aZoneB->Parent() );
  595. if( testedParent->Type () != PCB_ZONE_AREA_T )
  596. return;
  597. if( aZoneB == aZoneA || refParent == testedParent )
  598. return;
  599. if( aZoneB->Net() != aZoneA->Net() )
  600. return; // we only test zones belonging to the same net
  601. const auto& outline = refParent->GetFilledPolysList().COutline( aZoneA->SubpolyIndex() );
  602. for( int i = 0; i < outline.PointCount(); i++ )
  603. {
  604. if( aZoneB->ContainsPoint( outline.CPoint( i ) ) )
  605. {
  606. std::lock_guard<std::mutex> lock( *m_listLock );
  607. CN_ITEM::Connect( aZoneA, aZoneB );
  608. return;
  609. }
  610. }
  611. const auto& outline2 = testedParent->GetFilledPolysList().COutline( aZoneB->SubpolyIndex() );
  612. for( int i = 0; i < outline2.PointCount(); i++ )
  613. {
  614. if( aZoneA->ContainsPoint( outline2.CPoint( i ) ) )
  615. {
  616. std::lock_guard<std::mutex> lock( *m_listLock );
  617. CN_ITEM::Connect( aZoneA, aZoneB );
  618. return;
  619. }
  620. }
  621. }
  622. bool CN_VISITOR::operator()( CN_ITEM* aCandidate )
  623. {
  624. const auto parentA = aCandidate->Parent();
  625. const auto parentB = m_item->Parent();
  626. if( !aCandidate->Valid() || !m_item->Valid() )
  627. return true;
  628. if( parentA == parentB )
  629. return true;
  630. if( !( parentA->GetLayerSet() & parentB->GetLayerSet() ).any() )
  631. return true;
  632. // If both m_item and aCandidate are marked dirty, they will both be searched
  633. // Since we are reciprocal in our connection, we arbitrarily pick one of the connections
  634. // to conduct the expensive search
  635. if( aCandidate->Dirty() && aCandidate < m_item )
  636. return true;
  637. // We should handle zone-zone connection separately
  638. if ( ( parentA->Type() == PCB_ZONE_AREA_T || parentA->Type() == PCB_ZONE_T ) &&
  639. ( parentB->Type() == PCB_ZONE_AREA_T || parentB->Type() == PCB_ZONE_T ) )
  640. {
  641. checkZoneZoneConnection( static_cast<CN_ZONE*>( m_item ),
  642. static_cast<CN_ZONE*>( aCandidate ) );
  643. return true;
  644. }
  645. if( parentA->Type() == PCB_ZONE_AREA_T || parentA->Type() == PCB_ZONE_T)
  646. {
  647. checkZoneItemConnection( static_cast<CN_ZONE*>( aCandidate ), m_item );
  648. return true;
  649. }
  650. if( parentB->Type() == PCB_ZONE_AREA_T || parentB->Type() == PCB_ZONE_T)
  651. {
  652. checkZoneItemConnection( static_cast<CN_ZONE*>( m_item ), aCandidate );
  653. return true;
  654. }
  655. // Items do not necessarily have reciprocity as we only check for anchors
  656. // therefore, we check HitTest both directions A->B & B->A
  657. // TODO: Check for collision geometry on extended features
  658. wxPoint ptA1( aCandidate->GetAnchor( 0 ).x, aCandidate->GetAnchor( 0 ).y );
  659. wxPoint ptA2( aCandidate->GetAnchor( 1 ).x, aCandidate->GetAnchor( 1 ).y );
  660. wxPoint ptB1( m_item->GetAnchor( 0 ).x, m_item->GetAnchor( 0 ).y );
  661. wxPoint ptB2( m_item->GetAnchor( 1 ).x, m_item->GetAnchor( 1 ).y );
  662. if( parentA->HitTest( ptB1 ) || parentB->HitTest( ptA1 ) ||
  663. ( parentA->Type() == PCB_TRACE_T && parentB->HitTest( ptA2 ) ) ||
  664. ( parentB->Type() == PCB_TRACE_T && parentA->HitTest( ptB2 ) ) )
  665. {
  666. std::lock_guard<std::mutex> lock( *m_listLock );
  667. CN_ITEM::Connect( m_item, aCandidate );
  668. }
  669. return true;
  670. };
  671. int CN_ITEM::AnchorCount() const
  672. {
  673. if( !m_valid )
  674. return 0;
  675. return m_parent->Type() == PCB_TRACE_T ? 2 : 1;
  676. }
  677. const VECTOR2I CN_ITEM::GetAnchor( int n ) const
  678. {
  679. if( !m_valid )
  680. return VECTOR2I();
  681. switch( m_parent->Type() )
  682. {
  683. case PCB_PAD_T:
  684. return static_cast<const D_PAD*>( m_parent )->ShapePos();
  685. break;
  686. case PCB_TRACE_T:
  687. {
  688. auto tr = static_cast<const TRACK*>( m_parent );
  689. return ( n == 0 ? tr->GetStart() : tr->GetEnd() );
  690. break;
  691. }
  692. case PCB_VIA_T:
  693. return static_cast<const VIA*>( m_parent )->GetStart();
  694. default:
  695. assert( false );
  696. return VECTOR2I();
  697. }
  698. }
  699. int CN_ZONE::AnchorCount() const
  700. {
  701. if( !Valid() )
  702. return 0;
  703. const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
  704. const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
  705. return outline.PointCount() ? 1 : 0;
  706. }
  707. const VECTOR2I CN_ZONE::GetAnchor( int n ) const
  708. {
  709. if( !Valid() )
  710. return VECTOR2I();
  711. const auto zone = static_cast<const ZONE_CONTAINER*> ( Parent() );
  712. const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
  713. return outline.CPoint( 0 );
  714. }
  715. int CN_ITEM::Net() const
  716. {
  717. if( !m_parent || !m_valid )
  718. return -1;
  719. return m_parent->GetNetCode();
  720. }
  721. BOARD_CONNECTED_ITEM* CN_ANCHOR::Parent() const
  722. {
  723. assert( m_item->Valid() );
  724. return m_item->Parent();
  725. }
  726. bool CN_ANCHOR::Valid() const
  727. {
  728. if( !m_item )
  729. return false;
  730. return m_item->Valid();
  731. }
  732. void CN_CONNECTIVITY_ALGO::Clear()
  733. {
  734. m_ratsnestClusters.clear();
  735. m_connClusters.clear();
  736. m_itemMap.clear();
  737. m_itemList.Clear();
  738. }
  739. void CN_CONNECTIVITY_ALGO::ForEachItem( const std::function<void( CN_ITEM& )>& aFunc )
  740. {
  741. for( auto item : m_itemList )
  742. aFunc( *item );
  743. }
  744. void CN_CONNECTIVITY_ALGO::ForEachAnchor( const std::function<void( CN_ANCHOR& )>& aFunc )
  745. {
  746. ForEachItem( [aFunc] ( CN_ITEM& item ) {
  747. for( const auto& anchor : item.Anchors() )
  748. aFunc( *anchor );
  749. }
  750. );
  751. }
  752. bool CN_ANCHOR::IsDangling() const
  753. {
  754. if( !m_cluster )
  755. return true;
  756. // Calculate the item count connected to this anchor.
  757. // m_cluster groups all items connected, but they are not necessary connected
  758. // at this coordinate point (they are only candidates)
  759. BOARD_CONNECTED_ITEM* item_ref = Parent();
  760. LSET layers = item_ref->GetLayerSet() & LSET::AllCuMask();
  761. // the number of items connected to item_ref at ths anchor point
  762. int connected_items_count = 0;
  763. // the minimal number of items connected to item_ref
  764. // at this anchor point to decide the anchor is *not* dangling
  765. int minimal_count = 1;
  766. // a via can be removed if connected to only one other item.
  767. // the minimal_count is therefore 2
  768. if( item_ref->Type() == PCB_VIA_T )
  769. minimal_count = 2;
  770. for( CN_ITEM* item : *m_cluster )
  771. {
  772. if( !item->Valid() )
  773. continue;
  774. BOARD_CONNECTED_ITEM* brd_item = item->Parent();
  775. if( brd_item == item_ref )
  776. continue;
  777. // count only items on the same layer at this coordinate (especially for zones)
  778. if( !( brd_item->GetLayerSet() & layers ).any() )
  779. continue;
  780. if( brd_item->Type() == PCB_ZONE_AREA_T )
  781. {
  782. ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( brd_item );
  783. if( zone->HitTestInsideZone( wxPoint( Pos() ) ) )
  784. connected_items_count++;
  785. }
  786. else if( brd_item->HitTest( wxPoint( Pos() ) ) )
  787. connected_items_count++;
  788. }
  789. return connected_items_count < minimal_count;
  790. }
  791. void CN_CONNECTIVITY_ALGO::SetProgressReporter( PROGRESS_REPORTER* aReporter )
  792. {
  793. m_progressReporter = aReporter;
  794. }