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.

1409 lines
35 KiB

11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
9 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
9 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
9 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 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
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 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
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
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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
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
10 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-2014 CERN
  5. * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #define PNS_DEBUG
  21. #include <deque>
  22. #include <cassert>
  23. #include "range.h"
  24. #include "pns_line.h"
  25. #include "pns_node.h"
  26. #include "pns_walkaround.h"
  27. #include "pns_shove.h"
  28. #include "pns_solid.h"
  29. #include "pns_optimizer.h"
  30. #include "pns_via.h"
  31. #include "pns_utils.h"
  32. #include "pns_router.h"
  33. #include "pns_shove.h"
  34. #include "pns_utils.h"
  35. #include "pns_topology.h"
  36. #include "time_limit.h"
  37. #include <profile.h>
  38. void PNS_SHOVE::replaceItems( PNS_ITEM* aOld, PNS_ITEM* aNew )
  39. {
  40. OPT_BOX2I changed_area = ChangedArea( aOld, aNew );
  41. if( changed_area )
  42. {
  43. m_affectedAreaSum = m_affectedAreaSum ? m_affectedAreaSum->Merge( *changed_area ) : *changed_area;
  44. }
  45. m_currentNode->Replace( aOld, aNew );
  46. }
  47. int PNS_SHOVE::getClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const
  48. {
  49. if( m_forceClearance >= 0 )
  50. return m_forceClearance;
  51. return m_currentNode->GetClearance( aA, aB );
  52. }
  53. void PNS_SHOVE::sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew )
  54. {
  55. assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) );
  56. assert( aOld->CPoint( -1 ) == aNew->CPoint( -1 ) );
  57. }
  58. PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) :
  59. PNS_ALGO_BASE( aRouter )
  60. {
  61. m_forceClearance = -1;
  62. m_root = aWorld;
  63. m_currentNode = aWorld;
  64. // Initialize other temporary variables:
  65. m_draggedVia = NULL;
  66. m_iter = 0;
  67. m_multiLineMode = false;
  68. }
  69. PNS_SHOVE::~PNS_SHOVE()
  70. {
  71. }
  72. PNS_LINE PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex )
  73. {
  74. return m_currentNode->AssembleLine( const_cast<PNS_SEGMENT*>( aSeg ), aIndex, true );
  75. }
  76. // A dumb function that checks if the shoved line is shoved the right way, e.g.
  77. // visually "outwards" of the line/via applying pressure on it. Unfortunately there's no
  78. // mathematical concept of orientation of an open curve, so we use some primitive heuristics:
  79. // if the shoved line wraps around the start of the "pusher", it's likely shoved in wrong direction.
  80. bool PNS_SHOVE::checkBumpDirection( const PNS_LINE& aCurrent, const PNS_LINE& aShoved ) const
  81. {
  82. const SEG& ss = aCurrent.CSegment( 0 );
  83. int dist = getClearance( &aCurrent, &aShoved ) + PNS_HULL_MARGIN;
  84. dist += aCurrent.Width() / 2;
  85. dist += aShoved.Width() / 2;
  86. const VECTOR2I ps = ss.A - ( ss.B - ss.A ).Resize( dist );
  87. return !aShoved.CLine().PointOnEdge( ps );
  88. }
  89. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::walkaroundLoneVia( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
  90. PNS_LINE& aShoved )
  91. {
  92. int clearance = getClearance( &aCurrent, &aObstacle );
  93. const SHAPE_LINE_CHAIN hull = aCurrent.Via().Hull( clearance, aObstacle.Width() );
  94. SHAPE_LINE_CHAIN path_cw, path_ccw;
  95. aObstacle.Walkaround( hull, path_cw, true );
  96. aObstacle.Walkaround( hull, path_ccw, false );
  97. const SHAPE_LINE_CHAIN& shortest = path_ccw.Length() < path_cw.Length() ? path_ccw : path_cw;
  98. if( shortest.PointCount() < 2 )
  99. return SH_INCOMPLETE;
  100. if( aObstacle.CPoint( -1 ) != shortest.CPoint( -1 ) )
  101. return SH_INCOMPLETE;
  102. if( aObstacle.CPoint( 0 ) != shortest.CPoint( 0 ) )
  103. return SH_INCOMPLETE;
  104. aShoved.SetShape( shortest );
  105. if( m_currentNode->CheckColliding( &aShoved, &aCurrent ) )
  106. return SH_INCOMPLETE;
  107. return SH_OK;
  108. }
  109. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
  110. PNS_LINE& aShoved, const HULL_SET& aHulls )
  111. {
  112. const SHAPE_LINE_CHAIN& obs = aObstacle.CLine();
  113. int attempt;
  114. for( attempt = 0; attempt < 4; attempt++ )
  115. {
  116. bool invertTraversal = ( attempt >= 2 );
  117. bool clockwise = attempt % 2;
  118. int vFirst = -1, vLast = -1;
  119. SHAPE_LINE_CHAIN path;
  120. PNS_LINE l( aObstacle );
  121. for( int i = 0; i < (int) aHulls.size(); i++ )
  122. {
  123. const SHAPE_LINE_CHAIN& hull = aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
  124. l.Walkaround( hull, path, clockwise );
  125. path.Simplify();
  126. l.SetShape( path );
  127. }
  128. for( int i = 0; i < std::min( path.PointCount(), obs.PointCount() ); i++ )
  129. {
  130. if( path.CPoint( i ) != obs.CPoint( i ) )
  131. {
  132. vFirst = i;
  133. break;
  134. }
  135. }
  136. int k = obs.PointCount() - 1;
  137. for( int i = path.PointCount() - 1; i >= 0 && k >= 0; i--, k-- )
  138. {
  139. if( path.CPoint( i ) != obs.CPoint( k ) )
  140. {
  141. vLast = i;
  142. break;
  143. }
  144. }
  145. if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( aObstacle.CLine() ) )
  146. {
  147. wxLogTrace( "PNS", "attempt %d fail vfirst-last", attempt );
  148. continue;
  149. }
  150. if( path.CPoint( -1 ) != obs.CPoint( -1 ) || path.CPoint( 0 ) != obs.CPoint( 0 ) )
  151. {
  152. wxLogTrace( "PNS", "attempt %d fail vend-start\n", attempt );
  153. continue;
  154. }
  155. if( !checkBumpDirection( aCurrent, l ) )
  156. {
  157. wxLogTrace( "PNS", "attempt %d fail direction-check", attempt );
  158. aShoved.SetShape( l.CLine() );
  159. continue;
  160. }
  161. if( path.SelfIntersecting() )
  162. {
  163. wxLogTrace( "PNS", "attempt %d fail self-intersect", attempt );
  164. continue;
  165. }
  166. bool colliding = m_currentNode->CheckColliding( &l, &aCurrent, PNS_ITEM::ANY, m_forceClearance );
  167. if( ( aCurrent.Marker() & MK_HEAD ) && !colliding )
  168. {
  169. PNS_JOINT* jtStart = m_currentNode->FindJoint( aCurrent.CPoint( 0 ), &aCurrent );
  170. for( PNS_ITEM* item : jtStart->LinkList() )
  171. {
  172. if( m_currentNode->CheckColliding( item, &l ) )
  173. colliding = true;
  174. }
  175. }
  176. if( colliding )
  177. {
  178. wxLogTrace( "PNS", "attempt %d fail coll-check", attempt );
  179. continue;
  180. }
  181. aShoved.SetShape( l.CLine() );
  182. return SH_OK;
  183. }
  184. return SH_INCOMPLETE;
  185. }
  186. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ProcessSingleLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
  187. PNS_LINE& aShoved )
  188. {
  189. aShoved.ClearSegmentLinks();
  190. bool obstacleIsHead = false;
  191. if( aObstacle.LinkedSegments() )
  192. {
  193. for( PNS_SEGMENT* s : *aObstacle.LinkedSegments() )
  194. if( s->Marker() & MK_HEAD )
  195. {
  196. obstacleIsHead = true;
  197. break;
  198. }
  199. }
  200. SHOVE_STATUS rv;
  201. bool viaOnEnd = aCurrent.EndsWithVia();
  202. if( viaOnEnd && ( !aCurrent.LayersOverlap( &aObstacle ) || aCurrent.SegmentCount() == 0 ) )
  203. {
  204. rv = walkaroundLoneVia( aCurrent, aObstacle, aShoved );
  205. }
  206. else
  207. {
  208. int w = aObstacle.Width();
  209. int n_segs = aCurrent.SegmentCount();
  210. int clearance = getClearance( &aCurrent, &aObstacle ) + 1;
  211. HULL_SET hulls;
  212. hulls.reserve( n_segs + 1 );
  213. for( int i = 0; i < n_segs; i++ )
  214. {
  215. PNS_SEGMENT seg( aCurrent, aCurrent.CSegment( i ) );
  216. SHAPE_LINE_CHAIN hull = seg.Hull( clearance, w );
  217. hulls.push_back( hull );
  218. }
  219. if( viaOnEnd )
  220. hulls.push_back( aCurrent.Via().Hull( clearance, w ) );
  221. rv = processHullSet( aCurrent, aObstacle, aShoved, hulls );
  222. }
  223. if( obstacleIsHead )
  224. aShoved.Mark( aShoved.Marker() | MK_HEAD );
  225. return rv;
  226. }
  227. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE& aCurrent, PNS_SEGMENT* aObstacleSeg )
  228. {
  229. int segIndex;
  230. PNS_LINE obstacleLine = assembleLine( aObstacleSeg, &segIndex );
  231. PNS_LINE shovedLine( obstacleLine );
  232. PNS_SEGMENT tmp( *aObstacleSeg );
  233. if( obstacleLine.HasLockedSegments() )
  234. return SH_TRY_WALK;
  235. SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine );
  236. const double extensionWalkThreshold = 1.0;
  237. double obsLen = obstacleLine.CLine().Length();
  238. double shovedLen = shovedLine.CLine().Length();
  239. double extensionFactor = 0.0;
  240. if( obsLen != 0.0f )
  241. extensionFactor = shovedLen / obsLen - 1.0;
  242. if( extensionFactor > extensionWalkThreshold )
  243. return SH_TRY_WALK;
  244. assert( obstacleLine.LayersOverlap( &shovedLine ) );
  245. #ifdef DEBUG
  246. m_logger.NewGroup( "on-colliding-segment", m_iter );
  247. m_logger.Log( &tmp, 0, "obstacle-segment" );
  248. m_logger.Log( &aCurrent, 1, "current-line" );
  249. m_logger.Log( &obstacleLine, 2, "obstacle-line" );
  250. m_logger.Log( &shovedLine, 3, "shoved-line" );
  251. #endif
  252. if( rv == SH_OK )
  253. {
  254. if( shovedLine.Marker() & MK_HEAD )
  255. {
  256. if( m_multiLineMode )
  257. return SH_INCOMPLETE;
  258. m_newHead = shovedLine;
  259. }
  260. int rank = aCurrent.Rank();
  261. shovedLine.SetRank( rank - 1 );
  262. sanityCheck( &obstacleLine, &shovedLine );
  263. replaceItems( &obstacleLine, &shovedLine );
  264. if( !pushLine( shovedLine ) )
  265. rv = SH_INCOMPLETE;
  266. }
  267. return rv;
  268. }
  269. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle )
  270. {
  271. PNS_LINE shovedLine( aObstacle );
  272. SHOVE_STATUS rv = ProcessSingleLine( aCurrent, aObstacle, shovedLine );
  273. #ifdef DEBUG
  274. m_logger.NewGroup( "on-colliding-line", m_iter );
  275. m_logger.Log( &aObstacle, 0, "obstacle-line" );
  276. m_logger.Log( &aCurrent, 1, "current-line" );
  277. m_logger.Log( &shovedLine, 3, "shoved-line" );
  278. #endif
  279. if( rv == SH_OK )
  280. {
  281. if( shovedLine.Marker() & MK_HEAD )
  282. {
  283. if( m_multiLineMode )
  284. return SH_INCOMPLETE;
  285. m_newHead = shovedLine;
  286. }
  287. sanityCheck( &aObstacle, &shovedLine );
  288. replaceItems( &aObstacle, &shovedLine );
  289. int rank = aObstacle.Rank();
  290. shovedLine.SetRank( rank - 1 );
  291. if( !pushLine( shovedLine ) )
  292. {
  293. rv = SH_INCOMPLETE;
  294. }
  295. }
  296. return rv;
  297. }
  298. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_ITEM* aObstacle )
  299. {
  300. PNS_WALKAROUND walkaround( m_currentNode, Router() );
  301. PNS_LINE walkaroundLine( aCurrent );
  302. if( aCurrent.EndsWithVia() )
  303. {
  304. PNS_VIA vh = aCurrent.Via();
  305. PNS_VIA* via = NULL;
  306. PNS_JOINT* jtStart = m_currentNode->FindJoint( vh.Pos(), &aCurrent );
  307. if( !jtStart )
  308. return SH_INCOMPLETE;
  309. for( PNS_ITEM* item : jtStart->LinkList() )
  310. {
  311. if( item->OfKind( PNS_ITEM::VIA ) )
  312. {
  313. via = (PNS_VIA*) item;
  314. break;
  315. }
  316. }
  317. if( via && m_currentNode->CheckColliding( via, aObstacle ) )
  318. return onCollidingVia( aObstacle, via );
  319. }
  320. PNS_TOPOLOGY topo( m_currentNode );
  321. std::set<PNS_ITEM*> cluster = topo.AssembleCluster( aObstacle, aCurrent.Layers().Start() );
  322. #ifdef DEBUG
  323. m_logger.NewGroup( "on-colliding-solid-cluster", m_iter );
  324. for( PNS_ITEM* item : cluster )
  325. {
  326. m_logger.Log( item, 0, "cluster-entry" );
  327. }
  328. #endif
  329. walkaround.SetSolidsOnly( false );
  330. walkaround.RestrictToSet( true, cluster );
  331. walkaround.SetIterationLimit( 16 ); // fixme: make configurable
  332. int currentRank = aCurrent.Rank();
  333. int nextRank;
  334. bool success = false;
  335. for( int attempt = 0; attempt < 2; attempt++ )
  336. {
  337. if( attempt == 1 || Settings().JumpOverObstacles() )
  338. {
  339. nextRank = currentRank - 1;
  340. walkaround.SetSingleDirection( true );
  341. }
  342. else
  343. {
  344. nextRank = currentRank + 10000;
  345. walkaround.SetSingleDirection( false );
  346. }
  347. PNS_WALKAROUND::WALKAROUND_STATUS status = walkaround.Route( aCurrent, walkaroundLine, false );
  348. if( status != PNS_WALKAROUND::DONE )
  349. continue;
  350. walkaroundLine.ClearSegmentLinks();
  351. walkaroundLine.Unmark();
  352. walkaroundLine.Line().Simplify();
  353. if( walkaroundLine.HasLoops() )
  354. continue;
  355. if( aCurrent.Marker() & MK_HEAD )
  356. {
  357. walkaroundLine.Mark( MK_HEAD );
  358. if( m_multiLineMode )
  359. continue;
  360. m_newHead = walkaroundLine;
  361. }
  362. sanityCheck( &aCurrent, &walkaroundLine );
  363. if( !m_lineStack.empty() )
  364. {
  365. PNS_LINE lastLine = m_lineStack.front();
  366. if( m_currentNode->CheckColliding( &lastLine, &walkaroundLine ) )
  367. {
  368. PNS_LINE dummy( lastLine );
  369. if( ProcessSingleLine( walkaroundLine, lastLine, dummy ) == SH_OK )
  370. {
  371. success = true;
  372. break;
  373. }
  374. } else {
  375. success = true;
  376. break;
  377. }
  378. }
  379. }
  380. if(!success)
  381. return SH_INCOMPLETE;
  382. replaceItems( &aCurrent, &walkaroundLine );
  383. walkaroundLine.SetRank( nextRank );
  384. #ifdef DEBUG
  385. m_logger.NewGroup( "on-colliding-solid", m_iter );
  386. m_logger.Log( aObstacle, 0, "obstacle-solid" );
  387. m_logger.Log( &aCurrent, 1, "current-line" );
  388. m_logger.Log( &walkaroundLine, 3, "walk-line" );
  389. #endif
  390. popLine();
  391. if( !pushLine( walkaroundLine ) )
  392. return SH_INCOMPLETE;
  393. return SH_OK;
  394. }
  395. bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet )
  396. {
  397. bool rv = false;
  398. while( !m_nodeStack.empty() )
  399. {
  400. SPRINGBACK_TAG spTag = m_nodeStack.back();
  401. if( !spTag.m_node->CheckColliding( aHeadSet ) )
  402. {
  403. rv = true;
  404. delete spTag.m_node;
  405. m_nodeStack.pop_back();
  406. }
  407. else
  408. break;
  409. }
  410. return rv;
  411. }
  412. bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems,
  413. const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea )
  414. {
  415. SPRINGBACK_TAG st;
  416. OPT_BOX2I prev_area;
  417. if( !m_nodeStack.empty() )
  418. prev_area = m_nodeStack.back().m_affectedArea;
  419. st.m_node = aNode;
  420. st.m_cost = aCost;
  421. st.m_headItems = aHeadItems;
  422. if( aAffectedArea )
  423. {
  424. if( prev_area )
  425. st.m_affectedArea = prev_area->Merge( *aAffectedArea );
  426. else
  427. st.m_affectedArea = aAffectedArea;
  428. } else
  429. st.m_affectedArea = prev_area;
  430. m_nodeStack.push_back( st );
  431. return true;
  432. }
  433. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun )
  434. {
  435. LINE_PAIR_VEC draggedLines;
  436. VECTOR2I p0( aVia->Pos() );
  437. PNS_JOINT* jt = m_currentNode->FindJoint( p0, aVia );
  438. VECTOR2I p0_pushed( p0 + aForce );
  439. if( !jt )
  440. {
  441. wxLogTrace( "PNS", "weird, can't find the center-of-via joint\n" );
  442. return SH_INCOMPLETE;
  443. }
  444. if( aVia->IsLocked() )
  445. return SH_TRY_WALK;
  446. if( jt->IsLocked() )
  447. return SH_INCOMPLETE;
  448. while( aForce.x != 0 || aForce.y != 0 )
  449. {
  450. PNS_JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia );
  451. if( !jt_next )
  452. break;
  453. p0_pushed += aForce.Resize( 2 ); // make sure pushed via does not overlap with any existing joint
  454. }
  455. PNS_VIA* pushedVia = aVia->Clone();
  456. pushedVia->SetPos( p0_pushed );
  457. pushedVia->Mark( aVia->Marker() );
  458. if( aVia->Marker() & MK_HEAD )
  459. {
  460. m_draggedVia = pushedVia;
  461. m_draggedViaHeadSet.Clear();
  462. }
  463. for( PNS_ITEM* item : jt->LinkList() )
  464. {
  465. if( PNS_SEGMENT* seg = dyn_cast<PNS_SEGMENT*>( item ) )
  466. {
  467. LINE_PAIR lp;
  468. int segIndex;
  469. lp.first = assembleLine( seg, &segIndex );
  470. if( lp.first.HasLockedSegments() )
  471. return SH_TRY_WALK;
  472. assert( segIndex == 0 || ( segIndex == ( lp.first.SegmentCount() - 1 ) ) );
  473. if( segIndex == 0 )
  474. lp.first.Reverse();
  475. lp.second = lp.first;
  476. lp.second.ClearSegmentLinks();
  477. lp.second.DragCorner( p0_pushed, lp.second.CLine().Find( p0 ) );
  478. lp.second.AppendVia( *pushedVia );
  479. draggedLines.push_back( lp );
  480. if( aVia->Marker() & MK_HEAD )
  481. m_draggedViaHeadSet.Add( lp.second );
  482. }
  483. }
  484. m_draggedViaHeadSet.Add( pushedVia );
  485. if( aDryRun )
  486. return SH_OK;
  487. replaceItems( aVia, pushedVia );
  488. #ifdef DEBUG
  489. m_logger.Log( aVia, 0, "obstacle-via" );
  490. #endif
  491. pushedVia->SetRank( aCurrentRank - 1 );
  492. #ifdef DEBUG
  493. m_logger.Log( pushedVia, 1, "pushed-via" );
  494. #endif
  495. for( LINE_PAIR lp : draggedLines )
  496. {
  497. if( lp.first.Marker() & MK_HEAD )
  498. {
  499. lp.second.Mark( MK_HEAD );
  500. if( m_multiLineMode )
  501. return SH_INCOMPLETE;
  502. m_newHead = lp.second;
  503. }
  504. unwindStack( &lp.first );
  505. if( lp.second.SegmentCount() )
  506. {
  507. replaceItems( &lp.first, &lp.second );
  508. lp.second.SetRank( aCurrentRank - 1 );
  509. if( !pushLine( lp.second, true ) )
  510. return SH_INCOMPLETE;
  511. }
  512. else
  513. {
  514. m_currentNode->Remove( &lp.first );
  515. }
  516. #ifdef DEBUG
  517. m_logger.Log( &lp.first, 2, "fan-pre" );
  518. m_logger.Log( &lp.second, 3, "fan-post" );
  519. #endif
  520. }
  521. return SH_OK;
  522. }
  523. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia )
  524. {
  525. int clearance = getClearance( aCurrent, aObstacleVia ) ;
  526. LINE_PAIR_VEC draggedLines;
  527. bool colLine = false, colVia = false;
  528. PNS_LINE* currentLine = NULL;
  529. VECTOR2I mtvLine, mtvVia, mtv, mtvSolid;
  530. int rank = -1;
  531. if( aCurrent->OfKind( PNS_ITEM::LINE ) )
  532. {
  533. #ifdef DEBUG
  534. m_logger.NewGroup( "push-via-by-line", m_iter );
  535. m_logger.Log( aCurrent, 4, "current" );
  536. #endif
  537. currentLine = (PNS_LINE*) aCurrent;
  538. colLine = CollideShapes( aObstacleVia->Shape(), currentLine->Shape(),
  539. clearance + currentLine->Width() / 2 + PNS_HULL_MARGIN,
  540. true, mtvLine );
  541. if( currentLine->EndsWithVia() )
  542. colVia = CollideShapes( currentLine->Via().Shape(), aObstacleVia->Shape(),
  543. clearance + PNS_HULL_MARGIN, true, mtvVia );
  544. if( !colLine && !colVia )
  545. return SH_OK;
  546. if( colLine && colVia )
  547. mtv = mtvVia.EuclideanNorm() > mtvLine.EuclideanNorm() ? mtvVia : mtvLine;
  548. else if( colLine )
  549. mtv = mtvLine;
  550. else
  551. mtv = mtvVia;
  552. rank = currentLine->Rank();
  553. }
  554. else if( aCurrent->OfKind( PNS_ITEM::SOLID ) )
  555. {
  556. CollideShapes( aObstacleVia->Shape(), aCurrent->Shape(),
  557. clearance + PNS_HULL_MARGIN, true, mtvSolid );
  558. mtv = -mtvSolid;
  559. rank = aCurrent->Rank() + 10000;
  560. }
  561. return pushVia( aObstacleVia, mtv, rank );
  562. }
  563. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE& aCurrent, PNS_VIA* aObstacleVia )
  564. {
  565. int n = 0;
  566. PNS_LINE cur( aCurrent );
  567. cur.ClearSegmentLinks();
  568. PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
  569. PNS_LINE shoved( aCurrent );
  570. shoved.ClearSegmentLinks();
  571. cur.RemoveVia();
  572. unwindStack( &aCurrent );
  573. for( PNS_ITEM* item : jt->LinkList() )
  574. {
  575. if( item->OfKind( PNS_ITEM::SEGMENT ) && item->LayersOverlap( &aCurrent ) )
  576. {
  577. PNS_SEGMENT* seg = (PNS_SEGMENT*) item;
  578. PNS_LINE head = assembleLine( seg );
  579. head.AppendVia( *aObstacleVia );
  580. SHOVE_STATUS st = ProcessSingleLine( head, cur, shoved );
  581. if( st != SH_OK )
  582. {
  583. #ifdef DEBUG
  584. m_logger.NewGroup( "on-reverse-via-fail-shove", m_iter );
  585. m_logger.Log( aObstacleVia, 0, "the-via" );
  586. m_logger.Log( &aCurrent, 1, "current-line" );
  587. m_logger.Log( &shoved, 3, "shoved-line" );
  588. #endif
  589. return st;
  590. }
  591. cur.SetShape( shoved.CLine() );
  592. n++;
  593. }
  594. }
  595. if( !n )
  596. {
  597. #ifdef DEBUG
  598. m_logger.NewGroup( "on-reverse-via-fail-lonevia", m_iter );
  599. m_logger.Log( aObstacleVia, 0, "the-via" );
  600. m_logger.Log( &aCurrent, 1, "current-line" );
  601. #endif
  602. PNS_LINE head( aCurrent );
  603. head.Line().Clear();
  604. head.AppendVia( *aObstacleVia );
  605. head.ClearSegmentLinks();
  606. SHOVE_STATUS st = ProcessSingleLine( head, aCurrent, shoved );
  607. if( st != SH_OK )
  608. return st;
  609. cur.SetShape( shoved.CLine() );
  610. }
  611. if( aCurrent.EndsWithVia() )
  612. shoved.AppendVia( aCurrent.Via() );
  613. #ifdef DEBUG
  614. m_logger.NewGroup( "on-reverse-via", m_iter );
  615. m_logger.Log( aObstacleVia, 0, "the-via" );
  616. m_logger.Log( &aCurrent, 1, "current-line" );
  617. m_logger.Log( &shoved, 3, "shoved-line" );
  618. #endif
  619. int currentRank = aCurrent.Rank();
  620. replaceItems( &aCurrent, &shoved );
  621. if( !pushLine( shoved ) )
  622. return SH_INCOMPLETE;
  623. shoved.SetRank( currentRank );
  624. return SH_OK;
  625. }
  626. void PNS_SHOVE::unwindStack( PNS_SEGMENT* aSeg )
  627. {
  628. for( std::vector<PNS_LINE>::iterator i = m_lineStack.begin(); i != m_lineStack.end() ; )
  629. {
  630. if( i->ContainsSegment( aSeg ) )
  631. i = m_lineStack.erase( i );
  632. else
  633. i++;
  634. }
  635. for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end() ; )
  636. {
  637. if( i->ContainsSegment( aSeg ) )
  638. i = m_optimizerQueue.erase( i );
  639. else
  640. i++;
  641. }
  642. }
  643. void PNS_SHOVE::unwindStack( PNS_ITEM* aItem )
  644. {
  645. if( aItem->OfKind( PNS_ITEM::SEGMENT ) )
  646. unwindStack( static_cast<PNS_SEGMENT*>( aItem ) );
  647. else if( aItem->OfKind( PNS_ITEM::LINE ) )
  648. {
  649. PNS_LINE* l = static_cast<PNS_LINE*>( aItem );
  650. if( !l->LinkedSegments() )
  651. return;
  652. for( PNS_SEGMENT* seg : *l->LinkedSegments() )
  653. unwindStack( seg );
  654. }
  655. }
  656. bool PNS_SHOVE::pushLine( const PNS_LINE& aL, bool aKeepCurrentOnTop )
  657. {
  658. if( aL.LinkCount() >= 0 && ( aL.LinkCount() != aL.SegmentCount() ) )
  659. return false;
  660. if( aKeepCurrentOnTop && m_lineStack.size() > 0)
  661. {
  662. m_lineStack.insert( m_lineStack.begin() + m_lineStack.size() - 1, aL );
  663. }
  664. else
  665. {
  666. m_lineStack.push_back( aL );
  667. }
  668. m_optimizerQueue.push_back( aL );
  669. return true;
  670. }
  671. void PNS_SHOVE::popLine( )
  672. {
  673. PNS_LINE& l = m_lineStack.back();
  674. for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
  675. {
  676. bool found = false;
  677. if( !l.LinkedSegments() )
  678. continue;
  679. for( PNS_SEGMENT *s : *l.LinkedSegments() )
  680. {
  681. if( i->ContainsSegment( s ) )
  682. {
  683. i = m_optimizerQueue.erase( i );
  684. found = true;
  685. break;
  686. }
  687. }
  688. if( !found )
  689. i++;
  690. }
  691. m_lineStack.pop_back();
  692. }
  693. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveIteration( int aIter )
  694. {
  695. PNS_LINE currentLine = m_lineStack.back();
  696. PNS_NODE::OPT_OBSTACLE nearest;
  697. SHOVE_STATUS st = SH_NULL;
  698. PNS_ITEM::PnsKind search_order[] = { PNS_ITEM::SOLID, PNS_ITEM::VIA, PNS_ITEM::SEGMENT };
  699. for( int i = 0; i < 3; i++ )
  700. {
  701. nearest = m_currentNode->NearestObstacle( &currentLine, search_order[i] );
  702. if( nearest )
  703. break;
  704. }
  705. if( !nearest )
  706. {
  707. m_lineStack.pop_back();
  708. return SH_OK;
  709. }
  710. PNS_ITEM* ni = nearest->m_item;
  711. unwindStack( ni );
  712. if( !ni->OfKind( PNS_ITEM::SOLID ) && ni->Rank() >= 0 && ni->Rank() > currentLine.Rank() )
  713. {
  714. switch( ni->Kind() )
  715. {
  716. case PNS_ITEM::VIA:
  717. {
  718. PNS_VIA* revVia = (PNS_VIA*) ni;
  719. wxLogTrace( "PNS", "iter %d: reverse-collide-via", aIter );
  720. if( currentLine.EndsWithVia() && m_currentNode->CheckColliding( &currentLine.Via(), revVia ) )
  721. {
  722. st = SH_INCOMPLETE;
  723. }
  724. else
  725. {
  726. st = onReverseCollidingVia( currentLine, revVia );
  727. }
  728. break;
  729. }
  730. case PNS_ITEM::SEGMENT:
  731. {
  732. PNS_SEGMENT* seg = (PNS_SEGMENT*) ni;
  733. wxLogTrace( "PNS", "iter %d: reverse-collide-segment ", aIter );
  734. PNS_LINE revLine = assembleLine( seg );
  735. popLine();
  736. st = onCollidingLine( revLine, currentLine );
  737. if( !pushLine( revLine ) )
  738. return SH_INCOMPLETE;
  739. break;
  740. }
  741. default:
  742. assert( false );
  743. }
  744. }
  745. else
  746. { // "forward" collisions
  747. switch( ni->Kind() )
  748. {
  749. case PNS_ITEM::SEGMENT:
  750. wxLogTrace( "PNS", "iter %d: collide-segment ", aIter );
  751. st = onCollidingSegment( currentLine, (PNS_SEGMENT*) ni );
  752. if( st == SH_TRY_WALK )
  753. {
  754. st = onCollidingSolid( currentLine, ni );
  755. }
  756. break;
  757. case PNS_ITEM::VIA:
  758. wxLogTrace( "PNS", "iter %d: shove-via ", aIter );
  759. st = onCollidingVia( &currentLine, (PNS_VIA*) ni );
  760. if( st == SH_TRY_WALK )
  761. {
  762. st = onCollidingSolid( currentLine, ni );
  763. }
  764. break;
  765. case PNS_ITEM::SOLID:
  766. wxLogTrace( "PNS", "iter %d: walk-solid ", aIter );
  767. st = onCollidingSolid( currentLine, (PNS_SOLID*) ni );
  768. break;
  769. default:
  770. break;
  771. }
  772. }
  773. return st;
  774. }
  775. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop()
  776. {
  777. SHOVE_STATUS st = SH_OK;
  778. m_affectedAreaSum = OPT_BOX2I();
  779. wxLogTrace( "PNS", "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount(),
  780. m_currentNode->JointCount() );
  781. int iterLimit = Settings().ShoveIterationLimit();
  782. TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
  783. m_iter = 0;
  784. timeLimit.Restart();
  785. while( !m_lineStack.empty() )
  786. {
  787. st = shoveIteration( m_iter );
  788. m_iter++;
  789. if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
  790. {
  791. st = SH_INCOMPLETE;
  792. break;
  793. }
  794. }
  795. return st;
  796. }
  797. OPT_BOX2I PNS_SHOVE::totalAffectedArea() const
  798. {
  799. OPT_BOX2I area;
  800. if( !m_nodeStack.empty() )
  801. area = m_nodeStack.back().m_affectedArea;
  802. if( area )
  803. {
  804. if( m_affectedAreaSum )
  805. area->Merge( *m_affectedAreaSum );
  806. } else
  807. area = m_affectedAreaSum;
  808. return area;
  809. }
  810. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
  811. {
  812. SHOVE_STATUS st = SH_OK;
  813. m_multiLineMode = false;
  814. // empty head? nothing to shove...
  815. if( !aCurrentHead.SegmentCount() && !aCurrentHead.EndsWithVia() )
  816. return SH_INCOMPLETE;
  817. PNS_LINE head( aCurrentHead );
  818. head.ClearSegmentLinks();
  819. m_lineStack.clear();
  820. m_optimizerQueue.clear();
  821. m_newHead = OPT_LINE();
  822. m_logger.Clear();
  823. PNS_ITEMSET headSet;
  824. headSet.Add( aCurrentHead );
  825. reduceSpringback( headSet );
  826. PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
  827. m_currentNode = parent->Branch();
  828. m_currentNode->ClearRanks();
  829. m_currentNode->Add( &head );
  830. m_currentNode->LockJoint( head.CPoint(0), &head, true );
  831. if( !head.EndsWithVia() )
  832. m_currentNode->LockJoint( head.CPoint( -1 ), &head, true );
  833. head.Mark( MK_HEAD );
  834. head.SetRank( 100000 );
  835. m_logger.NewGroup( "initial", 0 );
  836. m_logger.Log( &head, 0, "head" );
  837. PNS_VIA* headVia = NULL;
  838. if( head.EndsWithVia() )
  839. {
  840. headVia = head.Via().Clone();
  841. m_currentNode->Add( headVia );
  842. headVia->Mark( MK_HEAD );
  843. headVia->SetRank( 100000 );
  844. m_logger.Log( headVia, 0, "head-via" );
  845. }
  846. if( !pushLine( head ) )
  847. {
  848. delete m_currentNode;
  849. m_currentNode = parent;
  850. return SH_INCOMPLETE;
  851. }
  852. st = shoveMainLoop();
  853. if( st == SH_OK )
  854. {
  855. runOptimizer( m_currentNode );
  856. if( m_newHead )
  857. st = m_currentNode->CheckColliding( &( *m_newHead ) ) ? SH_INCOMPLETE : SH_HEAD_MODIFIED;
  858. else
  859. st = m_currentNode->CheckColliding( &head ) ? SH_INCOMPLETE : SH_OK;
  860. }
  861. m_currentNode->RemoveByMarker( MK_HEAD );
  862. wxLogTrace( "PNS", "Shove status : %s after %d iterations",
  863. ( ( st == SH_OK || st == SH_HEAD_MODIFIED ) ? "OK" : "FAILURE"), m_iter );
  864. if( st == SH_OK || st == SH_HEAD_MODIFIED )
  865. {
  866. pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum );
  867. }
  868. else
  869. {
  870. delete m_currentNode;
  871. m_currentNode = parent;
  872. m_newHead = OPT_LINE();
  873. }
  874. if(m_newHead)
  875. m_newHead->Unmark();
  876. if( m_newHead && head.EndsWithVia() )
  877. {
  878. PNS_VIA v = head.Via();
  879. v.SetPos( m_newHead->CPoint( -1 ) );
  880. m_newHead->AppendVia(v);
  881. }
  882. return st;
  883. }
  884. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveMultiLines( const PNS_ITEMSET& aHeadSet )
  885. {
  886. SHOVE_STATUS st = SH_OK;
  887. m_multiLineMode = true;
  888. PNS_ITEMSET headSet;
  889. for( const PNS_ITEM* item : aHeadSet.CItems() )
  890. {
  891. const PNS_LINE* headOrig = static_cast<const PNS_LINE*>( item );
  892. // empty head? nothing to shove...
  893. if( !headOrig->SegmentCount() )
  894. return SH_INCOMPLETE;
  895. headSet.Add( *headOrig );
  896. }
  897. m_lineStack.clear();
  898. m_optimizerQueue.clear();
  899. m_logger.Clear();
  900. reduceSpringback( headSet );
  901. PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
  902. m_currentNode = parent->Branch();
  903. m_currentNode->ClearRanks();
  904. int n = 0;
  905. for( const PNS_ITEM* item : aHeadSet.CItems() )
  906. {
  907. const PNS_LINE* headOrig = static_cast<const PNS_LINE*>( item );
  908. PNS_LINE head( *headOrig );
  909. head.ClearSegmentLinks();
  910. m_currentNode->Add( &head );
  911. head.Mark( MK_HEAD );
  912. head.SetRank( 100000 );
  913. n++;
  914. if( !pushLine( head ) )
  915. return SH_INCOMPLETE;
  916. PNS_VIA* headVia = NULL;
  917. if( head.EndsWithVia() )
  918. {
  919. headVia = head.Via().Clone(); // fixme: leak
  920. m_currentNode->Add( headVia );
  921. headVia->Mark( MK_HEAD );
  922. headVia->SetRank( 100000 );
  923. m_logger.Log( headVia, 0, "head-via" );
  924. }
  925. }
  926. m_logger.NewGroup( "initial", 0 );
  927. //m_logger.Log( head, 0, "head" );
  928. st = shoveMainLoop();
  929. if( st == SH_OK )
  930. runOptimizer( m_currentNode );
  931. m_currentNode->RemoveByMarker( MK_HEAD );
  932. wxLogTrace( "PNS", "Shove status : %s after %d iterations",
  933. ( st == SH_OK ? "OK" : "FAILURE"), m_iter );
  934. if( st == SH_OK )
  935. {
  936. pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR(), m_affectedAreaSum );
  937. }
  938. else
  939. {
  940. delete m_currentNode;
  941. m_currentNode = parent;
  942. }
  943. return st;
  944. }
  945. PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere,
  946. PNS_VIA** aNewVia )
  947. {
  948. SHOVE_STATUS st = SH_OK;
  949. m_lineStack.clear();
  950. m_optimizerQueue.clear();
  951. m_newHead = OPT_LINE();
  952. m_draggedVia = NULL;
  953. m_draggedViaHeadSet.Clear();
  954. PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
  955. m_currentNode = parent;
  956. parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
  957. m_currentNode = parent->Branch();
  958. m_currentNode->ClearRanks();
  959. aVia->Mark( MK_HEAD );
  960. st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 );
  961. st = shoveMainLoop();
  962. if( st == SH_OK )
  963. runOptimizer( m_currentNode );
  964. if( st == SH_OK || st == SH_HEAD_MODIFIED )
  965. {
  966. if( aNewVia )
  967. {
  968. wxLogTrace( "PNS","setNewV %p", m_draggedVia );
  969. *aNewVia = m_draggedVia;
  970. }
  971. pushSpringback( m_currentNode, m_draggedViaHeadSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum );
  972. }
  973. else
  974. {
  975. if( aNewVia )
  976. {
  977. *aNewVia = nullptr;
  978. }
  979. delete m_currentNode;
  980. m_currentNode = parent;
  981. }
  982. return st;
  983. }
  984. void PNS_SHOVE::runOptimizer( PNS_NODE* aNode )
  985. {
  986. PNS_OPTIMIZER optimizer( aNode );
  987. int optFlags = 0, n_passes = 0;
  988. PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort();
  989. OPT_BOX2I area = totalAffectedArea();
  990. int maxWidth = 0;
  991. for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin();
  992. i != m_optimizerQueue.end(); ++i )
  993. {
  994. maxWidth = std::max( i->Width(), maxWidth );
  995. }
  996. if( area )
  997. {
  998. area->Inflate( 10 * maxWidth );
  999. }
  1000. switch( effort )
  1001. {
  1002. case OE_LOW:
  1003. optFlags = PNS_OPTIMIZER::MERGE_OBTUSE;
  1004. n_passes = 1;
  1005. break;
  1006. case OE_MEDIUM:
  1007. optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS;
  1008. if( area )
  1009. optimizer.SetRestrictArea( *area );
  1010. n_passes = 2;
  1011. break;
  1012. case OE_FULL:
  1013. optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS;
  1014. n_passes = 2;
  1015. break;
  1016. default:
  1017. break;
  1018. }
  1019. if( Settings().SmartPads() )
  1020. optFlags |= PNS_OPTIMIZER::SMART_PADS;
  1021. optimizer.SetEffortLevel( optFlags );
  1022. optimizer.SetCollisionMask( PNS_ITEM::ANY );
  1023. for( int pass = 0; pass < n_passes; pass++ )
  1024. {
  1025. std::reverse( m_optimizerQueue.begin(), m_optimizerQueue.end() );
  1026. for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin();
  1027. i != m_optimizerQueue.end(); ++i )
  1028. {
  1029. PNS_LINE& line = *i;
  1030. if( !( line.Marker() & MK_HEAD ) )
  1031. {
  1032. PNS_LINE optimized;
  1033. if( optimizer.Optimize( &line, &optimized ) )
  1034. {
  1035. aNode->Remove( &line );
  1036. line.SetShape( optimized.CLine() );
  1037. aNode->Add( &line );
  1038. }
  1039. }
  1040. }
  1041. }
  1042. }
  1043. PNS_NODE* PNS_SHOVE::CurrentNode()
  1044. {
  1045. return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
  1046. }
  1047. const PNS_LINE PNS_SHOVE::NewHead() const
  1048. {
  1049. assert( m_newHead );
  1050. return *m_newHead;
  1051. }
  1052. void PNS_SHOVE::SetInitialLine( PNS_LINE& aInitial )
  1053. {
  1054. m_root = m_root->Branch();
  1055. m_root->Remove( &aInitial );
  1056. }