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.

550 lines
15 KiB

11 years ago
11 years ago
11 years ago
5 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. /*
  2. * KiRouter - a push-and-(sometimes-)shove PCB router
  3. *
  4. * Copyright (C) 2013-2015 CERN
  5. * Copyright (C) 2016-2021 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 modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include "pns_line.h"
  22. #include "pns_segment.h"
  23. #include "pns_node.h"
  24. #include "pns_joint.h"
  25. #include "pns_solid.h"
  26. #include "pns_router.h"
  27. #include "pns_utils.h"
  28. #include "pns_diff_pair.h"
  29. #include "pns_topology.h"
  30. #include <board.h>
  31. #include <pad.h>
  32. namespace PNS {
  33. bool TOPOLOGY::SimplifyLine( LINE* aLine )
  34. {
  35. if( !aLine->IsLinked() || !aLine->SegmentCount() )
  36. return false;
  37. LINKED_ITEM* root = aLine->GetLink( 0 );
  38. LINE l = m_world->AssembleLine( root );
  39. SHAPE_LINE_CHAIN simplified( l.CLine() );
  40. simplified.Simplify();
  41. if( simplified.PointCount() != l.PointCount() )
  42. {
  43. m_world->Remove( l );
  44. LINE lnew( l );
  45. lnew.SetShape( simplified );
  46. m_world->Add( lnew );
  47. return true;
  48. }
  49. return false;
  50. }
  51. const TOPOLOGY::JOINT_SET TOPOLOGY::ConnectedJoints( JOINT* aStart )
  52. {
  53. std::deque<JOINT*> searchQueue;
  54. JOINT_SET processed;
  55. searchQueue.push_back( aStart );
  56. processed.insert( aStart );
  57. while( !searchQueue.empty() )
  58. {
  59. JOINT* current = searchQueue.front();
  60. searchQueue.pop_front();
  61. for( ITEM* item : current->LinkList() )
  62. {
  63. if( item->OfKind( ITEM::SEGMENT_T ) )
  64. {
  65. SEGMENT* seg = static_cast<SEGMENT*>( item );
  66. JOINT* a = m_world->FindJoint( seg->Seg().A, seg );
  67. JOINT* b = m_world->FindJoint( seg->Seg().B, seg );
  68. JOINT* next = ( *a == *current ) ? b : a;
  69. if( processed.find( next ) == processed.end() )
  70. {
  71. processed.insert( next );
  72. searchQueue.push_back( next );
  73. }
  74. }
  75. }
  76. }
  77. return processed;
  78. }
  79. bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
  80. {
  81. LINE track( *aTrack );
  82. VECTOR2I end;
  83. if( !track.PointCount() )
  84. return false;
  85. std::unique_ptr<NODE> tmpNode( m_world->Branch() );
  86. tmpNode->Add( track );
  87. JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track );
  88. if( !jt || jt->Net() <= 0 )
  89. return false;
  90. if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 )
  91. || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
  92. {
  93. end = jt->Pos();
  94. }
  95. else
  96. {
  97. int anchor;
  98. TOPOLOGY topo( tmpNode.get() );
  99. ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
  100. if( !it )
  101. return false;
  102. end = it->Anchor( anchor );
  103. }
  104. aRatLine.Clear();
  105. aRatLine.Append( track.CPoint( -1 ) );
  106. aRatLine.Append( end );
  107. return true;
  108. }
  109. ITEM* TOPOLOGY::NearestUnconnectedItem( JOINT* aStart, int* aAnchor, int aKindMask )
  110. {
  111. std::set<ITEM*> disconnected;
  112. m_world->AllItemsInNet( aStart->Net(), disconnected );
  113. for( const JOINT* jt : ConnectedJoints( aStart ) )
  114. {
  115. for( ITEM* link : jt->LinkList() )
  116. {
  117. if( disconnected.find( link ) != disconnected.end() )
  118. disconnected.erase( link );
  119. }
  120. }
  121. int best_dist = INT_MAX;
  122. ITEM* best = nullptr;
  123. for( ITEM* item : disconnected )
  124. {
  125. if( item->OfKind( aKindMask ) )
  126. {
  127. for( int i = 0; i < item->AnchorCount(); i++ )
  128. {
  129. VECTOR2I p = item->Anchor( i );
  130. int d = ( p - aStart->Pos() ).EuclideanNorm();
  131. if( d < best_dist )
  132. {
  133. best_dist = d;
  134. best = item;
  135. if( aAnchor )
  136. *aAnchor = i;
  137. }
  138. }
  139. }
  140. }
  141. return best;
  142. }
  143. bool TOPOLOGY::followTrivialPath( LINE* aLine, bool aLeft, ITEM_SET& aSet,
  144. std::set<ITEM*>& aVisited, JOINT** aTerminalJoint )
  145. {
  146. assert( aLine->IsLinked() );
  147. VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 );
  148. LINKED_ITEM* last = aLeft ? aLine->Links().front() : aLine->Links().back();
  149. JOINT* jt = m_world->FindJoint( anchor, aLine );
  150. assert( jt != nullptr );
  151. aVisited.insert( last );
  152. if( jt->IsNonFanoutVia() || jt->IsTraceWidthChange() )
  153. {
  154. ITEM* via = nullptr;
  155. SEGMENT* next_seg = nullptr;
  156. for( ITEM* link : jt->Links().Items() )
  157. {
  158. if( link->OfKind( ITEM::VIA_T ) )
  159. via = link;
  160. else if( aVisited.find( link ) == aVisited.end() )
  161. next_seg = static_cast<SEGMENT*>( link );
  162. }
  163. if( !next_seg )
  164. {
  165. if( aTerminalJoint )
  166. *aTerminalJoint = jt;
  167. return false;
  168. }
  169. LINE l = m_world->AssembleLine( next_seg );
  170. VECTOR2I nextAnchor = ( aLeft ? l.CLine().CPoint( -1 ) : l.CLine().CPoint( 0 ) );
  171. if( nextAnchor != anchor )
  172. {
  173. l.Reverse();
  174. }
  175. if( aLeft )
  176. {
  177. if( via )
  178. aSet.Prepend( via );
  179. aSet.Prepend( l );
  180. }
  181. else
  182. {
  183. if( via )
  184. aSet.Add( via );
  185. aSet.Add( l );
  186. }
  187. return followTrivialPath( &l, aLeft, aSet, aVisited, aTerminalJoint );
  188. }
  189. if( aTerminalJoint )
  190. *aTerminalJoint = jt;
  191. return false;
  192. }
  193. const ITEM_SET TOPOLOGY::AssembleTrivialPath( ITEM* aStart,
  194. std::pair<JOINT*, JOINT*>* aTerminalJoints,
  195. bool aFollowLockedSegments )
  196. {
  197. ITEM_SET path;
  198. std::set<ITEM*> visited;
  199. LINKED_ITEM* seg = nullptr;
  200. if( aStart->Kind() == ITEM::VIA_T )
  201. {
  202. VIA* via = static_cast<VIA*>( aStart );
  203. JOINT* jt = m_world->FindJoint( via->Pos(), via );
  204. if( !jt->IsNonFanoutVia() )
  205. return ITEM_SET();
  206. for( const ITEM_SET::ENTRY& entry : jt->Links().Items() )
  207. {
  208. if( entry.item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
  209. {
  210. seg = static_cast<LINKED_ITEM*>( entry.item );
  211. break;
  212. }
  213. }
  214. }
  215. else if( aStart->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
  216. {
  217. seg = static_cast<LINKED_ITEM*>( aStart );
  218. }
  219. if( !seg )
  220. return ITEM_SET();
  221. // Assemble a line following through locked segments
  222. // TODO: consider if we want to allow tuning lines with different widths in the future
  223. LINE l = m_world->AssembleLine( seg, nullptr, false, true );
  224. path.Add( l );
  225. JOINT* jointA = nullptr;
  226. JOINT* jointB = nullptr;
  227. followTrivialPath( &l, false, path, visited, &jointA );
  228. followTrivialPath( &l, true, path, visited, &jointB );
  229. if( aTerminalJoints )
  230. {
  231. wxASSERT( jointA && jointB );
  232. *aTerminalJoints = std::make_pair( jointA, jointB );
  233. }
  234. return path;
  235. }
  236. const ITEM_SET TOPOLOGY::AssembleTuningPath( ITEM* aStart, SOLID** aStartPad, SOLID** aEndPad )
  237. {
  238. std::pair<JOINT*, JOINT*> joints;
  239. ITEM_SET initialPath = AssembleTrivialPath( aStart, &joints, true );
  240. PAD* padA = nullptr;
  241. PAD* padB = nullptr;
  242. auto getPadFromJoint =
  243. []( JOINT* aJoint, PAD** aTargetPad, SOLID** aTargetSolid )
  244. {
  245. for( ITEM* item : aJoint->LinkList() )
  246. {
  247. if( item->OfKind( ITEM::SOLID_T ) )
  248. {
  249. BOARD_ITEM* bi = static_cast<SOLID*>( item )->Parent();
  250. if( bi->Type() == PCB_PAD_T )
  251. {
  252. *aTargetPad = static_cast<PAD*>( bi );
  253. if( aTargetSolid )
  254. *aTargetSolid = static_cast<SOLID*>( item );
  255. }
  256. break;
  257. }
  258. }
  259. };
  260. if( joints.first )
  261. getPadFromJoint( joints.first, &padA, aStartPad );
  262. if( joints.second )
  263. getPadFromJoint( joints.second, &padB, aEndPad );
  264. if( !padA && !padB )
  265. return initialPath;
  266. auto clipLineToPad =
  267. []( SHAPE_LINE_CHAIN& aLine, PAD* aPad, bool aForward = true )
  268. {
  269. const std::shared_ptr<SHAPE_POLY_SET>& shape = aPad->GetEffectivePolygon();
  270. int start = aForward ? 0 : aLine.PointCount() - 1;
  271. int delta = aForward ? 1 : -1;
  272. // Skip the "first" (or last) vertex, we already know it's contained in the pad
  273. int clip = start;
  274. for( int vertex = start + delta;
  275. aForward ? vertex < aLine.PointCount() : vertex >= 0;
  276. vertex += delta )
  277. {
  278. SEG seg( aLine.GetPoint( vertex ), aLine.GetPoint( vertex - delta ) );
  279. bool containsA = shape->Contains( seg.A );
  280. bool containsB = shape->Contains( seg.B );
  281. if( containsA && containsB )
  282. {
  283. // Whole segment is inside: clip out this segment
  284. clip = vertex;
  285. }
  286. else if( containsB &&
  287. ( aForward ? vertex < aLine.PointCount() - 1 : vertex > 0 ) )
  288. {
  289. // Only one point inside: Find the intersection
  290. VECTOR2I loc;
  291. if( shape->Collide( seg, 0, nullptr, &loc ) )
  292. {
  293. aLine.Replace( vertex - delta, vertex - delta, loc );
  294. }
  295. }
  296. }
  297. if( !aForward && clip < start )
  298. aLine.Remove( clip + 1, start );
  299. else if( clip > start )
  300. aLine.Remove( start, clip - 1 );
  301. // Now connect the dots
  302. aLine.Insert( aForward ? 0 : aLine.PointCount(), aPad->GetPosition() );
  303. };
  304. auto processPad =
  305. [&]( JOINT* aJoint, PAD* aPad )
  306. {
  307. const std::shared_ptr<SHAPE_POLY_SET>& shape = aPad->GetEffectivePolygon();
  308. for( int idx = 0; idx < initialPath.Size(); idx++ )
  309. {
  310. if( initialPath[idx]->Kind() != ITEM::LINE_T )
  311. continue;
  312. LINE* line = static_cast<LINE*>( initialPath[idx] );
  313. if( !aPad->FlashLayer( line->Layer() ) )
  314. continue;
  315. const std::vector<VECTOR2I>& points = line->CLine().CPoints();
  316. if( points.front() != aJoint->Pos() && points.back() != aJoint->Pos() )
  317. continue;
  318. SHAPE_LINE_CHAIN& slc = line->Line();
  319. if( shape->Contains( slc.CPoint( 0 ) ) )
  320. clipLineToPad( slc, aPad, true );
  321. else if( shape->Contains( slc.CPoint( -1 ) ) )
  322. clipLineToPad( slc, aPad, false );
  323. }
  324. };
  325. if( padA )
  326. processPad( joints.first, padA );
  327. if( padB )
  328. processPad( joints.second, padB );
  329. return initialPath;
  330. }
  331. const ITEM_SET TOPOLOGY::ConnectedItems( JOINT* aStart, int aKindMask )
  332. {
  333. return ITEM_SET();
  334. }
  335. const ITEM_SET TOPOLOGY::ConnectedItems( ITEM* aStart, int aKindMask )
  336. {
  337. return ITEM_SET();
  338. }
  339. bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip );
  340. bool TOPOLOGY::AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair )
  341. {
  342. int refNet = aStart->Net();
  343. int coupledNet = m_world->GetRuleResolver()->DpCoupledNet( refNet );
  344. if( coupledNet < 0 )
  345. return false;
  346. std::set<ITEM*> coupledItems;
  347. m_world->AllItemsInNet( coupledNet, coupledItems );
  348. SEGMENT* coupledSeg = nullptr, *refSeg;
  349. int minDist = std::numeric_limits<int>::max();
  350. if( ( refSeg = dyn_cast<SEGMENT*>( aStart ) ) != nullptr )
  351. {
  352. for( ITEM* item : coupledItems )
  353. {
  354. if( SEGMENT* s = dyn_cast<SEGMENT*>( item ) )
  355. {
  356. if( s->Layers().Start() == refSeg->Layers().Start() &&
  357. s->Width() == refSeg->Width() )
  358. {
  359. int dist = s->Seg().Distance( refSeg->Seg() );
  360. bool isParallel = refSeg->Seg().ApproxParallel( s->Seg(), DP_PARALLELITY_THRESHOLD );
  361. SEG p_clip, n_clip;
  362. bool isCoupled = commonParallelProjection( refSeg->Seg(), s->Seg(), p_clip,
  363. n_clip );
  364. if( isParallel && isCoupled && dist < minDist )
  365. {
  366. minDist = dist;
  367. coupledSeg = s;
  368. }
  369. }
  370. }
  371. }
  372. }
  373. else
  374. {
  375. return false;
  376. }
  377. if( !coupledSeg )
  378. return false;
  379. LINE lp = m_world->AssembleLine( refSeg );
  380. LINE ln = m_world->AssembleLine( coupledSeg );
  381. if( m_world->GetRuleResolver()->DpNetPolarity( refNet ) < 0 )
  382. {
  383. std::swap( lp, ln );
  384. }
  385. int gap = -1;
  386. if( refSeg->Seg().ApproxParallel( coupledSeg->Seg(), DP_PARALLELITY_THRESHOLD ) )
  387. {
  388. // Segments are parallel -> compute pair gap
  389. const VECTOR2I refDir = refSeg->Anchor( 1 ) - refSeg->Anchor( 0 );
  390. const VECTOR2I displacement = refSeg->Anchor( 1 ) - coupledSeg->Anchor( 1 );
  391. gap = (int) std::abs( refDir.Cross( displacement ) / refDir.EuclideanNorm() ) - lp.Width();
  392. }
  393. aPair = DIFF_PAIR( lp, ln );
  394. aPair.SetWidth( lp.Width() );
  395. aPair.SetLayers( lp.Layers() );
  396. aPair.SetGap( gap );
  397. return true;
  398. }
  399. const std::set<ITEM*> TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer )
  400. {
  401. std::set<ITEM*> visited;
  402. std::deque<ITEM*> pending;
  403. pending.push_back( aStart );
  404. while( !pending.empty() )
  405. {
  406. NODE::OBSTACLES obstacles;
  407. ITEM* top = pending.front();
  408. pending.pop_front();
  409. visited.insert( top );
  410. m_world->QueryColliding( top, obstacles, ITEM::ANY_T, -1, false );
  411. for( OBSTACLE& obs : obstacles )
  412. {
  413. if( visited.find( obs.m_item ) == visited.end() &&
  414. obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
  415. {
  416. visited.insert( obs.m_item );
  417. pending.push_back( obs.m_item );
  418. }
  419. }
  420. }
  421. return visited;
  422. }
  423. }