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.

570 lines
17 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013-2017 CERN
  5. * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Maciej Suminski <maciej.suminski@cern.ch>
  8. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. /**
  28. * @file ratsnest_data.cpp
  29. * @brief Class that computes missing connections on a PCB.
  30. */
  31. #ifdef PROFILE
  32. #include <profile.h>
  33. #endif
  34. #include <ratsnest/ratsnest_data.h>
  35. #include <functional>
  36. using namespace std::placeholders;
  37. #include <algorithm>
  38. #include <cassert>
  39. #include <limits>
  40. #include <delaunator.hpp>
  41. class disjoint_set
  42. {
  43. public:
  44. disjoint_set( size_t size )
  45. {
  46. m_data.resize( size );
  47. m_depth.resize( size, 0 );
  48. for( size_t i = 0; i < size; i++ )
  49. m_data[i] = i;
  50. }
  51. int find( int aVal )
  52. {
  53. int root = aVal;
  54. while( m_data[root] != root )
  55. root = m_data[root];
  56. // Compress the path
  57. while( m_data[aVal] != aVal )
  58. {
  59. auto& tmp = m_data[aVal];
  60. aVal = tmp;
  61. tmp = root;
  62. }
  63. return root;
  64. }
  65. bool unite( int aVal1, int aVal2 )
  66. {
  67. aVal1 = find( aVal1 );
  68. aVal2 = find( aVal2 );
  69. if( aVal1 != aVal2 )
  70. {
  71. if( m_depth[aVal1] < m_depth[aVal2] )
  72. {
  73. m_data[aVal1] = aVal2;
  74. }
  75. else
  76. {
  77. m_data[aVal2] = aVal1;
  78. if( m_depth[aVal1] == m_depth[aVal2] )
  79. m_depth[aVal1]++;
  80. }
  81. return true;
  82. }
  83. return false;
  84. }
  85. private:
  86. std::vector<int> m_data;
  87. std::vector<int> m_depth;
  88. };
  89. void RN_NET::kruskalMST( const std::vector<CN_EDGE> &aEdges )
  90. {
  91. disjoint_set dset( m_nodes.size() );
  92. m_rnEdges.clear();
  93. int i = 0;
  94. for( const std::shared_ptr<CN_ANCHOR>& node : m_nodes )
  95. node->SetTag( i++ );
  96. for( const CN_EDGE& tmp : aEdges )
  97. {
  98. const std::shared_ptr<CN_ANCHOR>& source = tmp.GetSourceNode();
  99. const std::shared_ptr<CN_ANCHOR>& target = tmp.GetTargetNode();
  100. if( dset.unite( source->GetTag(), target->GetTag() ) )
  101. {
  102. if( tmp.GetWeight() > 0 )
  103. m_rnEdges.push_back( tmp );
  104. }
  105. }
  106. }
  107. class RN_NET::TRIANGULATOR_STATE
  108. {
  109. private:
  110. std::multiset<std::shared_ptr<CN_ANCHOR>, CN_PTR_CMP> m_allNodes;
  111. // Checks if all nodes in aNodes lie on a single line. Requires the nodes to
  112. // have unique coordinates!
  113. bool areNodesColinear( const std::vector<std::shared_ptr<CN_ANCHOR>>& aNodes ) const
  114. {
  115. if ( aNodes.size() <= 2 )
  116. return true;
  117. const VECTOR2I p0( aNodes[0]->Pos() );
  118. const VECTOR2I v0( aNodes[1]->Pos() - p0 );
  119. for( unsigned i = 2; i < aNodes.size(); i++ )
  120. {
  121. const VECTOR2I v1 = aNodes[i]->Pos() - p0;
  122. if( v0.Cross( v1 ) != 0 )
  123. return false;
  124. }
  125. return true;
  126. }
  127. public:
  128. void Clear()
  129. {
  130. m_allNodes.clear();
  131. }
  132. void AddNode( std::shared_ptr<CN_ANCHOR> aNode )
  133. {
  134. m_allNodes.insert( aNode );
  135. }
  136. void Triangulate( std::vector<CN_EDGE>& mstEdges )
  137. {
  138. std::vector<double> node_pts;
  139. std::vector<std::shared_ptr<CN_ANCHOR>> anchors;
  140. std::vector< std::vector<std::shared_ptr<CN_ANCHOR>> > anchorChains( m_allNodes.size() );
  141. node_pts.reserve( 2 * m_allNodes.size() );
  142. anchors.reserve( m_allNodes.size() );
  143. auto addEdge =
  144. [&]( const std::shared_ptr<CN_ANCHOR>& src, const std::shared_ptr<CN_ANCHOR>& dst )
  145. {
  146. mstEdges.emplace_back( src, dst, src->Dist( *dst ) );
  147. };
  148. std::shared_ptr<CN_ANCHOR> prev = nullptr;
  149. for( const std::shared_ptr<CN_ANCHOR>& n : m_allNodes )
  150. {
  151. if( !prev || prev->Pos() != n->Pos() )
  152. {
  153. node_pts.push_back( n->Pos().x );
  154. node_pts.push_back( n->Pos().y );
  155. anchors.push_back( n );
  156. prev = n;
  157. }
  158. anchorChains[anchors.size() - 1].push_back( n );
  159. }
  160. if( anchors.size() < 2 )
  161. {
  162. return;
  163. }
  164. else if( areNodesColinear( anchors ) )
  165. {
  166. // special case: all nodes are on the same line - there's no
  167. // triangulation for such set. In this case, we sort along any coordinate
  168. // and chain the nodes together.
  169. for( size_t i = 0; i < anchors.size() - 1; i++ )
  170. addEdge( anchors[i], anchors[i + 1] );
  171. }
  172. else
  173. {
  174. delaunator::Delaunator delaunator( node_pts );
  175. auto& triangles = delaunator.triangles;
  176. for( size_t i = 0; i < triangles.size(); i += 3 )
  177. {
  178. addEdge( anchors[triangles[i]], anchors[triangles[i + 1]] );
  179. addEdge( anchors[triangles[i + 1]], anchors[triangles[i + 2]] );
  180. addEdge( anchors[triangles[i + 2]], anchors[triangles[i]] );
  181. }
  182. for( size_t i = 0; i < delaunator.halfedges.size(); i++ )
  183. {
  184. if( delaunator.halfedges[i] == delaunator::INVALID_INDEX )
  185. continue;
  186. addEdge( anchors[triangles[i]], anchors[triangles[delaunator.halfedges[i]]] );
  187. }
  188. }
  189. for( size_t i = 0; i < anchorChains.size(); i++ )
  190. {
  191. std::vector<std::shared_ptr<CN_ANCHOR>>& chain = anchorChains[i];
  192. if( chain.size() < 2 )
  193. continue;
  194. std::sort( chain.begin(), chain.end(),
  195. [] ( const std::shared_ptr<CN_ANCHOR>& a, const std::shared_ptr<CN_ANCHOR>& b )
  196. {
  197. return a->GetCluster().get() < b->GetCluster().get();
  198. } );
  199. for( unsigned int j = 1; j < chain.size(); j++ )
  200. {
  201. const std::shared_ptr<CN_ANCHOR>& prevNode = chain[j - 1];
  202. const std::shared_ptr<CN_ANCHOR>& curNode = chain[j];
  203. int weight = prevNode->GetCluster() != curNode->GetCluster() ? 1 : 0;
  204. mstEdges.emplace_back( prevNode, curNode, weight );
  205. }
  206. }
  207. }
  208. };
  209. RN_NET::RN_NET() : m_dirty( true )
  210. {
  211. m_triangulator.reset( new TRIANGULATOR_STATE );
  212. }
  213. void RN_NET::compute()
  214. {
  215. // Special cases do not need complicated algorithms (actually, it does not work well with
  216. // the Delaunay triangulator)
  217. if( m_nodes.size() <= 2 )
  218. {
  219. m_rnEdges.clear();
  220. // Check if the only possible connection exists
  221. if( m_boardEdges.size() == 0 && m_nodes.size() == 2 )
  222. {
  223. auto last = ++m_nodes.begin();
  224. // There can be only one possible connection, but it is missing
  225. CN_EDGE edge ( *m_nodes.begin(), *last );
  226. edge.GetSourceNode()->SetTag( 0 );
  227. edge.GetTargetNode()->SetTag( 1 );
  228. m_rnEdges.push_back( edge );
  229. }
  230. else
  231. {
  232. // Set tags to m_nodes as connected
  233. for( const std::shared_ptr<CN_ANCHOR>& node : m_nodes )
  234. node->SetTag( 0 );
  235. }
  236. return;
  237. }
  238. m_triangulator->Clear();
  239. for( const std::shared_ptr<CN_ANCHOR>& n : m_nodes )
  240. m_triangulator->AddNode( n );
  241. std::vector<CN_EDGE> triangEdges;
  242. triangEdges.reserve( m_nodes.size() + m_boardEdges.size() );
  243. #ifdef PROFILE
  244. PROF_TIMER cnt( "triangulate" );
  245. #endif
  246. m_triangulator->Triangulate( triangEdges );
  247. #ifdef PROFILE
  248. cnt.Show();
  249. #endif
  250. for( const CN_EDGE& e : m_boardEdges )
  251. triangEdges.emplace_back( e );
  252. std::sort( triangEdges.begin(), triangEdges.end() );
  253. // Get the minimal spanning tree
  254. #ifdef PROFILE
  255. PROF_TIMER cnt2( "mst" );
  256. #endif
  257. kruskalMST( triangEdges );
  258. #ifdef PROFILE
  259. cnt2.Show();
  260. #endif
  261. }
  262. void RN_NET::optimizeRNEdges()
  263. {
  264. auto findZoneAnchor =
  265. [&]( const VECTOR2I& aPos, const LSET& aLayerSet,
  266. const std::shared_ptr<CN_ANCHOR> aAnchor )
  267. {
  268. SEG::ecoord closest_dist_sq = ( aAnchor->Pos() - aPos ).SquaredEuclideanNorm();
  269. VECTOR2I closest_pt;
  270. CN_ITEM* closest_item = nullptr;
  271. for( CN_ITEM* item : aAnchor->Item()->ConnectedItems() )
  272. {
  273. CN_ZONE_LAYER* zoneLayer = dynamic_cast<CN_ZONE_LAYER*>( item );
  274. if( zoneLayer && aLayerSet.test( zoneLayer->Layer() ) )
  275. {
  276. const std::vector<VECTOR2I>& pts = zoneLayer->GetOutline().CPoints();
  277. for( VECTOR2I pt : pts )
  278. {
  279. SEG::ecoord dist_sq = ( pt - aPos ).SquaredEuclideanNorm();
  280. if( dist_sq < closest_dist_sq )
  281. {
  282. closest_pt = pt;
  283. closest_item = zoneLayer;
  284. closest_dist_sq = dist_sq;
  285. }
  286. }
  287. }
  288. }
  289. if( closest_item )
  290. {
  291. closest_item->AddAnchor( closest_pt );
  292. return closest_item->GetAnchorItem( closest_item->GetAnchorItemCount() - 1 );
  293. }
  294. return aAnchor;
  295. };
  296. auto findZoneToZoneAnchors =
  297. [&]( std::shared_ptr<CN_ANCHOR>& a, std::shared_ptr<CN_ANCHOR>& b )
  298. {
  299. for( CN_ITEM* itemA : a->Item()->ConnectedItems() )
  300. {
  301. CN_ZONE_LAYER* zoneLayerA = dynamic_cast<CN_ZONE_LAYER*>( itemA );
  302. if( !zoneLayerA )
  303. continue;
  304. for( CN_ITEM* itemB : b->Item()->ConnectedItems() )
  305. {
  306. CN_ZONE_LAYER* zoneLayerB = dynamic_cast<CN_ZONE_LAYER*>( itemB );
  307. if( zoneLayerB && zoneLayerB->Layer() == zoneLayerA->Layer() )
  308. {
  309. // Process the first matching layer. We don't really care if it's
  310. // the "best" layer or not, as anything will be better than the
  311. // original anchors (which are connected to the zone and so certainly
  312. // don't look like they should have ratsnest lines coming off them).
  313. VECTOR2I startA = zoneLayerA->GetOutline().GetPoint( 0 );
  314. VECTOR2I startB = zoneLayerB->GetOutline().GetPoint( 0 );
  315. const SHAPE* shapeA = &zoneLayerA->GetOutline();
  316. const SHAPE* shapeB = &zoneLayerB->GetOutline();
  317. int startDist = ( startA - startB ).EuclideanNorm();
  318. VECTOR2I ptA;
  319. shapeA->Collide( shapeB, startDist + 10, nullptr, &ptA );
  320. zoneLayerA->AddAnchor( ptA );
  321. a = zoneLayerA->GetAnchorItem( zoneLayerA->GetAnchorItemCount() - 1 );
  322. VECTOR2I ptB;
  323. shapeB->Collide( shapeA, startDist + 10, nullptr, &ptB );
  324. zoneLayerB->AddAnchor( ptB );
  325. b = zoneLayerB->GetAnchorItem( zoneLayerB->GetAnchorItemCount() - 1 );
  326. return;
  327. }
  328. }
  329. }
  330. };
  331. for( CN_EDGE& edge : m_rnEdges )
  332. {
  333. std::shared_ptr<CN_ANCHOR> source = edge.GetSourceNode();
  334. std::shared_ptr<CN_ANCHOR> target = edge.GetTargetNode();
  335. if( source->ConnectedItemsCount() == 0 )
  336. {
  337. edge.SetTargetNode( findZoneAnchor( source->Pos(), source->Parent()->GetLayerSet(),
  338. target ) );
  339. }
  340. else if( target->ConnectedItemsCount() == 0 )
  341. {
  342. edge.SetSourceNode( findZoneAnchor( target->Pos(), target->Parent()->GetLayerSet(),
  343. source ) );
  344. }
  345. else
  346. {
  347. findZoneToZoneAnchors( source, target );
  348. edge.SetSourceNode( source );
  349. edge.SetTargetNode( target );
  350. }
  351. }
  352. }
  353. void RN_NET::Update()
  354. {
  355. compute();
  356. #ifdef PROFILE
  357. PROF_TIMER cnt( "optimize" );
  358. #endif
  359. optimizeRNEdges();
  360. #ifdef PROFILE
  361. cnt.Show();
  362. #endif
  363. m_dirty = false;
  364. }
  365. void RN_NET::Clear()
  366. {
  367. m_rnEdges.clear();
  368. m_boardEdges.clear();
  369. m_nodes.clear();
  370. m_dirty = true;
  371. }
  372. void RN_NET::AddCluster( std::shared_ptr<CN_CLUSTER> aCluster )
  373. {
  374. std::shared_ptr<CN_ANCHOR> firstAnchor;
  375. for( CN_ITEM* item : *aCluster )
  376. {
  377. std::vector<std::shared_ptr<CN_ANCHOR>>& anchors = item->Anchors();
  378. unsigned int nAnchors = dynamic_cast<CN_ZONE_LAYER*>( item ) ? 1 : anchors.size();
  379. if( nAnchors > anchors.size() )
  380. nAnchors = anchors.size();
  381. for( unsigned int i = 0; i < nAnchors; i++ )
  382. {
  383. anchors[i]->SetCluster( aCluster );
  384. m_nodes.insert( anchors[i] );
  385. if( firstAnchor )
  386. {
  387. if( firstAnchor != anchors[i] )
  388. m_boardEdges.emplace_back( firstAnchor, anchors[i], 0 );
  389. }
  390. else
  391. {
  392. firstAnchor = anchors[i];
  393. }
  394. }
  395. }
  396. }
  397. bool RN_NET::NearestBicoloredPair( RN_NET* aOtherNet, VECTOR2I& aPos1, VECTOR2I& aPos2 ) const
  398. {
  399. bool rv = false;
  400. SEG::ecoord distMax_sq = VECTOR2I::ECOORD_MAX;
  401. auto verify =
  402. [&]( const std::shared_ptr<CN_ANCHOR>& aTestNode1,
  403. const std::shared_ptr<CN_ANCHOR>& aTestNode2 )
  404. {
  405. VECTOR2I diff = aTestNode1->Pos() - aTestNode2->Pos();
  406. SEG::ecoord dist_sq = diff.SquaredEuclideanNorm();
  407. if( dist_sq < distMax_sq )
  408. {
  409. rv = true;
  410. distMax_sq = dist_sq;
  411. aPos1 = aTestNode1->Pos();
  412. aPos2 = aTestNode2->Pos();
  413. }
  414. };
  415. std::multiset<std::shared_ptr<CN_ANCHOR>, CN_PTR_CMP> nodes_b;
  416. std::copy_if( m_nodes.begin(), m_nodes.end(), std::inserter( nodes_b, nodes_b.end() ),
  417. []( const std::shared_ptr<CN_ANCHOR> &aVal )
  418. { return !aVal->GetNoLine(); } );
  419. /// Sweep-line algorithm to cut the number of comparisons to find the closest point
  420. ///
  421. /// Step 1: The outer loop needs to be the subset (selected nodes) as it is a linear search
  422. for( const std::shared_ptr<CN_ANCHOR>& nodeA : aOtherNet->m_nodes )
  423. {
  424. if( nodeA->GetNoLine() )
  425. continue;
  426. /// Step 2: O( log n ) search to identify a close element ordered by x
  427. /// The fwd_it iterator will move forward through the elements while
  428. /// the rev_it iterator will move backward through the same set
  429. auto fwd_it = nodes_b.lower_bound( nodeA );
  430. auto rev_it = std::make_reverse_iterator( fwd_it );
  431. for( ; fwd_it != nodes_b.end(); ++fwd_it )
  432. {
  433. const std::shared_ptr<CN_ANCHOR>& nodeB = *fwd_it;
  434. SEG::ecoord distX_sq = SEG::Square( nodeA->Pos().x - nodeB->Pos().x );
  435. /// As soon as the x distance (primary sort) is larger than the smallest distance,
  436. /// stop checking further elements
  437. if( distX_sq > distMax_sq )
  438. break;
  439. verify( nodeA, nodeB );
  440. }
  441. /// Step 3: using the same starting point, check points backwards for closer points
  442. for( ; rev_it != nodes_b.rend(); ++rev_it )
  443. {
  444. const std::shared_ptr<CN_ANCHOR>& nodeB = *rev_it;
  445. SEG::ecoord distX_sq = SEG::Square( nodeA->Pos().x - nodeB->Pos().x );
  446. if( distX_sq > distMax_sq )
  447. break;
  448. verify( nodeA, nodeB );
  449. }
  450. }
  451. return rv;
  452. }