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.

759 lines
19 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 CERN
  5. * Copyright (C) 2018-2020 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #ifdef PROFILE
  26. #include <profile.h>
  27. #endif
  28. #include <thread>
  29. #include <algorithm>
  30. #include <future>
  31. #include <connectivity/connectivity_data.h>
  32. #include <connectivity/connectivity_algo.h>
  33. #include <ratsnest/ratsnest_data.h>
  34. CONNECTIVITY_DATA::CONNECTIVITY_DATA()
  35. {
  36. m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
  37. m_progressReporter = nullptr;
  38. }
  39. CONNECTIVITY_DATA::CONNECTIVITY_DATA( const std::vector<BOARD_ITEM*>& aItems, bool aSkipRatsnest )
  40. : m_skipRatsnest( aSkipRatsnest )
  41. {
  42. Build( aItems );
  43. m_progressReporter = nullptr;
  44. }
  45. CONNECTIVITY_DATA::~CONNECTIVITY_DATA()
  46. {
  47. Clear();
  48. }
  49. bool CONNECTIVITY_DATA::Add( BOARD_ITEM* aItem )
  50. {
  51. m_connAlgo->Add( aItem );
  52. return true;
  53. }
  54. bool CONNECTIVITY_DATA::Remove( BOARD_ITEM* aItem )
  55. {
  56. m_connAlgo->Remove( aItem );
  57. return true;
  58. }
  59. bool CONNECTIVITY_DATA::Update( BOARD_ITEM* aItem )
  60. {
  61. m_connAlgo->Remove( aItem );
  62. m_connAlgo->Add( aItem );
  63. return true;
  64. }
  65. void CONNECTIVITY_DATA::Build( BOARD* aBoard )
  66. {
  67. m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
  68. m_connAlgo->Build( aBoard );
  69. m_netclassMap.clear();
  70. for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
  71. if( net->GetNetClass()->GetName() != NETCLASS::Default )
  72. m_netclassMap[net->GetNet()] = net->GetNetClass()->GetName();
  73. RecalculateRatsnest();
  74. }
  75. void CONNECTIVITY_DATA::Build( const std::vector<BOARD_ITEM*>& aItems )
  76. {
  77. m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
  78. m_connAlgo->Build( aItems );
  79. RecalculateRatsnest();
  80. }
  81. void CONNECTIVITY_DATA::Move( const VECTOR2I& aDelta )
  82. {
  83. m_connAlgo->ForEachAnchor( [&aDelta] ( CN_ANCHOR& anchor ) { anchor.Move( aDelta ); } );
  84. }
  85. void CONNECTIVITY_DATA::updateRatsnest()
  86. {
  87. #ifdef PROFILE
  88. PROF_COUNTER rnUpdate( "update-ratsnest" );
  89. #endif
  90. std::vector<RN_NET*> dirty_nets;
  91. // Start with net 1 as net 0 is reserved for not-connected
  92. // Nets without nodes are also ignored
  93. std::copy_if( m_nets.begin() + 1, m_nets.end(), std::back_inserter( dirty_nets ),
  94. [] ( RN_NET* aNet ) { return aNet->IsDirty() && aNet->GetNodeCount() > 0; } );
  95. // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
  96. size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
  97. ( dirty_nets.size() + 7 ) / 8 );
  98. std::atomic<size_t> nextNet( 0 );
  99. std::vector<std::future<size_t>> returns( parallelThreadCount );
  100. auto update_lambda = [&nextNet, &dirty_nets]() -> size_t
  101. {
  102. for( size_t i = nextNet++; i < dirty_nets.size(); i = nextNet++ )
  103. dirty_nets[i]->Update();
  104. return 1;
  105. };
  106. if( parallelThreadCount == 1 )
  107. update_lambda();
  108. else
  109. {
  110. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  111. returns[ii] = std::async( std::launch::async, update_lambda );
  112. // Finalize the ratsnest threads
  113. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  114. returns[ii].wait();
  115. }
  116. #ifdef PROFILE
  117. rnUpdate.Show();
  118. #endif /* PROFILE */
  119. }
  120. void CONNECTIVITY_DATA::addRatsnestCluster( const std::shared_ptr<CN_CLUSTER>& aCluster )
  121. {
  122. auto rnNet = m_nets[ aCluster->OriginNet() ];
  123. rnNet->AddCluster( aCluster );
  124. }
  125. void CONNECTIVITY_DATA::RecalculateRatsnest( BOARD_COMMIT* aCommit )
  126. {
  127. m_connAlgo->PropagateNets( aCommit );
  128. int lastNet = m_connAlgo->NetCount();
  129. if( lastNet >= (int) m_nets.size() )
  130. {
  131. unsigned int prevSize = m_nets.size();
  132. m_nets.resize( lastNet + 1 );
  133. for( unsigned int i = prevSize; i < m_nets.size(); i++ )
  134. m_nets[i] = new RN_NET;
  135. }
  136. auto clusters = m_connAlgo->GetClusters();
  137. int dirtyNets = 0;
  138. for( int net = 0; net < lastNet; net++ )
  139. {
  140. if( m_connAlgo->IsNetDirty( net ) )
  141. {
  142. m_nets[net]->Clear();
  143. dirtyNets++;
  144. }
  145. }
  146. for( const auto& c : clusters )
  147. {
  148. int net = c->OriginNet();
  149. // Don't add intentionally-kept zone islands to the ratsnest
  150. if( c->IsOrphaned() && c->Size() == 1 )
  151. {
  152. if( dynamic_cast<CN_ZONE*>( *c->begin() ) )
  153. continue;
  154. }
  155. if( m_connAlgo->IsNetDirty( net ) )
  156. {
  157. addRatsnestCluster( c );
  158. }
  159. }
  160. m_connAlgo->ClearDirtyFlags();
  161. if( !m_skipRatsnest )
  162. updateRatsnest();
  163. }
  164. void CONNECTIVITY_DATA::BlockRatsnestItems( const std::vector<BOARD_ITEM*>& aItems )
  165. {
  166. std::vector<BOARD_CONNECTED_ITEM*> citems;
  167. for( auto item : aItems )
  168. {
  169. if( item->Type() == PCB_MODULE_T )
  170. {
  171. for( auto pad : static_cast<MODULE*>(item)->Pads() )
  172. citems.push_back( pad );
  173. }
  174. else
  175. {
  176. if( auto citem = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  177. citems.push_back( citem );
  178. }
  179. }
  180. for( const auto& item : citems )
  181. {
  182. if ( m_connAlgo->ItemExists( item ) )
  183. {
  184. auto& entry = m_connAlgo->ItemEntry( item );
  185. for( const auto& cnItem : entry.GetItems() )
  186. {
  187. for( auto anchor : cnItem->Anchors() )
  188. anchor->SetNoLine( true );
  189. }
  190. }
  191. }
  192. }
  193. int CONNECTIVITY_DATA::GetNetCount() const
  194. {
  195. return m_connAlgo->NetCount();
  196. }
  197. void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone,
  198. std::vector<int>& aIslands )
  199. {
  200. // TODO(JE) ZONES
  201. #if 0
  202. m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands );
  203. #endif
  204. }
  205. void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
  206. {
  207. m_connAlgo->FindIsolatedCopperIslands( aZones );
  208. }
  209. void CONNECTIVITY_DATA::ComputeDynamicRatsnest( const std::vector<BOARD_ITEM*>& aItems,
  210. const CONNECTIVITY_DATA* aDynamicData )
  211. {
  212. if( !aDynamicData )
  213. return;
  214. m_dynamicRatsnest.clear();
  215. // This gets connections between the stationary board and the
  216. // moving selection
  217. for( unsigned int nc = 1; nc < aDynamicData->m_nets.size(); nc++ )
  218. {
  219. auto dynNet = aDynamicData->m_nets[nc];
  220. if( dynNet->GetNodeCount() != 0 )
  221. {
  222. auto ourNet = m_nets[nc];
  223. CN_ANCHOR_PTR nodeA, nodeB;
  224. if( ourNet->NearestBicoloredPair( *dynNet, nodeA, nodeB ) )
  225. {
  226. RN_DYNAMIC_LINE l;
  227. l.a = nodeA->Pos();
  228. l.b = nodeB->Pos();
  229. l.netCode = nc;
  230. m_dynamicRatsnest.push_back( l );
  231. }
  232. }
  233. }
  234. // This gets the ratsnest for internal connections in the moving set
  235. const auto& edges = GetRatsnestForItems( aItems );
  236. for( const auto& edge : edges )
  237. {
  238. const auto& nodeA = edge.GetSourceNode();
  239. const auto& nodeB = edge.GetTargetNode();
  240. RN_DYNAMIC_LINE l;
  241. // Use the parents' positions
  242. l.a = nodeA->Parent()->GetPosition();
  243. l.b = nodeB->Parent()->GetPosition();
  244. l.netCode = 0;
  245. m_dynamicRatsnest.push_back( l );
  246. }
  247. }
  248. void CONNECTIVITY_DATA::ClearDynamicRatsnest()
  249. {
  250. m_connAlgo->ForEachAnchor( []( CN_ANCHOR& anchor )
  251. {
  252. anchor.SetNoLine( false );
  253. } );
  254. HideDynamicRatsnest();
  255. }
  256. void CONNECTIVITY_DATA::HideDynamicRatsnest()
  257. {
  258. m_dynamicRatsnest.clear();
  259. }
  260. void CONNECTIVITY_DATA::PropagateNets()
  261. {
  262. m_connAlgo->PropagateNets();
  263. }
  264. bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, int aLayer,
  265. std::vector<KICAD_T> aTypes ) const
  266. {
  267. CN_CONNECTIVITY_ALGO::ITEM_MAP_ENTRY &entry = m_connAlgo->ItemEntry( aItem );
  268. for( auto citem : entry.GetItems() )
  269. {
  270. for( auto connected : citem->ConnectedItems() )
  271. {
  272. if( connected->Valid() && connected->Layers().Overlaps( aLayer )
  273. && ( aTypes.empty()
  274. || std::count( aTypes.begin(), aTypes.end(),
  275. connected->Parent()->Type() ) > 0 ) )
  276. return true;
  277. }
  278. }
  279. return false;
  280. }
  281. unsigned int CONNECTIVITY_DATA::GetUnconnectedCount() const
  282. {
  283. unsigned int unconnected = 0;
  284. for( auto net : m_nets )
  285. {
  286. if( !net )
  287. continue;
  288. const auto& edges = net->GetUnconnected();
  289. if( edges.empty() )
  290. continue;
  291. unconnected += edges.size();
  292. }
  293. return unconnected;
  294. }
  295. void CONNECTIVITY_DATA::Clear()
  296. {
  297. for( auto net : m_nets )
  298. delete net;
  299. m_nets.clear();
  300. }
  301. const std::vector<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetConnectedItems(
  302. const BOARD_CONNECTED_ITEM* aItem,
  303. const KICAD_T aTypes[],
  304. bool aIgnoreNetcodes ) const
  305. {
  306. std::vector<BOARD_CONNECTED_ITEM*> rv;
  307. const auto clusters = m_connAlgo->SearchClusters(
  308. aIgnoreNetcodes ?
  309. CN_CONNECTIVITY_ALGO::CSM_PROPAGATE :
  310. CN_CONNECTIVITY_ALGO::CSM_CONNECTIVITY_CHECK, aTypes,
  311. aIgnoreNetcodes ? -1 : aItem->GetNetCode() );
  312. for( auto cl : clusters )
  313. {
  314. if( cl->Contains( aItem ) )
  315. {
  316. for( const auto item : *cl )
  317. {
  318. if( item->Valid() )
  319. rv.push_back( item->Parent() );
  320. }
  321. }
  322. }
  323. return rv;
  324. }
  325. const std::vector<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetNetItems( int aNetCode,
  326. const KICAD_T aTypes[] ) const
  327. {
  328. std::vector<BOARD_CONNECTED_ITEM*> items;
  329. items.reserve( 32 );
  330. std::bitset<MAX_STRUCT_TYPE_ID> type_bits;
  331. for( unsigned int i = 0; aTypes[i] != EOT; ++i )
  332. {
  333. wxASSERT( aTypes[i] < MAX_STRUCT_TYPE_ID );
  334. type_bits.set( aTypes[i] );
  335. }
  336. m_connAlgo->ForEachItem( [&]( CN_ITEM& aItem ) {
  337. if( aItem.Valid() && ( aItem.Net() == aNetCode ) && type_bits[aItem.Parent()->Type()] )
  338. items.push_back( aItem.Parent() );
  339. } );
  340. std::sort( items.begin(), items.end() );
  341. items.erase( std::unique( items.begin(), items.end() ), items.end() );
  342. return items;
  343. }
  344. bool CONNECTIVITY_DATA::CheckConnectivity( std::vector<CN_DISJOINT_NET_ENTRY>& aReport )
  345. {
  346. RecalculateRatsnest();
  347. for( auto net : m_nets )
  348. {
  349. if( net )
  350. {
  351. for( const auto& edge : net->GetEdges() )
  352. {
  353. CN_DISJOINT_NET_ENTRY ent;
  354. ent.net = edge.GetSourceNode()->Parent()->GetNetCode();
  355. ent.a = edge.GetSourceNode()->Parent();
  356. ent.b = edge.GetTargetNode()->Parent();
  357. ent.anchorA = edge.GetSourceNode()->Pos();
  358. ent.anchorB = edge.GetTargetNode()->Pos();
  359. aReport.push_back( ent );
  360. }
  361. }
  362. }
  363. return aReport.empty();
  364. }
  365. const std::vector<TRACK*> CONNECTIVITY_DATA::GetConnectedTracks( const BOARD_CONNECTED_ITEM* aItem )
  366. const
  367. {
  368. auto& entry = m_connAlgo->ItemEntry( aItem );
  369. std::set<TRACK*> tracks;
  370. std::vector<TRACK*> rv;
  371. for( auto citem : entry.GetItems() )
  372. {
  373. for( auto connected : citem->ConnectedItems() )
  374. {
  375. if( connected->Valid() &&
  376. ( connected->Parent()->Type() == PCB_TRACE_T ||
  377. connected->Parent()->Type() == PCB_VIA_T ||
  378. connected->Parent()->Type() == PCB_ARC_T ) )
  379. tracks.insert( static_cast<TRACK*> ( connected->Parent() ) );
  380. }
  381. }
  382. std::copy( tracks.begin(), tracks.end(), std::back_inserter( rv ) );
  383. return rv;
  384. }
  385. void CONNECTIVITY_DATA::GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem,
  386. std::set<D_PAD*>* pads ) const
  387. {
  388. for( auto citem : m_connAlgo->ItemEntry( aItem ).GetItems() )
  389. {
  390. for( auto connected : citem->ConnectedItems() )
  391. {
  392. if( connected->Valid() && connected->Parent()->Type() == PCB_PAD_T )
  393. pads->insert( static_cast<D_PAD*> ( connected->Parent() ) );
  394. }
  395. }
  396. }
  397. const std::vector<D_PAD*> CONNECTIVITY_DATA::GetConnectedPads( const BOARD_CONNECTED_ITEM* aItem )
  398. const
  399. {
  400. std::set<D_PAD*> pads;
  401. std::vector<D_PAD*> rv;
  402. GetConnectedPads( aItem, &pads );
  403. std::copy( pads.begin(), pads.end(), std::back_inserter( rv ) );
  404. return rv;
  405. }
  406. unsigned int CONNECTIVITY_DATA::GetNodeCount( int aNet ) const
  407. {
  408. int sum = 0;
  409. if( aNet < 0 ) // Node count for all nets
  410. {
  411. for( const auto& net : m_nets )
  412. sum += net->GetNodeCount();
  413. }
  414. else if( aNet < (int) m_nets.size() )
  415. {
  416. sum = m_nets[aNet]->GetNodeCount();
  417. }
  418. return sum;
  419. }
  420. unsigned int CONNECTIVITY_DATA::GetPadCount( int aNet ) const
  421. {
  422. int n = 0;
  423. for( auto&& pad : m_connAlgo->ItemList() )
  424. {
  425. if( !pad->Valid() || pad->Parent()->Type() != PCB_PAD_T)
  426. continue;
  427. auto dpad = static_cast<D_PAD*>( pad->Parent() );
  428. if( aNet < 0 || aNet == dpad->GetNetCode() )
  429. {
  430. n++;
  431. }
  432. }
  433. return n;
  434. }
  435. void CONNECTIVITY_DATA::GetUnconnectedEdges( std::vector<CN_EDGE>& aEdges) const
  436. {
  437. for( auto rnNet : m_nets )
  438. {
  439. if( rnNet )
  440. {
  441. for( const auto& edge : rnNet->GetEdges() )
  442. {
  443. aEdges.push_back( edge );
  444. }
  445. }
  446. }
  447. }
  448. bool CONNECTIVITY_DATA::TestTrackEndpointDangling( TRACK* aTrack, wxPoint* aPos )
  449. {
  450. auto items = GetConnectivityAlgo()->ItemEntry( aTrack ).GetItems();
  451. // Not in the connectivity system. This is a bug!
  452. if( items.empty() )
  453. {
  454. wxFAIL_MSG( "track not in connectivity system" );
  455. return false;
  456. }
  457. CN_ITEM* citem = items.front();
  458. if( !citem->Valid() )
  459. return false;
  460. for( const std::shared_ptr<CN_ANCHOR>& anchor : citem->Anchors() )
  461. {
  462. if( anchor->IsDangling() )
  463. {
  464. if( aPos )
  465. *aPos = static_cast<wxPoint>( anchor->Pos() );
  466. return true;
  467. }
  468. }
  469. return false;
  470. }
  471. const std::vector<BOARD_CONNECTED_ITEM*> CONNECTIVITY_DATA::GetConnectedItems(
  472. const BOARD_CONNECTED_ITEM* aItem, const VECTOR2I& aAnchor, KICAD_T aTypes[] )
  473. {
  474. auto& entry = m_connAlgo->ItemEntry( aItem );
  475. std::vector<BOARD_CONNECTED_ITEM* > rv;
  476. for( auto cnItem : entry.GetItems() )
  477. {
  478. for( auto anchor : cnItem->Anchors() )
  479. {
  480. if( anchor->Pos() == aAnchor )
  481. {
  482. for( int i = 0; aTypes[i] > 0; i++ )
  483. {
  484. if( cnItem->Valid() && cnItem->Parent()->Type() == aTypes[i] )
  485. {
  486. rv.push_back( cnItem->Parent() );
  487. break;
  488. }
  489. }
  490. }
  491. }
  492. }
  493. return rv;
  494. }
  495. RN_NET* CONNECTIVITY_DATA::GetRatsnestForNet( int aNet )
  496. {
  497. if ( aNet < 0 || aNet >= (int) m_nets.size() )
  498. {
  499. return nullptr;
  500. }
  501. return m_nets[ aNet ];
  502. }
  503. void CONNECTIVITY_DATA::MarkItemNetAsDirty( BOARD_ITEM *aItem )
  504. {
  505. if (aItem->Type() == PCB_MODULE_T)
  506. {
  507. for ( auto pad : static_cast<MODULE*>( aItem )->Pads() )
  508. {
  509. m_connAlgo->MarkNetAsDirty( pad->GetNetCode() );
  510. }
  511. }
  512. if (aItem->IsConnected() )
  513. {
  514. m_connAlgo->MarkNetAsDirty( static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() );
  515. }
  516. }
  517. void CONNECTIVITY_DATA::SetProgressReporter( PROGRESS_REPORTER* aReporter )
  518. {
  519. m_progressReporter = aReporter;
  520. m_connAlgo->SetProgressReporter( m_progressReporter );
  521. }
  522. const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForItems( std::vector<BOARD_ITEM*> aItems )
  523. {
  524. std::set<int> nets;
  525. std::vector<CN_EDGE> edges;
  526. std::set<BOARD_CONNECTED_ITEM*> item_set;
  527. for( auto item : aItems )
  528. {
  529. if( item->Type() == PCB_MODULE_T )
  530. {
  531. auto component = static_cast<MODULE*>( item );
  532. for( auto pad : component->Pads() )
  533. {
  534. nets.insert( pad->GetNetCode() );
  535. item_set.insert( pad );
  536. }
  537. }
  538. else if( auto conn_item = dyn_cast<BOARD_CONNECTED_ITEM*>( item ) )
  539. {
  540. item_set.insert( conn_item );
  541. nets.insert( conn_item->GetNetCode() );
  542. }
  543. }
  544. for( const auto& netcode : nets )
  545. {
  546. const auto& net = GetRatsnestForNet( netcode );
  547. for( const auto& edge : net->GetEdges() )
  548. {
  549. auto srcNode = edge.GetSourceNode();
  550. auto dstNode = edge.GetTargetNode();
  551. auto srcParent = srcNode->Parent();
  552. auto dstParent = dstNode->Parent();
  553. bool srcFound = ( item_set.find(srcParent) != item_set.end() );
  554. bool dstFound = ( item_set.find(dstParent) != item_set.end() );
  555. if ( srcFound && dstFound )
  556. edges.push_back( edge );
  557. }
  558. }
  559. return edges;
  560. }
  561. const std::vector<CN_EDGE> CONNECTIVITY_DATA::GetRatsnestForComponent( MODULE* aComponent, bool aSkipInternalConnections )
  562. {
  563. std::set<int> nets;
  564. std::set<const D_PAD*> pads;
  565. std::vector<CN_EDGE> edges;
  566. for( auto pad : aComponent->Pads() )
  567. {
  568. nets.insert( pad->GetNetCode() );
  569. pads.insert( pad );
  570. }
  571. for( const auto& netcode : nets )
  572. {
  573. const auto& net = GetRatsnestForNet( netcode );
  574. for( const auto& edge : net->GetEdges() )
  575. {
  576. auto srcNode = edge.GetSourceNode();
  577. auto dstNode = edge.GetTargetNode();
  578. auto srcParent = static_cast<const D_PAD*>( srcNode->Parent() );
  579. auto dstParent = static_cast<const D_PAD*>( dstNode->Parent() );
  580. bool srcFound = ( pads.find(srcParent) != pads.end() );
  581. bool dstFound = ( pads.find(dstParent) != pads.end() );
  582. if ( srcFound && dstFound && !aSkipInternalConnections )
  583. {
  584. edges.push_back( edge );
  585. }
  586. else if ( srcFound || dstFound )
  587. {
  588. edges.push_back( edge );
  589. }
  590. }
  591. }
  592. return edges;
  593. }