KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_shove.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
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
22#include <deque>
23#include <cassert>
24#include <math/box2.h>
25
26#include <wx/log.h>
27
28#include "pns_arc.h"
29#include "pns_line.h"
30#include "pns_node.h"
31#include "pns_debug_decorator.h"
32#include "pns_walkaround.h"
33#include "pns_shove.h"
34#include "pns_solid.h"
35#include "pns_optimizer.h"
36#include "pns_via.h"
37#include "pns_utils.h"
38#include "pns_router.h"
39#include "pns_topology.h"
40
41#include "time_limit.h"
42
43// fixme - move all logger calls to debug decorator
44
46
47namespace PNS {
48
49void SHOVE::replaceItems( ITEM* aOld, std::unique_ptr< ITEM > aNew )
50{
51 OPT_BOX2I changed_area = ChangedArea( aOld, aNew.get() );
52
53 if( changed_area )
54 m_affectedArea = m_affectedArea ? m_affectedArea->Merge( *changed_area ) : *changed_area;
55
56 ROOT_LINE_ENTRY *re = nullptr;
58
59 if( aOld->OfKind( ITEM::VIA_T ) )
60 {
61 VIA* vold = static_cast<VIA*>( aOld );
62 VIA* vnew = static_cast<VIA*>( aNew.get() );
63 re = touchRootLine( vold );
64 re->newVia = vnew;
65 newId = static_cast<VIA*>( aNew.get() )->Uid();
66
67 PNS_DBG( Dbg(), Message,
68 wxString::Format( "replace-via node=%p vold=%p [%d %d]-> vnew=%p [%d %d] nid %llu", m_currentNode, aOld,
69 vold->Pos().x, vold->Pos().y, aNew.get(), vnew->Pos().x,
70 vnew->Pos().y, newId ) );
71 }
72
73 m_currentNode->Replace( aOld, std::move( aNew ) );
74
75 if( re )
76 m_rootLineHistory[ newId ] = re;
77}
78
79
80SHOVE::ROOT_LINE_ENTRY* SHOVE::replaceLine( LINE& aOld, LINE& aNew, bool aIncludeInChangedArea, bool aAllowRedundantSegments, NODE* aNode )
81{
82 if( aIncludeInChangedArea )
83 {
84 OPT_BOX2I changed_area = ChangedArea( aOld, aNew );
85
86 if( changed_area )
87 {
88 SHAPE_RECT r( *changed_area );
89 PNS_DBG( Dbg(), AddShape, &r, BLUE, 0, wxT( "shove-changed-area" ) );
90
91 m_affectedArea = m_affectedArea ? m_affectedArea->Merge( *changed_area )
92 : *changed_area;
93 }
94 }
95
96 int old_sc = aOld.SegmentCount();
97 int old_lc = aOld.LinkCount();
98
99 if( aOld.EndsWithVia() )
100 {
101 for( LINKED_ITEM* lnk : aOld.Links() )
102 {
103 if( lnk->OfKind( ITEM::VIA_T ) )
104 {
105 aOld.Unlink( lnk );
106 }
107 }
108 }
109
110
111 bool foundPredecessor = false;
112 ROOT_LINE_ENTRY *rootEntry = nullptr;
113
114 // Keep track of the 'root lines', i.e. the unmodified (pre-shove) versions
115 // of the affected tracks in a map. The optimizer can then query the pre-shove shape
116 // for each shoved line and perform additional constraint checks (i.e. prevent overzealous
117 // optimizations)
118
119 // Check if the shoved line already has an ancestor (e.g. line from a previous shove
120 // iteration/cursor movement)
121 for( LINKED_ITEM* link : aOld.Links() )
122 {
123 auto oldLineIter = m_rootLineHistory.find( link->Uid() );
124
125 if( oldLineIter != m_rootLineHistory.end() )
126 {
127 rootEntry = oldLineIter->second;
128 foundPredecessor = true;
129 break;
130 }
131 }
132
133 // If found, use it, otherwise, create new entry in the map (we have a genuine new 'root' line)
134 if( !foundPredecessor )
135 {
136 if( ! rootEntry )
137 {
138 rootEntry = new ROOT_LINE_ENTRY( aOld.Clone() );
139 }
140
141 for( LINKED_ITEM* link : aOld.Links() )
142 {
143 m_rootLineHistory[link->Uid()] = rootEntry;
144 }
145 }
146
147 // Now update the NODE (calling Replace invalidates the Links() in a LINE)
148 if( aNode )
149 aNode->Replace( aOld, aNew, aAllowRedundantSegments );
150 else
151 m_currentNode->Replace( aOld, aNew, aAllowRedundantSegments );
152
153
154 // point the Links() of the new line to its oldest ancestor
155 for( LINKED_ITEM* link : aNew.Links() )
156 {
157 m_rootLineHistory[ link->Uid() ] = rootEntry;
158 }
159
160 rootEntry->newLine = aNew;
161
162 return rootEntry;
163}
164
165
166int SHOVE::getClearance( const ITEM* aA, const ITEM* aB ) const
167{
168 if( m_forceClearance >= 0 )
169 return m_forceClearance;
170
171 int clearance = m_currentNode->GetClearance( aA, aB, false );
172
173 if( aA->HasHole() )
174 clearance = std::max( clearance, m_currentNode->GetClearance( aA->Hole(), aB, false ) );
175
176 if( aB->HasHole() )
177 clearance = std::max( clearance, m_currentNode->GetClearance( aA, aB->Hole(), false ) );
178
179 return clearance;
180}
181
182
183void SHOVE::sanityCheck( LINE* aOld, LINE* aNew )
184{
185 assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) );
186 assert( aOld->CPoint( -1 ) == aNew->CPoint( -1 ) );
187}
188
189
190SHOVE::SHOVE( NODE* aWorld, ROUTER* aRouter ) :
191 ALGO_BASE( aRouter )
192{
194 m_forceClearance = -1;
195 m_root = aWorld;
196 m_currentNode = aWorld;
198
199 // Initialize other temporary variables:
200 m_draggedVia = nullptr;
201 m_iter = 0;
202 m_multiLineMode = false;
203 m_headsModified = false;
207}
208
209
211{
212}
213
214
215LINE SHOVE::assembleLine( const LINKED_ITEM* aSeg, int* aIndex, bool aPreCleanup )
216{
217 LINE cur = m_currentNode->AssembleLine( const_cast<LINKED_ITEM*>( aSeg ), aIndex, true );
218
219 if( aPreCleanup )
220 {
221 LINE cleaned;
222 if (preShoveCleanup( &cur, &cleaned ) )
223 return cleaned;
224 }
225
226 return cur;
227}
228
229
230// A dumb function that checks if the shoved line is shoved the right way, e.g. visually
231// "outwards" of the line/via applying pressure on it. Unfortunately there's no mathematical
232// concept of orientation of an open curve, so we use some primitive heuristics: if the shoved
233// line wraps around the start of the "pusher", it's likely shoved in wrong direction.
234
235// Update: there's no concept of an orientation of an open curve, but nonetheless Tom's dumb
236// as.... (censored)
237// Two open curves put together make a closed polygon... Tom should learn high school geometry!
238bool SHOVE::checkShoveDirection( const LINE& aCurLine, const LINE& aObstacleLine,
239 const LINE& aShovedLine ) const
240{
241 SHAPE_LINE_CHAIN::POINT_INSIDE_TRACKER checker( aCurLine.CPoint( 0) );
242 checker.AddPolyline( aObstacleLine.CLine() );
243 checker.AddPolyline( aShovedLine.CLine().Reverse() );
244
245 bool inside = checker.IsInside();
246
247 return !inside;
248}
249
250
251/*
252 * Push aObstacleLine away from aCurLine's via by the clearance distance, returning the result
253 * in aResultLine.
254 *
255 * Must be called only when aCurLine itself is on another layer (or has no segments) so that it
256 * can be ignored.
257 */
258bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLine,
259 LINE& aResultLine )
260{
261 // Build a hull for aCurLine's via and re-walk aObstacleLine around it.
262
263 int obstacleLineWidth = aObstacleLine.Width();
264 const VIA& via = aCurLine.Via();
265 int clearance = getClearance( &via, &aObstacleLine );
266 HOLE* viaHole = via.Hole();
267 int holeClearance = getClearance( viaHole, &aObstacleLine );
268
269 if( holeClearance + via.Drill() / 2 > clearance + via.Diameter( aObstacleLine.Layer() ) / 2 )
270 clearance = holeClearance + via.Drill() / 2 - via.Diameter( aObstacleLine.Layer() ) / 2;
271
272 SHAPE_LINE_CHAIN hull = aCurLine.Via().Hull( clearance, obstacleLineWidth, aCurLine.Layer() );
273 SHAPE_LINE_CHAIN path_cw;
274 SHAPE_LINE_CHAIN path_ccw;
275
276 if( ! aObstacleLine.Walkaround( hull, path_cw, true ) )
277 return false;
278
279 if( ! aObstacleLine.Walkaround( hull, path_ccw, false ) )
280 return false;
281
282 const SHAPE_LINE_CHAIN& shortest = path_ccw.Length() < path_cw.Length() ? path_ccw : path_cw;
283
284 if( shortest.PointCount() < 2 )
285 return false;
286
287 if( aObstacleLine.CPoint( -1 ) != shortest.CPoint( -1 ) )
288 return false;
289
290 if( aObstacleLine.CPoint( 0 ) != shortest.CPoint( 0 ) )
291 return false;
292
293 aResultLine.SetShape( shortest );
294
295 if( aResultLine.Collide( &aCurLine, m_currentNode, aResultLine.Layer() ) )
296 return false;
297
298 return true;
299}
300
301
302/*
303 * Re-walk aObstacleLine around the given set of hulls, returning the result in aResultLine.
304 */
305bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
306 LINE& aResultLine, const HULL_SET& aHulls, bool aPermitAdjustingEndpoints )
307{
308 const int c_ENDPOINT_ON_HULL_THRESHOLD = 10000;
309 int attempt;
310
311 PNS_DBG( Dbg(), BeginGroup, "shove-details", 1 );
312
313 for( attempt = 0; attempt < 4; attempt++ )
314 {
315 bool invertTraversal = ( attempt >= 2 );
316 bool clockwise = attempt % 2;
317 int vFirst = -1, vLast = -1;
318 SHAPE_LINE_CHAIN obs = aObstacleLine.CLine();
319 LINE l( aObstacleLine );
321
322 if( aPermitAdjustingEndpoints )
323 {
324 auto minDistP = [&]( VECTOR2I pref, int& mdist, int& minhull ) -> VECTOR2I
325 {
326 int min_dist = std::numeric_limits<int>::max();
327 VECTOR2I nearestP;
328
329 for( int i = 0; i < (int) aHulls.size(); i++ )
330 {
331 const SHAPE_LINE_CHAIN& hull =
332 aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
333 int dist;
334 const VECTOR2I p = hull.NearestPoint( pref, true );
335
336 if( hull.PointInside( pref ) )
337 dist = 0;
338 else
339 dist = ( p - pref ).EuclideanNorm();
340
341 if( dist < c_ENDPOINT_ON_HULL_THRESHOLD && dist < min_dist )
342 {
343 bool reject = false;
344
345 /*for( int j = 0; j < (int) aHulls.size(); j++ )
346 {
347 if ( i != j && aHulls[j].PointInside( p ) )
348 {
349 reject = true;
350 break;
351 }
352 }*/
353
354 if( !reject )
355 {
356 min_dist = dist;
357 nearestP = p;
358 minhull = invertTraversal ? aHulls.size() - 1 - i : i;
359 }
360 }
361 }
362 mdist = min_dist;
363 return nearestP;
364 };
365
366 int minDist0, minDist1, minhull0, minhull1 ;
367 VECTOR2I p0 = minDistP( l.CPoint( 0 ), minDist0, minhull0 );
368 VECTOR2I p1 = minDistP( l.CPoint( -1 ), minDist1, minhull1 );
369
370 PNS_DBG( Dbg(), Message, wxString::Format( "mindists : %d %d hulls %d %d\n", minDist0, minDist1, minhull0, minhull1 ) );
371
372 if( minDist1 < c_ENDPOINT_ON_HULL_THRESHOLD )
373 {
374 l.Line().Append( p1 );
375 obs = l.CLine();
376 path = l.CLine();
377 }
378
379 if( minDist0 < c_ENDPOINT_ON_HULL_THRESHOLD )
380 {
381 l.Line().Insert( 0, p0 );
382 obs = l.CLine();
383 path = l.CLine();
384 }
385 }
386
387
388 bool failWalk = false;
389
390 for( int i = 0; i < (int) aHulls.size(); i++ )
391 {
392 const SHAPE_LINE_CHAIN& hull = aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
393
394 PNS_DBG( Dbg(), AddShape, &hull, YELLOW, 10000, wxString::Format( "hull[%d]", i ) );
395 PNS_DBG( Dbg(), AddShape, &path, WHITE, l.Width(), wxString::Format( "path[%d]", i ) );
396 PNS_DBG( Dbg(), AddShape, &obs, LIGHTGRAY, aObstacleLine.Width(), wxString::Format( "obs[%d]", i ) );
397
398 if( !l.Walkaround( hull, path, clockwise ) )
399 {
400 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "Fail-Walk %s %s %d\n" ),
401 hull.Format().c_str(),
402 l.CLine().Format().c_str(),
403 clockwise? 1 : 0) );
404
405 failWalk = true;
406 break;
407 }
408
409 path.Simplify2();
410 l.SetShape( path );
411 }
412
413 if( failWalk )
414 continue;
415
416 for( int i = 0; i < std::min( path.PointCount(), obs.PointCount() ); i++ )
417 {
418 if( path.CPoint( i ) != obs.CPoint( i ) )
419 {
420 vFirst = i;
421 break;
422 }
423 }
424
425 int k = obs.PointCount() - 1;
426
427 for( int i = path.PointCount() - 1; i >= 0 && k >= 0; i--, k-- )
428 {
429 if( path.CPoint( i ) != obs.CPoint( k ) )
430 {
431 vLast = i;
432 break;
433 }
434 }
435
436 if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( obs ) )
437 {
438 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail vfirst-last" ),
439 attempt ) );
440 continue;
441 }
442
443 if( path.CPoint( -1 ) != obs.CPoint( -1 ) || path.CPoint( 0 ) != obs.CPoint( 0 ) )
444 {
445 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail vend-start\n" ),
446 attempt ) );
447 continue;
448 }
449
450 if( !checkShoveDirection( aCurLine, aObstacleLine, l ) )
451 {
452 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail direction-check" ),
453 attempt ) );
454 aResultLine.SetShape( l.CLine() );
455 continue;
456 }
457
458 if( path.SelfIntersecting() )
459 {
460 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail self-intersect" ),
461 attempt ) );
462 continue;
463 }
464
465 bool colliding = l.Collide( &aCurLine, m_currentNode, l.Layer() );
466
467 #if 0
468 if(( aCurLine.Marker() & MK_HEAD ) && !colliding )
469 {
470 const JOINT* jtStart = m_currentNode->FindJoint( aCurLine.CPoint( 0 ), &aCurLine );
471
472 for( ITEM* item : jtStart->LinkList() )
473 {
474 if( item->Collide( &l, m_currentNode, l.Layer() ) )
475 colliding = true;
476 }
477 }
478 #endif
479
480 if( colliding )
481 {
482 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail coll-check" ),
483 attempt ) );
484 continue;
485 }
486
487 aResultLine.SetShape( l.CLine() );
488
489 PNS_DBGN( Dbg(), EndGroup );
490
491 return true;
492 }
493
494 PNS_DBGN( Dbg(), EndGroup );
495
496 return false;
497}
498
499
500/*
501 * Push aObstacleLine line away from aCurLine by the clearance distance, and return the result in
502 * aResultLine.
503 */
504bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
505 LINE& aResultLine )
506{
507 const int cHullFailureExpansionFactor = 1000;
508 int extraHullExpansion = 0;
509
510
511 aResultLine.ClearLinks();
512 bool viaOnEnd = aCurLine.EndsWithVia();
513
514 LINE obstacleLine( aObstacleLine );
515 std::optional<VIA> obsVia;
516
517 if( obstacleLine.EndsWithVia() )
518 {
519 obsVia = aObstacleLine.Via();
520 obstacleLine.RemoveVia();
521 }
522
523 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove process-single: voe-cur %d voe-obs %d" ),
524 aCurLine.EndsWithVia()?1:0, aObstacleLine.EndsWithVia()?1:0 ) );
525
526
527 if( viaOnEnd && ( !aCurLine.LayersOverlap( &obstacleLine ) || aCurLine.SegmentCount() == 0 ) )
528 {
529 // Shove obstacleLine to the hull of aCurLine's via.
530 return shoveLineFromLoneVia( aCurLine, obstacleLine, aResultLine );
531 }
532 else
533 {
534 // Build a set of hulls around the segments of aCurLine. Hulls are at the clearance
535 // distance + obstacleLine's linewidth so that when re-walking obstacleLine along the
536 // hull it will be at the appropriate clearance.
537
538 int obstacleLineWidth = obstacleLine.Width();
539 int clearance = getClearance( &aCurLine, &obstacleLine );
540 int currentLineSegmentCount = aCurLine.SegmentCount();
541
542 /*PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove process-single: cur net %d obs %d cl %d" ),
543 m_router->GetInterface()->GetNetCode( aCurLine.Net() ),
544 m_router->GetInterface()->GetNetCode( obstacleLine.Net() ),
545 clearance ) );*/
546
547 for( int attempt = 0; attempt < 3; attempt++ )
548 {
549 HULL_SET hulls;
550
551 hulls.reserve( currentLineSegmentCount + 1 );
552
553 for( int i = 0; i < currentLineSegmentCount; i++ )
554 {
555 SEGMENT seg( aCurLine, aCurLine.CSegment( i ) );
556
557 // Arcs need additional clearance to ensure the hulls are always bigger than the arc
558 if( aCurLine.CLine().IsArcSegment( i ) )
559 {
560 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove add-extra-clearance %d" ),
563 }
564
565 SHAPE_LINE_CHAIN hull = seg.Hull( clearance + extraHullExpansion, obstacleLineWidth, obstacleLine.Layer() );
566
567 hulls.push_back( hull );
568 }
569
570 if( viaOnEnd )
571 {
572 const VIA& via = aCurLine.Via();
573 int viaClearance = getClearance( &via, &obstacleLine );
574 HOLE* viaHole = via.Hole();
575 int holeClearance = getClearance( viaHole, &obstacleLine );
576 int layer = aObstacleLine.Layer();
577
578 if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter( layer ) / 2 )
579 {
580 viaClearance = holeClearance + via.Drill() / 2 - via.Diameter( layer ) / 2;
581 }
582
583 hulls.push_back( aCurLine.Via().Hull( viaClearance, obstacleLineWidth, layer ) );
584 }
585
586 bool permitMovingEndpoints = (attempt >= 2);
587
588 if (shoveLineToHullSet( aCurLine, obstacleLine, aResultLine, hulls, permitMovingEndpoints ) )
589 {
590 if( obsVia )
591 aResultLine.AppendVia( *obsVia );
592
593 return true;
594 }
595
596 extraHullExpansion += cHullFailureExpansionFactor;
597 }
598 }
599
600 return false;
601}
602
603
604/*
605 * TODO describe....
606 */
608{
609 int segIndex;
610
611 LINE obstacleLine = assembleLine( aObstacleSeg, &segIndex, true );
612 LINE shovedLine( obstacleLine );
613 SEGMENT tmp( *aObstacleSeg );
614
615 if( obstacleLine.HasLockedSegments() )
616 {
617 PNS_DBG(Dbg(), Message, "try walk (locked segments)");
618 return SH_TRY_WALK;
619 }
620
621 bool shoveOK = ShoveObstacleLine( aCurrent, obstacleLine, shovedLine );
622
623 const double extensionWalkThreshold = 1.0;
624
625 double obsLen = obstacleLine.CLine().Length();
626 double shovedLen = shovedLine.CLine().Length();
627 double extensionFactor = 0.0;
628
629 if( obsLen != 0.0f )
630 extensionFactor = shovedLen / obsLen - 1.0;
631
632 //if( extensionFactor > extensionWalkThreshold )
633 // return SH_TRY_WALK;
634
635 assert( obstacleLine.LayersOverlap( &shovedLine ) );
636
637 if( Dbg() )
638 {
639 PNS_DBG( Dbg(), AddItem, aObstacleSeg, BLUE, 0, wxT( "colliding-segment" ) );
640 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxString::Format( "current-line [links %d l %d v %d]", aCurrent.LinkCount(), aCurrent.Layer(), aCurrent.EndsWithVia() ) );
641 PNS_DBG( Dbg(), AddItem, &obstacleLine, GREEN, 10000, wxString::Format( "obstacle-line [links %d l %d v %d]", obstacleLine.LinkCount(), obstacleLine.Layer(), obstacleLine.EndsWithVia() ) );
642 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 10000, wxT( "shoved-line" ) );
643 }
644
645 if( shoveOK )
646 {
647 int rank = aCurrent.Rank();
648
649 shovedLine.SetRank( rank - 1 );
650 shovedLine.Line().Simplify2();
651
652 unwindLineStack( &obstacleLine );
653
654 replaceLine( obstacleLine, shovedLine, true, false );
655
656 if( !pushLineStack( shovedLine ) )
657 return SH_INCOMPLETE;
658
659 return SH_OK;
660 }
661
662 return SH_INCOMPLETE;
663}
664
665
666/*
667 * TODO describe....
668 */
670{
671 int segIndex;
672 LINE obstacleLine = assembleLine( aObstacleArc, &segIndex );
673 LINE shovedLine( obstacleLine );
674 ARC tmp( *aObstacleArc );
675
676 if( obstacleLine.HasLockedSegments() )
677 return SH_TRY_WALK;
678
679 bool shoveOK = ShoveObstacleLine( aCurrent, obstacleLine, shovedLine );
680
681 const double extensionWalkThreshold = 1.0;
682
683 double obsLen = obstacleLine.CLine().Length();
684 double shovedLen = shovedLine.CLine().Length();
685 double extensionFactor = 0.0;
686
687 if( obsLen != 0.0f )
688 extensionFactor = shovedLen / obsLen - 1.0;
689
690 if( extensionFactor > extensionWalkThreshold )
691 return SH_TRY_WALK;
692
693 assert( obstacleLine.LayersOverlap( &shovedLine ) );
694
695 PNS_DBG( Dbg(), AddItem, &tmp, WHITE, 10000, wxT( "obstacle-arc" ) );
696 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxT( "current-line" ) );
697 PNS_DBG( Dbg(), AddItem, &obstacleLine, GREEN, 10000, wxT( "obstacle-line" ) );
698 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 10000, wxT( "shoved-line" ) );
699
700 if( shoveOK )
701 {
702 int rank = aCurrent.Rank();
703 shovedLine.SetRank( rank - 1 );
704
705 replaceLine( obstacleLine, shovedLine, true, false );
706
707 if( !pushLineStack( shovedLine ) )
708 return SH_INCOMPLETE;
709 }
710
711 return SH_OK;
712}
713
714
715/*
716 * TODO describe....
717 */
718SHOVE::SHOVE_STATUS SHOVE::onCollidingLine( LINE& aCurrent, LINE& aObstacle, int aNextRank )
719{
720 LINE shovedLine( aObstacle );
721
722 bool shoveOK = ShoveObstacleLine( aCurrent, aObstacle, shovedLine );
723
724 PNS_DBG( Dbg(), AddItem, &aObstacle, RED, 100000, wxT( "obstacle-line" ) );
725 PNS_DBG( Dbg(), AddItem, &aCurrent, GREEN, 150000, wxT( "current-line" ) );
726 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 200000, wxT( "shoved-line" ) );
727
728 if( shoveOK )
729 {
730#if 0
731 if( shovedLine.Marker() & MK_HEAD )
732 {
733 if( m_multiLineMode )
734 return SH_INCOMPLETE;
735
736 m_newHead = shovedLine;
737 }
738#endif
739 replaceLine( aObstacle, shovedLine, true, false );
740
741 shovedLine.SetRank( aNextRank );
742
743 if( !pushLineStack( shovedLine ) )
744 return SH_INCOMPLETE;
745
746 return SH_OK;
747 }
748
749 return SH_INCOMPLETE;
750}
751
752
753/*
754 * TODO describe....
755 */
756SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OBSTACLE& aObstacleInfo )
757{
758 LINE walkaroundLine( aCurrent );
759
760 if( aCurrent.EndsWithVia() )
761 {
762 VIA vh = aCurrent.Via();
763 VIA* via = nullptr;
764 const JOINT* jtStart = m_currentNode->FindJoint( vh.Pos(), &aCurrent );
765
766 if( !jtStart )
767 return SH_INCOMPLETE;
768
769 for( ITEM* item : jtStart->LinkList() )
770 {
771 if( item->OfKind( ITEM::VIA_T ) )
772 {
773 via = (VIA*) item;
774 break;
775 }
776 }
777
778 // TODO(JE) viastacks -- can aObstacle be a via?
779 if( via && via->Collide( aObstacle, m_currentNode, aObstacle->Layer() ) )
780 return onCollidingVia( aObstacle, via, aObstacleInfo, aObstacle->Rank() - 1 );
781 }
782
783 TOPOLOGY topo( m_currentNode );
784 TOPOLOGY::CLUSTER cluster = topo.AssembleCluster( aObstacle, aCurrent.Layers().Start(), 10.0 );
785
786 PNS_DBG( Dbg(), BeginGroup, "walk-cluster", 1 );
787
788 for( ITEM* item : cluster.m_items )
789 PNS_DBG( Dbg(), AddItem, item, RED, 10000, wxT( "cl-item" ) );
790
791 PNS_DBGN( Dbg(), EndGroup );
792
793 WALKAROUND walkaround( m_currentNode, Router() );
794 walkaround.SetDebugDecorator( Dbg() );
795 walkaround.SetSolidsOnly( false );
796 walkaround.RestrictToCluster( true, cluster );
798 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() ); // fixme: make configurable
799
800 int currentRank = aCurrent.Rank();
801 int nextRank;
802
803 bool success = false;
804
805 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxT( "current-line" ) );
806
807 for( int attempt = 0; attempt < 2; attempt++ )
808 {
809 if( attempt == 1 || Settings().JumpOverObstacles() )
810 nextRank = currentRank - 1;
811 else
812 nextRank = currentRank + 10000;
813
814 WALKAROUND::RESULT status = walkaround.Route( aCurrent );
815
816 if( status.status[ WALKAROUND::WP_SHORTEST ] != WALKAROUND::ST_DONE )//fixme policies!
817 continue;
818
819 walkaroundLine = status.lines[ WALKAROUND::WP_SHORTEST ];
820
821 walkaroundLine.ClearLinks();
822 walkaroundLine.Unmark();
823 walkaroundLine.Line().Simplify2();
824
825 if( walkaroundLine.HasLoops() )
826 continue;
827
828 PNS_DBG( Dbg(), AddItem, &walkaroundLine, BLUE, 10000, wxT( "walk-line" ) );
829
830#if 0
831 if( aCurrent.Marker() & MK_HEAD )
832 {
833 walkaroundLine.Mark( MK_HEAD );
834
835 if( m_multiLineMode )
836 continue;
837
838 m_newHead = walkaroundLine;
839 }
840#endif
841
842
843 if( !m_lineStack.empty() )
844 {
845 LINE lastLine = m_lineStack.front();
846
847 if( lastLine.Collide( &walkaroundLine, m_currentNode, lastLine.Layer() ) )
848 {
849 LINE dummy( lastLine );
850
851 if( ShoveObstacleLine( walkaroundLine, lastLine, dummy ) )
852 {
853 success = true;
854 break;
855 }
856 }
857 else
858 {
859 success = true;
860 break;
861 }
862 }
863 }
864
865 if(!success)
866 return SH_INCOMPLETE;
867
868 replaceLine( aCurrent, walkaroundLine, true, false );
869 walkaroundLine.SetRank( nextRank );
870
871
872 popLineStack();
873
874 if( !pushLineStack( walkaroundLine ) )
875 return SH_INCOMPLETE;
876
877 return SH_OK;
878}
879
880
881void SHOVE::pruneRootLines( NODE *aRemovedNode )
882{
883 PNS_DBG( Dbg(), Message, wxString::Format("prune called" ) );
884
885 NODE::ITEM_VECTOR added, removed;
886 aRemovedNode->GetUpdatedItems( removed, added );
887
888 for( const ITEM* item : added )
889 {
890 if( item->OfKind( ITEM::LINKED_ITEM_MASK_T ) )
891 {
892 const LINKED_ITEM* litem = static_cast<const LINKED_ITEM*>( item );
893
894 m_rootLineHistory.erase( litem->Uid() );
895 }
896 }
897}
898
899
900/*
901 * Pops NODE stackframes which no longer collide with aHeadSet. Optionally sets aDraggedVia
902 * to the dragged via of the last unpopped state.
903 */
905{
906 while( m_nodeStack.size() > 1 )
907 {
908 SPRINGBACK_TAG& spTag = m_nodeStack.back();
909
910 // Prevent the springback algo from erasing NODEs that might contain items used by the ROUTER_TOOL/LINE_PLACER.
911 // I noticed this can happen for the endItem provided to LINE_PLACER::Move() and cause a nasty crash.
913 break;
914
915 std::optional<OBSTACLE> obs = spTag.m_node->CheckColliding( aHeadSet );
916
917 if( !obs && !spTag.m_locked )
918 {
919 int i;
920
921 PNS_DBG( Dbg(), Message, wxString::Format( "pop-sp node=%p depth=%d", spTag.m_node, spTag.m_node->Depth() ) );
922
923 pruneRootLines( spTag.m_node );
924
925
926 delete spTag.m_node;
927 m_nodeStack.pop_back();
928 }
929 else
930 {
931 break;
932 }
933 }
934
935 if( m_nodeStack.empty() )
936 return m_root;
937
938 SPRINGBACK_TAG& spTag = m_nodeStack.back();
939
940 for( int i = 0; i < spTag.m_draggedVias.size(); i++ )
941 {
942 if (spTag.m_draggedVias[i].valid)
943 {
944 m_headLines[i].prevVia = m_headLines[i].theVia = spTag.m_draggedVias[i];
945 m_headLines[i].geometryModified = true;
946 PNS_DBG( Dbg(), Message,
947 wxString::Format( "restore-springback-via depth=%d %d %d %d %d ",
948 spTag.m_node->Depth(),
949 (int) m_nodeStack.size(),
950 m_headLines[i].theVia->pos.x,
951 m_headLines[i].theVia->pos.y,
952 m_headLines[i].theVia->layers.Start(),
953 m_headLines[i].theVia->layers.End() ) );
954 }
955 }
956
957 return m_nodeStack.back().m_node;
958}
959
960
961/*
962 * Push the current NODE on to the stack. aDraggedVia is the dragged via *before* the push
963 * (which will be restored in the event the stackframe is popped).
964 */
965bool SHOVE::pushSpringback( NODE* aNode, const OPT_BOX2I& aAffectedArea )
966{
968 OPT_BOX2I prev_area;
969
970 if( !m_nodeStack.empty() )
971 prev_area = m_nodeStack.back().m_affectedArea;
972
973 st.m_draggedVias.resize( m_headLines.size() );
974 int n = 0;
975
976 for( HEAD_LINE_ENTRY& head : m_headLines )
977 {
978 if ( head.theVia )
979 {
980 VIA_HANDLE vhandle = *head.theVia;
981
982 PNS_DBG( Dbg(), Message,
983 wxString::Format( "push-sp via depth=%d %d %d %d %d ", aNode->Depth(), vhandle.pos.x,
984 vhandle.pos.y,
985 vhandle.layers.Start(),
986 vhandle.layers.End() ) );
987
988 st.m_draggedVias[n] = vhandle;
989 }
990
991 n++;
992 }
993
994 st.m_node = aNode;
995
996 if( aAffectedArea )
997 {
998 if( prev_area )
999 st.m_affectedArea = prev_area->Merge( *aAffectedArea );
1000 else
1001 st.m_affectedArea = aAffectedArea;
1002 }
1003 else
1004 {
1005 st.m_affectedArea = prev_area;
1006 }
1007
1008 st.m_seq = ( m_nodeStack.empty() ? 1 : m_nodeStack.back().m_seq + 1 );
1009 st.m_locked = false;
1010
1011 m_nodeStack.push_back( st );
1012
1013 PNS_DBG( Dbg(), Message, wxString::Format( "push-sp depth=%d node=%p", st.m_node->Depth(), st.m_node ) );
1014
1015 return true;
1016}
1017
1018
1019/*
1020 * Push or shove a via by at least aForce. (The via might be pushed or shoved slightly further
1021 * to keep it from landing on an existing joint.)
1022 */
1023SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNewRank, bool aDontUnwindStack )
1024{
1025 LINE_PAIR_VEC draggedLines;
1026 VECTOR2I p0( aVia->Pos() );
1027 const JOINT* jt = m_currentNode->FindJoint( p0, aVia );
1028 VECTOR2I p0_pushed( p0 + aForce );
1029
1030 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "via force [%d %d]\n" ), aForce.x, aForce.y ) );
1031
1032 // nothing to do...
1033 if ( aForce.x == 0 && aForce.y == 0 )
1034 return SH_OK;
1035
1036 if( !jt )
1037 {
1038 PNS_DBG( Dbg(), Message, wxT( "weird, can't find the center-of-via joint\n" ) );
1039 return SH_INCOMPLETE;
1040 }
1041
1042 if( Settings().ShoveVias() == false || aVia->IsLocked() )
1043 return SH_TRY_WALK;
1044
1045 if( jt->IsLocked() )
1046 return SH_INCOMPLETE;
1047
1048 // make sure pushed via does not overlap with any existing joint
1049 while( true )
1050 {
1051 const JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia );
1052
1053 if( !jt_next )
1054 break;
1055
1056 p0_pushed += aForce.Resize( 2 );
1057 }
1058
1059 std::unique_ptr<VIA> pushedVia = Clone( *aVia );
1060 pushedVia->SetPos( p0_pushed );
1061 pushedVia->Mark( aVia->Marker() );
1062
1063 for( ITEM* item : jt->LinkList() )
1064 {
1065 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
1066 {
1067 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
1068 LINE_PAIR lp;
1069 int segIndex;
1070
1071 lp.first = assembleLine( li, &segIndex );
1072
1073 if( lp.first.HasLockedSegments() )
1074 return SH_TRY_WALK;
1075
1076 assert( segIndex == 0 || ( segIndex == ( lp.first.SegmentCount() - 1 ) ) );
1077
1078 if( segIndex == 0 )
1079 lp.first.Reverse();
1080
1081 lp.second = lp.first;
1082 lp.second.ClearLinks();
1083 lp.second.DragCorner( p0_pushed, lp.second.CLine().Find( p0 ) );
1084 lp.second.Line().Simplify2();
1085 draggedLines.push_back( lp );
1086 }
1087 }
1088
1089 pushedVia->SetRank( aNewRank );
1090 PNS_DBG( Dbg(), Message, wxString::Format("via rank %d, fanout %d\n", pushedVia->Rank(), (int) draggedLines.size() ) );
1091
1092 PNS_DBG( Dbg(), AddPoint, aVia->Pos(), LIGHTGREEN, 100000, "via-pre" );
1093 PNS_DBG( Dbg(), AddPoint, pushedVia->Pos(), LIGHTRED, 100000, "via-post" );
1094
1095 VIA *v2 = pushedVia.get();
1096
1097 if( !aDontUnwindStack )
1098 unwindLineStack( aVia );
1099
1100 replaceItems( aVia, std::move( pushedVia ) );
1101
1102 if( draggedLines.empty() ) // stitching via? make sure the router won't forget about it
1103 {
1104 LINE tmpLine;
1105 tmpLine.LinkVia( v2 );
1106 if( !pushLineStack( tmpLine ) )
1107 return SH_INCOMPLETE;
1108 }
1109
1110 int n = 0;
1111 for( LINE_PAIR lp : draggedLines )
1112 {
1113 if( !aDontUnwindStack )
1114 unwindLineStack( &lp.first );
1115
1116 PNS_DBG( Dbg(), Message, wxString::Format("fan %d/%d\n", n, (int) draggedLines.size() ) );
1117 n++;
1118
1119 if( lp.second.SegmentCount() )
1120 {
1121 lp.second.ClearLinks();
1122 ROOT_LINE_ENTRY* rootEntry = replaceLine( lp.first, lp.second, true, true );
1123
1124 lp.second.LinkVia( v2 );
1125
1126 if( !aDontUnwindStack )
1127 unwindLineStack( &lp.second );
1128
1129 lp.second.SetRank( aNewRank );
1130
1131 if( rootEntry )
1132 rootEntry->newLine = lp.second; // fixme: it's inelegant
1133
1134
1135 PNS_DBG( Dbg(), Message, wxString::Format("PushViaF %p %d eov %d\n", &lp.second, lp.second.SegmentCount(), lp.second.EndsWithVia()?1:0 ) );
1136
1137 if( !pushLineStack( lp.second ) ) //, true ) ) // WHY?
1138 return SH_INCOMPLETE;
1139 }
1140 else
1141 {
1142 m_currentNode->Remove( lp.first );
1143 }
1144
1145 PNS_DBG( Dbg(), AddItem, &lp.first, LIGHTGREEN, 10000, wxT( "fan-pre" ) );
1146 PNS_DBG( Dbg(), AddItem, &lp.second, LIGHTRED, 10000, wxT( "fan-post" ) );
1147 }
1148
1149 return SH_OK;
1150}
1151
1152
1153/*
1154 * Calculate the minimum translation vector required to resolve a collision with a via and
1155 * shove the via by that distance.
1156 */
1157SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo, int aNextRank )
1158{
1159 assert( aObstacleVia );
1160
1161 int clearance = getClearance( aCurrent, aObstacleVia );
1162 VECTOR2I mtv;
1163 int rank = -1;
1164
1165 bool lineCollision = false;
1166 bool viaCollision = false;
1167 bool solidCollision = false;
1168 VECTOR2I mtvLine, mtvVia, mtvSolid;
1169
1170 PNS_DBG( Dbg(), BeginGroup, "push-via-by-line", 1 );
1171
1172 if( aCurrent->OfKind( ITEM::LINE_T ) )
1173 {
1174 VIA vtmp ( *aObstacleVia );
1175 int layer = aCurrent->Layer();
1176
1177 if( aObstacleInfo.m_maxFanoutWidth > 0
1178 && aObstacleInfo.m_maxFanoutWidth > aObstacleVia->Diameter( layer ) )
1179 {
1180 vtmp.SetDiameter( layer, aObstacleInfo.m_maxFanoutWidth );
1181 }
1182
1183 LINE* currentLine = (LINE*) aCurrent;
1184
1185 PNS_DBG( Dbg(), AddItem, currentLine, LIGHTRED, 10000, wxT( "current-line" ) );
1186
1187 if( currentLine->EndsWithVia() )
1188 {
1189 PNS_DBG( Dbg(), AddItem, &currentLine->Via(), LIGHTRED, 10000, wxT( "current-line-via" ) );
1190 }
1191
1192 PNS_DBG( Dbg(), AddItem, vtmp.Clone(), LIGHTRED, 100000, wxT( "orig-via" ) );
1193
1194 lineCollision = vtmp.Shape( layer )->Collide( currentLine->Shape( -1 ),
1195 clearance + currentLine->Width() / 2,
1196 &mtvLine );
1197
1198 // Check the via if present. Via takes priority.
1199 if( currentLine->EndsWithVia() )
1200 {
1201 const VIA& currentVia = currentLine->Via();
1202 int viaClearance = getClearance( &currentVia, &vtmp );
1203 VECTOR2I layerMtv;
1204
1205 for( int viaLayer : currentVia.RelevantShapeLayers( &vtmp ) )
1206 {
1207 viaCollision |= currentVia.Shape( viaLayer )->Collide( vtmp.Shape( viaLayer ),
1208 viaClearance,
1209 &layerMtv );
1210
1211 if( layerMtv.SquaredEuclideanNorm() > mtvVia.SquaredEuclideanNorm() )
1212 mtvVia = layerMtv;
1213 }
1214 }
1215 }
1216 else if( aCurrent->OfKind( ITEM::SOLID_T ) )
1217 {
1218 PNS_DBG( Dbg(), Message, wxT("collidee-is-solid" ) );
1219 // TODO(JE) if this case is real, handle via stacks
1220 solidCollision = aCurrent->Shape( -1 )->Collide( aObstacleVia->Shape( -1 ), clearance,
1221 &mtvSolid );
1222 //PNS_DBGN( Dbg(), EndGroup );
1223
1224 // TODO: is this possible at all? We don't shove solids.
1225 //return SH_INCOMPLETE;
1226 }
1227
1228 // fixme: we may have a sign issue in Collide(CIRCLE, LINE_CHAIN)
1229 if( viaCollision )
1230 mtv = -mtvVia;
1231 else if ( lineCollision )
1232 mtv = -mtvLine;
1233 else if ( solidCollision )
1234 mtv = -mtvSolid;
1235 else
1236 mtv = VECTOR2I(0, 0);
1237
1238 SHOVE::SHOVE_STATUS st = pushOrShoveVia( aObstacleVia, -mtv, aNextRank );
1239 PNS_DBG( Dbg(), Message, wxString::Format("push-or-shove-via st %d", st ) );
1240
1241 PNS_DBGN( Dbg(), EndGroup );
1242
1243 return st;
1244}
1245
1246
1247/*
1248 * TODO describe....
1249 */
1250SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo )
1251{
1252 int n = 0;
1253
1254 if( aCurrent.EndsWithVia() )
1255 {
1256 const VECTOR2I p0 = aCurrent.Via().Pos();
1257 const VECTOR2I p1 = aObstacleVia->Pos();
1258
1259 int layer = aCurrent.Layer();
1260 int dist = (p0 - p1).EuclideanNorm() - aCurrent.Via().Diameter( layer ) / 2
1261 - aObstacleVia->Diameter( layer ) / 2;
1262
1263 int clearance = getClearance( &aCurrent.Via(), aObstacleVia );
1264
1265 SHAPE_LINE_CHAIN hull = aObstacleVia->Hull( clearance, aCurrent.Width(), layer );
1266
1267 bool epInsideHull = hull.PointInside( p0 );
1268
1269 PNS_DBG( Dbg(), AddShape, &hull, LIGHTYELLOW, 100000, wxT( "obstacle-via-hull" ) );
1270 PNS_DBG( Dbg(), Message, wxString::Format("via2via coll check dist %d cl %d delta %d pi %d\n", dist, clearance, dist - clearance, epInsideHull ? 1 : 0) );
1271
1272 bool viaCollision = false;
1273
1274 for( int viaLayer : aCurrent.Via().RelevantShapeLayers( aObstacleVia ) )
1275 {
1276 viaCollision |=
1277 aCurrent.Via().Shape( viaLayer )->Collide( aObstacleVia->Shape( viaLayer ),
1278 clearance );
1279 }
1280
1281 if( viaCollision )
1282 {
1283 return onCollidingVia( &aCurrent, aObstacleVia, aObstacleInfo, aCurrent.Rank() - 1 );
1284 }
1285 }
1286
1287 LINE cur( aCurrent );
1288 cur.ClearLinks();
1289
1290 const JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
1291 LINE shoved( aCurrent );
1292 shoved.ClearLinks();
1293
1294 cur.RemoveVia();
1295 unwindLineStack( &aCurrent );
1296
1297 for( ITEM* item : jt->LinkList() )
1298 {
1299 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->LayersOverlap( &aCurrent ) )
1300 {
1301 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
1302 LINE head = assembleLine( li );
1303
1304 head.AppendVia( *aObstacleVia );
1305
1306 bool shoveOK = ShoveObstacleLine( head, cur, shoved );
1307
1308 if( !shoveOK )
1309 {
1310 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via-fail-shove", m_iter );
1311 PNS_DBG( Dbg(), AddItem, aObstacleVia, LIGHTRED, 100000, wxT( "obstacle-via" ) );
1312 PNS_DBG( Dbg(), AddItem, &aCurrent, LIGHTGREEN, 10000, wxT( "current-line" ) );
1313 PNS_DBG( Dbg(), AddItem, &shoved, LIGHTRED, 10000, wxT( "shoved-line" ) );
1314
1315 if( aCurrent.EndsWithVia() )
1316 {
1317 PNS_DBG( Dbg(), AddItem, &aCurrent.Via(), LIGHTGREEN, 100000, wxT( "current-line-via" ) );
1318 }
1319
1320 PNS_DBGN( Dbg(), EndGroup );
1321
1322 return SH_INCOMPLETE;
1323 }
1324
1325 cur.SetShape( shoved.CLine() );
1326 n++;
1327 }
1328 }
1329
1330 if( !n )
1331 {
1332 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via-fail-lonevia", m_iter );
1333 PNS_DBG( Dbg(), AddItem, aObstacleVia, LIGHTRED, 100000, wxT( "the-via" ) );
1334 PNS_DBG( Dbg(), AddItem, &aCurrent, LIGHTGREEN, 10000, wxT( "current-line" ) );
1335 PNS_DBGN( Dbg(), EndGroup );
1336
1337 LINE head( aCurrent );
1338 head.Line().Clear();
1339 head.AppendVia( *aObstacleVia );
1340 head.ClearLinks();
1341
1342 bool shoveOK = ShoveObstacleLine( head, aCurrent, shoved );
1343
1344 if( !shoveOK )
1345 return SH_INCOMPLETE;
1346
1347 cur.SetShape( shoved.CLine() );
1348 }
1349
1350 if( aCurrent.EndsWithVia() )
1351 shoved.AppendVia( aCurrent.Via() );
1352
1353 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via", m_iter );
1354 PNS_DBG( Dbg(), AddItem, aObstacleVia, YELLOW, 0, wxT( "rr-the-via" ) );
1355 PNS_DBG( Dbg(), AddItem, &aCurrent, BLUE, 0, wxT( "rr-current-line" ) );
1356 PNS_DBG( Dbg(), AddItem, &shoved, GREEN, 0, wxT( "rr-shoved-line" ) );
1357 PNS_DBGN( Dbg(), EndGroup );
1358
1359 int currentRank = aCurrent.Rank();
1360 replaceLine( aCurrent, shoved, true, false );
1361
1362 if( !pushLineStack( shoved ) )
1363 return SH_INCOMPLETE;
1364
1365 shoved.SetRank( currentRank );
1366
1367 return SH_OK;
1368}
1369
1370
1372{
1373 int d = 0;
1374
1375 for( std::vector<LINE>::iterator i = m_lineStack.begin(); i != m_lineStack.end() ; )
1376 {
1377 if( i->ContainsLink( aSeg ) )
1378 {
1379
1380// note to my future self: if we have a "tadpole" in the stack, keep track of the via even if the parent line has been deleted.
1381// otherwise - the via will be ignored in the case of collisions with tracks on another layer. Can happen pretty often in densely packed PCBs.
1382 if( i->EndsWithVia() && !aSeg->OfKind( ITEM::VIA_T ) )
1383 {
1384 VIA* via = nullptr;
1385
1386 for( LINKED_ITEM* l : i->Links() )
1387 {
1388 if( l->OfKind( ITEM::VIA_T ) )
1389 {
1390 via = static_cast<VIA*>( l );
1391 }
1392 }
1393
1394 if( via )
1395 {
1396 i->ClearLinks();
1397 i->LinkVia( via );
1398 }
1399 i++;
1400 }
1401 else
1402 {
1403 i = m_lineStack.erase( i );
1404 }
1405 }
1406 else
1407 {
1408 i++;
1409 }
1410
1411 d++;
1412 }
1413
1414 for( std::vector<LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end() ; )
1415 {
1416 if( i->ContainsLink( aSeg ) )
1417 i = m_optimizerQueue.erase( i );
1418 else
1419 i++;
1420 }
1421}
1422
1423
1424void SHOVE::unwindLineStack( const ITEM* aItem )
1425{
1426 if( aItem->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
1427 {
1428 unwindLineStack( static_cast<const LINKED_ITEM*>( aItem ) );
1429 }
1430 else if( aItem->OfKind( ITEM::LINE_T ) )
1431 {
1432 const LINE* l = static_cast<const LINE*>( aItem );
1433
1434 for( const LINKED_ITEM* seg : l->Links() )
1435 unwindLineStack( seg );
1436 }
1437}
1438
1439
1440bool SHOVE::pushLineStack( const LINE& aL, bool aKeepCurrentOnTop )
1441{
1442 if( !aL.IsLinked() && aL.SegmentCount() != 0 )
1443 {
1444 PNS_DBG( Dbg(), AddItem, &aL, BLUE, 10000, wxT( "push line stack failed" ) );
1445
1446 return false;
1447 }
1448
1449 if( aKeepCurrentOnTop && m_lineStack.size() > 0)
1450 {
1451 m_lineStack.insert( m_lineStack.begin() + m_lineStack.size() - 1, aL );
1452 }
1453 else
1454 {
1455 m_lineStack.push_back( aL );
1456 }
1457
1458
1460 m_optimizerQueue.push_back( aL );
1461
1462 return true;
1463}
1464
1465
1467{
1468 int pre = m_optimizerQueue.size();
1469 for( std::vector<LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
1470 {
1471 bool found = false;
1472
1473 for( LINKED_ITEM* s : aLine.Links() )
1474 {
1475 PNS_DBG( Dbg(), Message,
1476 wxString::Format( "cur lc %d lnk %p cnt %d", i->LinkCount(), s, aLine.LinkCount() ) );
1477
1478 if( i->ContainsLink( s ) && !s->OfKind( ITEM::VIA_T ) )
1479 {
1480
1481 i = m_optimizerQueue.erase( i );
1482 found = true;
1483 break;
1484 }
1485 }
1486
1487 if( !found )
1488 i++;
1489 }
1490
1491 return true;
1492}
1493
1495{
1496 LINE& l = m_lineStack.back();
1498 m_lineStack.pop_back();
1499}
1500
1501
1502bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs )
1503{
1504 int layer = aCurrent->Layer();
1505
1506 // if the current obstacle is a via, consider also the lines connected to it
1507 // if their widths are larger or equal than the via diameter, the shove algorithm
1508 // will very likely fail in the subsequent iterations (as our base assumption is track
1509 // ends can never move on their own, only dragged by force-propagated vias
1510
1511 // our colliding item is a via: just find the max width of the traces connected to it
1512 if( obs.m_item->OfKind( ITEM::VIA_T ) )
1513 {
1514 const VIA* v = static_cast<const VIA*>( obs.m_item );
1515 int maxw = 0;
1516 const JOINT* jv = m_currentNode->FindJoint( v->Pos(), v );
1517
1518 ITEM_SET links( jv->CLinks() );
1519
1520 for( ITEM* link : links )
1521 {
1522 if( link->OfKind( ITEM::SEGMENT_T ) ) // consider segments ...
1523 {
1524 const SEGMENT* seg = static_cast<const SEGMENT*>( link );
1525 maxw = std::max( seg->Width(), maxw );
1526 }
1527 else if( link->OfKind( ITEM::ARC_T ) ) // ... or arcs
1528 {
1529 const ARC* arc = static_cast<const ARC*>( link );
1530 maxw = std::max( arc->Width(), maxw );
1531 }
1532 }
1533
1534 obs.m_maxFanoutWidth = 0;
1535
1536 if( maxw > 0 && maxw >= v->Diameter( layer ) )
1537 {
1538 obs.m_maxFanoutWidth = maxw + 1;
1539 PNS_DBG( Dbg(), Message,
1540 wxString::Format( "Fixup via: new-w %d via-w %d", maxw, v->Diameter( layer ) ) );
1541
1542 return true;
1543 }
1544 return false;
1545 }
1546
1547
1548 // our colliding item is a segment. check if it has a via on either of the ends.
1549 if( !obs.m_item->OfKind( ITEM::SEGMENT_T ) )
1550 return false;
1551
1552 const SEGMENT* s = static_cast<const SEGMENT*>( obs.m_item );
1553 int sl = s->Layer();
1554
1555 const JOINT* ja = m_currentNode->FindJoint( s->Seg().A, s );
1556 const JOINT* jb = m_currentNode->FindJoint( s->Seg().B, s );
1557
1558 VIA* vias[] = { ja->Via(), jb->Via() };
1559
1560 for( int i = 0; i < 2; i++ )
1561 {
1562 VIA* v = vias[i];
1563
1564 // via diameter is larger than the segment width - cool, the force propagation algo
1565 // will be able to deal with it, no need to intervene
1566 if( !v || v->Diameter( sl ) > s->Width() )
1567 continue;
1568
1569 VIA vtest( *v );
1570 vtest.SetDiameter( sl, s->Width() );
1571
1572 // enlarge the via to the width of the segment
1573 if( vtest.Collide( aCurrent, m_currentNode, aCurrent->Layer() ) )
1574 {
1575 // if colliding, drop the segment in the shove iteration loop and force-propagate the via instead
1576 obs.m_item = v;
1577 obs.m_maxFanoutWidth = s->Width() + 1;
1578 return true;
1579 }
1580 }
1581 return false;
1582}
1583
1584/*
1585 * Resolve the next collision.
1586 */
1588{
1589 LINE currentLine = m_lineStack.back();
1590 NODE::OPT_OBSTACLE nearest;
1591 SHOVE_STATUS st = SH_NULL;
1592
1593 ROUTER_IFACE* iface = Router()->GetInterface();
1594
1595 if( Dbg() )
1596 Dbg()->SetIteration( aIter );
1597
1598 PNS_DBG( Dbg(), AddItem, &currentLine, RED, currentLine.Width(),
1599 wxString::Format( wxT( "current sc=%d net=%s evia=%d" ),
1600 currentLine.SegmentCount(),
1601 iface->GetNetName( currentLine.Net() ),
1602 currentLine.EndsWithVia() ? 1 : 0 ) );
1603
1605 {
1607 opts.m_kindMask = search_order;
1608 opts.m_filter = [ this ] ( const ITEM* item ) -> bool
1609 {
1610 bool rv = true;
1611
1613 {
1614 const LINKED_ITEM* litem = static_cast<const LINKED_ITEM*>( item );
1615 ROOT_LINE_ENTRY* ent = this->findRootLine( litem );
1616
1617 if( !ent && m_defaultPolicy & SHP_IGNORE )
1618 rv = false;
1619
1620 if( ent && ent->policy & SHP_IGNORE )
1621 rv = false;
1622 }
1623 else
1624 {
1626 rv = false;
1627 }
1628
1629 return rv;
1630 };
1631
1632 nearest = m_currentNode->NearestObstacle( &currentLine, opts );
1633
1634 if( nearest )
1635 {
1636 PNS_DBG( Dbg(), AddShape, nearest->m_item->Shape( currentLine.Layer() ), YELLOW, 10000,
1637 wxString::Format( "nearest %p %s rank %d",
1638 nearest->m_item,
1639 nearest->m_item->KindStr(),
1640 nearest->m_item->Rank() ) );
1641 }
1642
1643 if( nearest )
1644 break;
1645 }
1646
1647 if( !nearest )
1648 {
1649 m_lineStack.pop_back();
1650 PNS_DBG( Dbg(), Message, wxT( "no-nearest-item ") );
1651 return SH_OK;
1652 }
1653
1654 bool viaFixup = fixupViaCollisions( &currentLine, *nearest );
1655
1656 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "iter %d: via-fixup %d" ), aIter, viaFixup?1:0 ) );
1657
1658
1659 ITEM* ni = nearest->m_item;
1660
1661 UNITS_PROVIDER up( pcbIUScale, EDA_UNITS::MILLIMETRES );
1662 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "NI: %s (%s) %p %d" ),
1663
1664 ni->Format(),
1665 ni->Parent() ? ni->Parent()->GetItemDescription( &up, false )
1666 : wxString( wxT( "null" ) ),
1667 ni,
1668 ni->OwningNode()->Depth() ) );
1669
1670 unwindLineStack( ni );
1671
1672 if( !ni->OfKind( ITEM::SOLID_T ) && ni->Rank() >= 0 && ni->Rank() > currentLine.Rank() )
1673 {
1674 // Collision with a higher-ranking object (ie: one that we've already shoved)
1675 //
1676 switch( ni->Kind() )
1677 {
1678 case ITEM::VIA_T:
1679 {
1680 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-via" ), aIter ), 0 );
1681
1682 // TODO(JE) viastacks -- via-via collisions here?
1683 if( currentLine.EndsWithVia()
1684 && nearest->m_item->Collide( &currentLine.Via(), m_currentNode,
1685 nearest->m_item->Layer() ) )
1686 {
1687 PNS_DBG( Dbg(), AddItem, nearest->m_item, YELLOW, 100000, wxT("v2v nearesti" ) );
1688 //PNS_DBG( Dbg(), AddItem, nearest->m_head,RED, 100000, wxString::Format("v2v nearesth force=%d,%d" ) );
1689
1690 st = onCollidingVia( &currentLine, (VIA*) ni, *nearest, ni->Rank() + 1 );
1691
1692 //ni->SetRank( currentLine.Rank() );
1693 }
1694 else
1695 {
1696 st = onReverseCollidingVia( currentLine, (VIA*) ni, *nearest );
1697 }
1698
1699 PNS_DBGN( Dbg(), EndGroup );
1700
1701 break;
1702 }
1703
1704 case ITEM::SEGMENT_T:
1705 {
1706 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-segment" ),
1707 aIter ), 0 );
1708
1709 PNS_DBG( Dbg(), AddItem, ni, YELLOW, 100000, wxT("head" ) );
1710
1711 LINE revLine = assembleLine( static_cast<SEGMENT*>( ni ) );
1712
1713 popLineStack();
1714
1715 if( currentLine.EndsWithVia()
1716 && currentLine.Via().Collide( (SEGMENT*) ni, m_currentNode, currentLine.Layer() ) )
1717 {
1718 VIA_HANDLE vh;
1719 vh.layers = currentLine.Via().Layers();
1720 vh.net = currentLine.Via().Net();
1721 vh.pos = currentLine.Via().Pos();
1722 vh.valid = true;
1723 auto rvia = m_currentNode->FindViaByHandle( vh );
1724 if ( !rvia )
1725 st = SH_INCOMPLETE;
1726 else
1727 st = onCollidingVia( &revLine, rvia, *nearest, revLine.Rank() + 1 );
1728 }
1729 else
1730 st = onCollidingLine( revLine, currentLine, revLine.Rank() + 1 );
1731
1732
1733 if( !pushLineStack( revLine ) )
1734 {
1735 return SH_INCOMPLETE;
1736 }
1737
1738
1739 PNS_DBGN( Dbg(), EndGroup );
1740
1741 break;
1742 }
1743
1744 case ITEM::ARC_T:
1745 {
1746 //TODO(snh): Handle Arc shove separate from track
1747 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-arc " ), aIter ), 0 );
1748 LINE revLine = assembleLine( static_cast<ARC*>( ni ) );
1749
1750 popLineStack();
1751 st = onCollidingLine( revLine, currentLine, revLine.Rank() - 1 );
1752
1753 PNS_DBGN( Dbg(), EndGroup );
1754
1755 if( !pushLineStack( revLine ) )
1756 return SH_INCOMPLETE;
1757
1758 break;
1759 }
1760
1761 default:
1762 assert( false );
1763 }
1764 }
1765 else
1766 {
1767 // Collision with a lower-ranking object or a solid
1768 //
1769 switch( ni->Kind() )
1770 {
1771 case ITEM::SEGMENT_T:
1772 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-segment " ), aIter ), 0 );
1773
1774 st = onCollidingSegment( currentLine, (SEGMENT*) ni );
1775
1776 if( st == SH_TRY_WALK )
1777 st = onCollidingSolid( currentLine, ni, *nearest );
1778
1779 PNS_DBGN( Dbg(), EndGroup );
1780
1781 break;
1782
1783 //TODO(snh): Customize Arc collide
1784 case ITEM::ARC_T:
1785 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-arc " ), aIter ), 0 );
1786
1787 st = onCollidingArc( currentLine, static_cast<ARC*>( ni ) );
1788
1789 if( st == SH_TRY_WALK )
1790 st = onCollidingSolid( currentLine, ni, *nearest );
1791
1792 PNS_DBGN( Dbg(), EndGroup );
1793
1794 break;
1795
1796 case ITEM::VIA_T:
1797 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-via (fixup: %d)" ), aIter, 0 ), 0 );
1798 st = onCollidingVia( &currentLine, (VIA*) ni, *nearest, currentLine.Rank() - 1 );
1799
1800 if( st == SH_TRY_WALK )
1801 st = onCollidingSolid( currentLine, ni, *nearest );
1802
1803 PNS_DBGN( Dbg(), EndGroup );
1804
1805 break;
1806
1807 case ITEM::HOLE_T:
1808 case ITEM::SOLID_T:
1809 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: walk-solid " ), aIter ), 0);
1810 st = onCollidingSolid( currentLine, ni, *nearest );
1811
1812 PNS_DBGN( Dbg(), EndGroup );
1813
1814 break;
1815
1816 default:
1817 break;
1818 }
1819 }
1820
1821 return st;
1822}
1823
1824
1825/*
1826 * Resolve collisions.
1827 * Each iteration pushes the next colliding object out of the way. Iterations are continued as
1828 * long as they propagate further collisions, or until the iteration timeout or max iteration
1829 * count is reached.
1830 */
1832{
1833 SHOVE_STATUS st = SH_OK;
1834
1836
1837 PNS_DBG( Dbg(), Message, wxString::Format( "ShoveStart [root: %d jts, current: %d jts]",
1838 m_root->JointCount(),
1840
1841 int iterLimit = Settings().ShoveIterationLimit();
1842 TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
1843
1844 m_iter = 0;
1845
1846 timeLimit.Restart();
1847
1848 if( m_lineStack.empty() && m_draggedVia )
1849 {
1850 // If we're shoving a free via then push a proxy LINE (with the via on the end) onto
1851 // the stack.
1853 }
1854
1855 while( !m_lineStack.empty() )
1856 {
1857 PNS_DBG( Dbg(), Message, wxString::Format( "iter %d: node %p stack %d ", m_iter,
1858 m_currentNode, (int) m_lineStack.size() ) );
1859
1860 st = shoveIteration( m_iter );
1861
1862 m_iter++;
1863
1864 if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
1865 {
1866 PNS_DBG( Dbg(), Message, wxString::Format( "Fail [time limit expired: %d iter %d iter limit %d",
1867 timeLimit.Expired()?1:0, m_iter, iterLimit ) );
1868 st = SH_INCOMPLETE;
1869 break;
1870 }
1871 }
1872
1873 return st;
1874}
1875
1876
1878{
1879 OPT_BOX2I area;
1880
1881 if( !m_nodeStack.empty() )
1882 area = m_nodeStack.back().m_affectedArea;
1883
1884 if( area && m_affectedArea)
1885 area->Merge( *m_affectedArea );
1886 else if( !area )
1887 area = m_affectedArea;
1888
1889 return area;
1890}
1891
1892
1894{
1895 for( const LINKED_ITEM* link : aLine.Links() )
1896 {
1897 auto it = m_rootLineHistory.find( link->Uid() );
1898
1899 if( it != m_rootLineHistory.end() )
1900 return it->second;
1901 }
1902
1903 return nullptr;
1904}
1905
1907{
1908 auto it = m_rootLineHistory.find( aItem->Uid() );
1909
1910 if( it != m_rootLineHistory.end() )
1911 return it->second;
1912
1913 return nullptr;
1914}
1915
1916
1918{
1919 for( const LINKED_ITEM* link : aLine.Links() )
1920 {
1921 auto it = m_rootLineHistory.find( link->Uid() );
1922
1923 if( it != m_rootLineHistory.end() )
1924 {
1925 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [found] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1926
1927 return it->second;
1928 }
1929 }
1930
1931 auto rootEntry = new ROOT_LINE_ENTRY( aLine.Clone() );
1932
1933
1934 for( const LINKED_ITEM* link : aLine.Links() )
1935 {
1936 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1937 m_rootLineHistory[link->Uid()] = rootEntry;
1938 }
1939
1940
1941 return rootEntry;
1942}
1943
1944
1946{
1947 auto it = m_rootLineHistory.find( aItem->Uid() );
1948
1949 if( it != m_rootLineHistory.end() )
1950 {
1951 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
1952 return it->second;
1953 }
1954
1955 auto rootEntry = new ROOT_LINE_ENTRY( nullptr );
1956
1957 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
1958 m_rootLineHistory[ aItem->Uid() ] = rootEntry;
1959
1960 return rootEntry;
1961}
1962
1963
1965{
1966 OPTIMIZER optimizer( aNode );
1967 int optFlags = 0;
1968 int n_passes = 0;
1969
1971
1973
1974 int maxWidth = 0;
1975
1976 for( LINE& line : m_optimizerQueue )
1977 maxWidth = std::max( line.Width(), maxWidth );
1978
1979 if( area )
1980 {
1981 area->Inflate( maxWidth );
1982 area = area->Intersect( VisibleViewArea() );
1983 }
1984
1985 switch( effort )
1986 {
1987 case OE_LOW:
1988 optFlags |= OPTIMIZER::MERGE_OBTUSE;
1989 n_passes = 1;
1990 break;
1991
1992 case OE_MEDIUM:
1993 optFlags |= OPTIMIZER::MERGE_SEGMENTS;
1994 n_passes = 2;
1995 break;
1996
1997 case OE_FULL:
1998 optFlags = OPTIMIZER::MERGE_SEGMENTS;
1999 n_passes = 2;
2000 break;
2001
2002 default:
2003 break;
2004 }
2005
2007
2008 if( area )
2009 {
2010 SHAPE_RECT r( *area );
2011
2012 PNS_DBG( Dbg(), AddShape, &r, BLUE, 0, wxT( "opt-area" ) );
2013
2014 optFlags |= OPTIMIZER::RESTRICT_AREA;
2015 optimizer.SetRestrictArea( *area, false );
2016 }
2017
2019
2020 // Smart Pads is incompatible with 90-degree mode for now
2021 if( Settings().SmartPads()
2022 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 ) )
2023 {
2024 optFlags |= OPTIMIZER::SMART_PADS;
2025 }
2026
2027
2028 optimizer.SetEffortLevel( optFlags & ~m_optFlagDisableMask );
2029 optimizer.SetCollisionMask( ITEM::ANY_T );
2030
2031 std::set<const ITEM*> itemsChk;
2032
2033 auto iface = Router()->GetInterface();
2034
2035 for( int pass = 0; pass < n_passes; pass++ )
2036 {
2037 std::reverse( m_optimizerQueue.begin(), m_optimizerQueue.end() );
2038
2039 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "optimize %d lines, pass %d"), (int)m_optimizerQueue.size(), (int)pass ) );
2040
2041 for( int i = 0; i < m_optimizerQueue.size(); i++ )
2042 {
2043 LINE& lineToOpt = m_optimizerQueue[i];
2044 LINE* rootLine = nullptr;
2045 auto rootEntry = findRootLine( lineToOpt );
2046
2047 if( rootEntry )
2048 {
2049 rootLine = rootEntry->rootLine;
2050
2051 if( rootEntry->policy & SHP_DONT_OPTIMIZE )
2052 continue;
2053 if( rootEntry->isHead )
2054 continue;
2055 }
2056
2057 LINE optimized;
2058 if( optimizer.Optimize( &lineToOpt, &optimized, rootLine ) )
2059 {
2060 assert( optimized.LinkCount() == 0 );
2061
2062 //PNS_DBG( Dbg(), AddShape, &lineToOpt.CLine(), BLUE, 0, wxT( "shove-pre-opt" ) );
2063 //if( rootLine )
2064 // PNS_DBG( Dbg(), AddItem, rootLine, RED, 0, wxT( "shove-root-opt" ) );
2065
2066 replaceLine( lineToOpt, optimized, false, aNode );
2067 m_optimizerQueue[i] = optimized; // keep links in the lines in the queue up to date
2068
2069 //PNS_DBG( Dbg(), AddShape, &optimized.CLine(), GREEN, 0, wxT( "shove-post-opt" ) );
2070 }
2071 }
2072 }
2073}
2074
2075
2077{
2078 return m_currentNode ? m_currentNode : m_root; //m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
2079}
2080
2081
2083{
2084 SPRINGBACK_TAG sp;
2085 sp.m_node = aNode;
2086 sp.m_locked = true;
2087
2088 m_nodeStack.push_back(sp);
2089
2090 PNS_DBG( Dbg(), Message, wxString::Format( "addLockedSPNode node=%p stack=%d\n", sp.m_node, (int) m_nodeStack.size() ) );
2091
2092 return true;
2093}
2094
2095
2097{
2098 bool found = false;
2099
2100 auto iter = m_nodeStack.begin();
2101
2102 while( iter != m_nodeStack.end() )
2103 {
2104 if ( iter->m_node == aNode )
2105 {
2106 found = true;
2107 break;
2108 }
2109
2110 iter++;
2111 }
2112
2113 if( !found )
2114 return false;
2115
2116 auto start = iter;
2117
2118 aNode->KillChildren();
2119 m_nodeStack.erase( start, m_nodeStack.end() );
2120
2121 if( !m_nodeStack.empty() )
2122 m_currentNode = m_nodeStack.back().m_node;
2123 else
2125
2126 return true;
2127}
2128
2129
2131{
2132 if( m_nodeStack.empty() )
2133 return false;
2134
2135 while( !m_nodeStack.back().m_locked && m_nodeStack.size() > 1 )
2136 m_nodeStack.pop_back();
2137
2138 m_currentNode = m_nodeStack.back().m_node;
2139
2140 return m_nodeStack.back().m_locked;
2141}
2142
2143
2145{
2146 auto iter = m_nodeStack.begin();
2147
2148 while( iter != m_nodeStack.end() )
2149 {
2150 if ( iter->m_node == aNode )
2151 {
2152 iter->m_locked = false;
2153 break;
2154 }
2155
2156 iter++;
2157 }
2158}
2159
2160
2162{
2163 m_optFlagDisableMask = aMask;
2164}
2165
2166
2168{
2170}
2171
2172
2174{
2175 m_defaultPolicy = aPolicy;
2176}
2177
2178
2179void SHOVE::SetShovePolicy( const LINKED_ITEM* aItem, int aPolicy )
2180{
2181 auto rl = touchRootLine( aItem );
2182 rl->policy = aPolicy;
2183}
2184
2185void SHOVE::SetShovePolicy( const LINE& aLine, int aPolicy )
2186{
2187 auto rl = touchRootLine( aLine );
2188 rl->policy = aPolicy;
2189}
2190
2191
2193{
2194 m_headLines.clear();
2195}
2196
2197
2198void SHOVE::AddHeads( const LINE& aHead, int aPolicy )
2199{
2200 m_headLines.push_back( SHOVE::HEAD_LINE_ENTRY( aHead, aPolicy ) );
2201}
2202
2203
2204void SHOVE::AddHeads( VIA_HANDLE aHead, VECTOR2I aNewPos, int aPolicy )
2205{
2206 SHOVE::HEAD_LINE_ENTRY ent( aHead, aPolicy );
2207 ent.viaNewPos = aNewPos;
2208 ent.prevVia = aHead;
2209 ent.theVia = aHead;
2210 m_headLines.push_back( ent );
2211}
2212
2213void removeHead( NODE *aNode, LINE& head )
2214{
2215 for (auto lnk : head.Links() )
2216 {
2217 if( lnk->BelongsTo( aNode ) )
2218 aNode->Remove( lnk );
2219 }
2220}
2221
2223{
2224 auto iface = Router()->GetInterface();
2225
2226 for( auto& headEntry : m_headLines )
2227 {
2228 if( headEntry.newHead )
2229 {
2230 //nodeStats( Dbg(), m_currentNode, "pre-remove-new" );
2231 int n_lc = headEntry.newHead->LinkCount();
2232 removeHead(m_currentNode, *headEntry.newHead );
2233 //nodeStats( Dbg(), m_currentNode, "post-remove-new" );
2234
2235
2236 wxString msg = wxString::Format(
2237 "remove newhead [net %-20s]: sc %d lc %d\n",
2238 iface->GetNetName( headEntry.newHead->Net() ).c_str().AsChar(),
2239 headEntry.newHead->SegmentCount(), n_lc );
2240 PNS_DBG( Dbg(), Message, msg );
2241 }
2242 else if( headEntry.origHead )
2243 {
2244 wxString msg = wxString::Format(
2245 "remove orighead [net %-20s]: sc %d lc %d node %p depth %d\n",
2246 iface->GetNetName( headEntry.origHead->Net() ).c_str().AsChar(),
2247 headEntry.origHead->SegmentCount(), headEntry.origHead->LinkCount(),
2248 headEntry.origHead->OwningNode(), m_currentNode->Depth() );
2249 PNS_DBG( Dbg(), Message, msg );
2250
2251 removeHead( m_currentNode, *headEntry.origHead );
2252
2253 }
2254 }
2255}
2256
2257
2258void SHOVE::reconstructHeads( bool aShoveFailed )
2259{
2260 int i = 0;
2261 auto iface = Router()->GetInterface();
2262
2263 PNS_DBG( Dbg(), Message, wxString::Format("reconstructing %zu heads", m_headLines.size() ) );
2264
2265 for( auto& headEntry : m_headLines )
2266 {
2267 if( headEntry.origHead )
2268 {
2269 auto rootEntry = findRootLine( *headEntry.origHead );
2270
2271 PNS_DBG( Dbg(), Message, wxString::Format("orig LinkC=%d RE=%p", headEntry.origHead->LinkCount(), rootEntry ) );
2272
2273 assert( rootEntry );
2274 assert( rootEntry->rootLine );
2275
2276 if( rootEntry->newLine )
2277 {
2278 headEntry.newHead = rootEntry->newLine;
2279 headEntry.geometryModified = !rootEntry->newLine->CLine().CompareGeometry( rootEntry->rootLine->CLine() );
2280
2281 wxString msg = wxString::Format(
2282 "head %d/%d [net %-20s]: root %p, lc-root %d, lc-new %d\n", i, (int) m_headLines.size(),
2283 iface->GetNetName( rootEntry->rootLine->Net() ).c_str().AsChar(), rootEntry->rootLine, rootEntry->rootLine->LinkCount(), headEntry.newHead->LinkCount() );
2284 PNS_DBG( Dbg(), AddItem, rootEntry->rootLine, CYAN, 0, msg );
2285 PNS_DBG( Dbg(), Message, msg );
2286
2287 }
2288 else
2289 {
2290 wxString msg = wxString::Format(
2291 "head %d/%d [net %-20s]: unmodified, lc-orig %d\n", i, (int) m_headLines.size(),
2292 iface->GetNetName( headEntry.origHead->Net() ).c_str().AsChar(),
2293 headEntry.origHead->LinkCount() );
2294 PNS_DBG( Dbg(), Message, msg );
2295 }
2296
2297 i++;
2298 } else {
2299 auto rootEntry = findRootLine( headEntry.draggedVia );
2300
2301 if( rootEntry->newVia )
2302 {
2303 headEntry.geometryModified = true;
2304 headEntry.theVia = VIA_HANDLE( rootEntry->newVia->Pos(), rootEntry->newVia->Layers(), rootEntry->newVia->Net() );
2305 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2306 wxString msg = wxString::Format( "[modif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2307
2308 PNS_DBG( Dbg(), Message, msg );
2309 assert( chk != nullptr );
2310 }
2311 else
2312 {
2313 headEntry.theVia = VIA_HANDLE( rootEntry->oldVia->Pos(), rootEntry->oldVia->Layers(), rootEntry->oldVia->Net() );
2314 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2315 wxString msg = wxString::Format( "[unmodif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2316 PNS_DBG( Dbg(), Message, msg );
2317 assert( chk != nullptr );
2318
2319 }
2320
2321
2322 }
2323
2324 m_headsModified |= headEntry.geometryModified;
2325 }
2326}
2327
2328
2329
2331{
2332 //COLLISION_SEARCH_CONTEXT ctx;
2333
2334 //ctx.options.m_differentNetsOnly = false;
2335 //ctx.options.m_kindMask = ITEM::SEGMENT_T; // fixme arcs
2336
2337 SHAPE_LINE_CHAIN orig( aOld->CLine() );
2338
2339 int vc_prev = orig.PointCount();
2340 orig.Simplify2();
2341 int vc_post = orig.PointCount();
2342
2343 *aNew = *aOld;
2344
2345 PNS_DBG( Dbg(), Message, wxString::Format( "**** PreshoveCleanup %d -> %d\n", vc_prev, vc_post ) );
2346
2347 if( vc_prev != vc_post )
2348 {
2349 aNew->ClearLinks();
2350 aNew->SetShape( orig );
2351 replaceLine( *aOld, *aNew );
2352 return true;
2353 }
2354
2355 return false;
2356}
2357
2358// new algo
2360{
2361 SHOVE_STATUS st = SH_OK;
2362
2363 m_multiLineMode = false;
2364 int currentHeadId = 0;
2365 int totalHeads = m_headLines.size();
2366
2367 m_headsModified = false;
2368 m_lineStack.clear();
2369 m_optimizerQueue.clear();
2370
2371 ITEM_SET headSet;
2372
2373 PNS_DBG( Dbg(), Message, wxString::Format("shove run (heads: %d, currentNode=%p, depth=%d)", (int) m_headLines.size(), m_currentNode, m_currentNode->Depth() ) );
2374
2375 for( auto& l : m_headLines )
2376 {
2377 if( l.theVia )
2378 {
2379 PNS_DBG( Dbg(), Message, wxString::Format("process head-via [%d %d] node=%p", l.theVia->pos.x, l.theVia->pos.y, m_currentNode ) );
2380 auto realVia = m_currentNode->FindViaByHandle( *l.theVia );
2381 assert( realVia != nullptr );
2382 headSet.Add( realVia->Clone() );
2383 }
2384 else
2385 {
2386 headSet.Add( *l.origHead->Clone() );
2387 }
2388 }
2389
2390 // Pop NODEs containing previous shoves which are no longer necessary
2391 NODE* parent = reduceSpringback( headSet );
2392 m_currentNode = parent->Branch();
2394
2395
2396
2397
2398
2399
2400 //nodeStats( Dbg(), m_currentNode, "right-after-branch" );
2401
2402 auto iface = Router()->GetInterface();
2403
2404 // for ( auto& hq : m_headLines )
2405 // if( hq.oldHead )
2406 // m_currentNode->Remove( *hq.oldHead );
2407
2408
2409 // Push the via to its new location
2410 for( auto& headLineEntry : m_headLines )
2411 {
2412 //if( rootEntry->line ) // head already processed in previous steps
2413 //{
2414 // PNS_DBG( Dbg(), Message, wxString::Format( "RL found" ) );
2415
2416 //continue;
2417 //}
2419
2420 if( headLineEntry.theVia )
2421 {
2422 VIA* viaToDrag = m_currentNode->FindViaByHandle( *headLineEntry.theVia );
2423
2424 if( !viaToDrag )
2425 {
2426 st = SH_INCOMPLETE;
2427 break;
2428 }
2429
2430 auto viaRoot = touchRootLine( viaToDrag );
2431 viaRoot->oldVia = viaToDrag;
2432 headLineEntry.draggedVia = viaToDrag;
2433
2434 st = pushOrShoveVia( viaToDrag, ( headLineEntry.viaNewPos - viaToDrag->Pos() ), 0, true );
2435
2436 if( st != SH_OK )
2437 break;
2438 }
2439 else
2440 {
2441 // Create a new NODE to store this version of the world
2442 assert( headLineEntry.origHead->LinkCount() == 0 );
2443 m_currentNode->Add( *headLineEntry.origHead );
2444
2445 //nodeStats( Dbg(), m_currentNode, "add-head" );
2446
2447
2448
2449 PNS_DBG( Dbg(), Message,
2450 wxString::Format( "touchRoot ohlc %d roots %d re=%p\n",
2451 headLineEntry.origHead->LinkCount(),
2452 (int) m_rootLineHistory.size(),
2453 findRootLine( *headLineEntry.origHead ) ) );
2454
2455
2456 LINE head( *headLineEntry.origHead );
2457
2458 // empty head? nothing to shove...
2459 if( !head.SegmentCount() && !head.EndsWithVia() )
2460 {
2461 st = SH_INCOMPLETE;
2462 break;
2463 }
2464
2465 currentHeadId++;
2466
2467 m_currentNode->LockJoint( head.CPoint( 0 ), &head, true );
2468
2469 if( !head.EndsWithVia() )
2470 m_currentNode->LockJoint( head.CPoint( -1 ), &head, true );
2471
2472 SetShovePolicy( head, headLineEntry.policy );
2473
2474 //head.Mark( MK_HEAD );
2475 head.SetRank( 100000 ); //- 100 * currentHeadId );
2476
2477 if( head.EndsWithVia() )
2478 {
2479 auto headVia = Clone( head.Via() );
2480 headVia->SetRank( 100000 ); // - 100 * currentHeadId );
2481 headLineEntry.origHead->LinkVia( headVia.get() );
2482 head.LinkVia( headVia.get() );
2483 m_currentNode->Add( std::move( headVia ) );
2484 }
2485
2486 auto headRoot = touchRootLine( *headLineEntry.origHead );
2487 headRoot->isHead = true;
2488 headRoot->rootLine = new PNS::LINE( *headLineEntry.origHead );
2489 headRoot->policy = headLineEntry.policy;
2490
2491
2492 PNS_DBG( Dbg(), Message,
2493 wxString::Format( "headLC %d, rlLC %d oolc %d eov %d\n", head.LinkCount(),
2494 headRoot->rootLine->LinkCount(),
2495 headLineEntry.origHead->LinkCount(),
2496 head.EndsWithVia()?1:0 ) );
2497
2498 //auto rootEntry = findRootLine( &head );
2499
2500 PNS_DBG( Dbg(), Message,
2501 wxString::Format( "Shove heads %d/%d h-lc=%d net=%s Line=%d Policy=%s",
2502 currentHeadId, totalHeads, head.LinkCount(),
2503 iface->GetNetName( head.Net() ), headRoot->newLine ? 1 : 0,
2504 headRoot ? formatPolicy( headRoot->policy )
2505 : wxString( wxT( "default[ne]" ) ) ) );
2506
2507
2508 // nodeStats( Dbg(), m_currentNode, "pre-push-stack" );
2509
2510 if( !pushLineStack( head ) )
2511 {
2512 st = SH_INCOMPLETE;
2513 break;
2514 }
2515 }
2516
2517 st = shoveMainLoop();
2518
2519 //nodeStats( Dbg(), m_currentNode, "post-main-loop" );
2520
2521 if( st != SH_OK )
2522 break;
2523 };
2524
2525 PNS_DBG( Dbg(), Message,
2526 wxString::Format( "Shove status : %s after %d iterations, heads: %d",
2527 ( ( st == SH_OK || st == SH_HEAD_MODIFIED ) ? "OK" : "FAILURE" ),
2528 m_iter, (int) m_headLines.size() ) );
2529 if( st == SH_OK )
2530 {
2531 //nodeStats( Dbg(), m_currentNode, "pre-opt" );
2532
2534
2535 reconstructHeads( false );
2536 removeHeads();
2537
2538 // this must be called afrter reconstructHeads as it requires up-to-date via handles
2540 }
2541 else
2542 {
2543 //reconstructHeads( true );
2544
2545 for( auto& headEntry : m_headLines )
2546 {
2547 if( headEntry.prevVia )
2548 {
2549
2550 PNS_DBG( Dbg(), Message,
2551 wxString::Format( "Fail-restore via mod [%d, %d] orig [%d, %d]",
2552 headEntry.theVia->pos.x,
2553 headEntry.theVia->pos.y,
2554 headEntry.prevVia->pos.x,
2555 headEntry.prevVia->pos.y ) );
2556
2557 headEntry.theVia = headEntry.prevVia;
2558 headEntry.geometryModified = true;
2559 m_headsModified = true;
2560 }
2561 }
2562
2564
2565 delete m_currentNode;
2566 m_currentNode = parent;
2567 }
2568
2569 return st;
2570}
2571
2573 {
2579 SHP_DONT_OPTIMIZE = 0x10
2581
2582const wxString SHOVE::formatPolicy( int aPolicy )
2583{
2584 if( aPolicy == SHP_DEFAULT )
2585 return wxT( "default" );
2586
2587 wxString rv;
2588
2589 if( aPolicy & SHP_SHOVE )
2590 rv.Append( "shove ");
2591 if( aPolicy & SHP_WALK_FORWARD )
2592 rv.Append( "walk-forward ");
2593 if( aPolicy & SHP_WALK_FORWARD )
2594 rv.Append( "walk-back ");
2595 if( aPolicy & SHP_IGNORE )
2596 rv.Append( "ignore ");
2597 if( aPolicy & SHP_IGNORE )
2598 rv.Append( "dont-optimize ");
2599
2600 return rv;
2601}
2602
2603bool SHOVE::HeadsModified( int aIndex ) const
2604{
2605 if( aIndex < 0 )
2606 return m_headsModified;
2607 else
2608 return m_headLines[ aIndex ].geometryModified;
2609}
2610
2611const PNS::LINE SHOVE::GetModifiedHead( int aIndex ) const
2612{
2613 return *m_headLines[ aIndex ].newHead;
2614}
2615
2616const VIA_HANDLE SHOVE::GetModifiedHeadVia( int aIndex ) const
2617{
2618 return *m_headLines[ aIndex ].theVia;
2619}
2620
2621
2622
2623}
2624
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
std::optional< BOX2I > OPT_BOX2I
Definition: box2.h:926
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
CORNER_MODE
Corner modes.
Definition: direction45.h:67
@ ROUNDED_45
H/V/45 with filleted corners.
Definition: direction45.h:69
@ MITERED_45
H/V/45 with mitered corners (default)
Definition: direction45.h:68
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition: eda_item.cpp:111
Base class for all P&S algorithms (shoving, walkaround, line placement, dragging, etc....
Definition: pns_algo_base.h:43
const BOX2I & VisibleViewArea() const
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
Definition: pns_algo_base.h:73
ROUTER * Router() const
Return current router settings.
Definition: pns_algo_base.h:54
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
DEBUG_DECORATOR * Dbg() const
Definition: pns_algo_base.h:78
int Width() const override
Definition: pns_arc.h:88
virtual void SetIteration(int iter)
void Add(const LINE &aLine)
Definition: pns_itemset.cpp:33
Base class for PNS router board items.
Definition: pns_item.h:98
BOARD_ITEM * Parent() const
Definition: pns_item.h:190
virtual const std::string Format() const
Definition: pns_item.cpp:336
virtual int Rank() const
Definition: pns_item.h:254
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition: pns_item.h:230
const PNS_LAYER_RANGE & Layers() const
Definition: pns_item.h:200
virtual NET_HANDLE Net() const
Definition: pns_item.h:198
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:171
std::set< int > RelevantShapeLayers(const ITEM *aOther) const
Returns the set of layers on which either this or the other item can have a unique shape.
Definition: pns_item.cpp:94
virtual int Layer() const
Definition: pns_item.h:204
PnsKind
< Supported item types
Definition: pns_item.h:102
@ SEGMENT_T
Definition: pns_item.h:107
@ LINKED_ITEM_MASK_T
Definition: pns_item.h:113
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition: pns_item.cpp:303
virtual const NODE * OwningNode() const
Definition: pns_item.cpp:352
bool OfKind(int aKindMask) const
Definition: pns_item.h:179
bool LayersOverlap(const ITEM *aOther) const
Return true if the set of layers spanned by aOther overlaps our layers.
Definition: pns_item.h:209
virtual HOLE * Hole() const
Definition: pns_item.h:292
virtual bool HasHole() const
Definition: pns_item.h:291
bool IsLocked() const
Definition: pns_item.h:266
virtual int Marker() const
Definition: pns_item.h:251
A 2D point on a given set of layers and belonging to a certain net, that links together a number of b...
Definition: pns_joint.h:43
const std::vector< ITEM * > & LinkList() const
Definition: pns_joint.h:303
VIA * Via() const
Definition: pns_joint.h:275
const ITEM_SET & CLinks() const
Definition: pns_joint.h:308
bool IsLocked() const
Definition: pns_joint.h:357
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition: pns_line.h:62
const VECTOR2I & CPoint(int aIdx) const
Definition: pns_line.h:146
bool HasLoops() const
Definition: pns_line.cpp:1186
bool HasLockedSegments() const
Definition: pns_line.cpp:1296
int Rank() const override
Definition: pns_line.cpp:1117
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition: pns_line.h:127
virtual void Mark(int aMarker) const override
Definition: pns_line.cpp:123
void LinkVia(VIA *aVia)
Definition: pns_line.cpp:1095
const SHAPE_LINE_CHAIN & CLine() const
Definition: pns_line.h:138
void RemoveVia()
Definition: pns_line.cpp:1315
void SetRank(int aRank) override
Definition: pns_line.cpp:1107
SHAPE_LINE_CHAIN & Line()
Definition: pns_line.h:137
const SHAPE * Shape(int aLayer) const override
Modifiable accessor to the underlying shape.
Definition: pns_line.h:134
virtual int Marker() const override
Definition: pns_line.cpp:142
void AppendVia(const VIA &aVia)
Definition: pns_line.cpp:1082
VIA & Via()
Definition: pns_line.h:198
int SegmentCount() const
Definition: pns_line.h:140
virtual void Unmark(int aMarker=-1) const override
Definition: pns_line.cpp:133
bool Walkaround(SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN &aPre, SHAPE_LINE_CHAIN &aWalk, SHAPE_LINE_CHAIN &aPost, bool aCw) const
Calculate a line tightly wrapping a convex hull of an obstacle object (aObstacle).
bool EndsWithVia() const
Definition: pns_line.h:190
const SEG CSegment(int aIdx) const
Set line width.
Definition: pns_line.h:147
virtual LINE * Clone() const override
Return a deep copy of the item.
Definition: pns_line.cpp:115
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition: pns_line.h:157
UNIQ_ID Uid() const
Keep the router "world" - i.e.
Definition: pns_node.h:231
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition: pns_node.cpp:143
std::vector< ITEM * > ITEM_VECTOR
Definition: pns_node.h:242
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the pre-set worst case clearance between any pair of items.
Definition: pns_node.cpp:129
void Replace(ITEM *aOldItem, std::unique_ptr< ITEM > aNewItem)
Replace an item with another one.
Definition: pns_node.cpp:869
void GetUpdatedItems(ITEM_VECTOR &aRemoved, ITEM_VECTOR &aAdded)
Return the list of items removed and added in this branch with respect to the root branch.
Definition: pns_node.cpp:1446
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition: pns_node.cpp:410
const JOINT * FindJoint(const VECTOR2I &aPos, int aLayer, NET_HANDLE aNet) const
Search for a joint at a given position, layer and belonging to given net.
Definition: pns_node.cpp:1245
std::optional< OBSTACLE > OPT_OBSTACLE
Definition: pns_node.h:241
int Depth() const
Definition: pns_node.h:281
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition: pns_node.cpp:665
OPT_OBSTACLE NearestObstacle(const LINE *aLine, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS())
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition: pns_node.cpp:286
void LockJoint(const VECTOR2I &aPos, const ITEM *aItem, bool aLock)
Definition: pns_node.cpp:1272
int JointCount() const
Return the number of nodes in the inheritance chain (wrs to the root node).
Definition: pns_node.h:275
VIA * FindViaByHandle(const VIA_HANDLE &handle) const
Definition: pns_node.cpp:1729
void KillChildren()
Definition: pns_node.cpp:1526
void ClearRanks(int aMarkerMask=MK_HEAD|MK_VIOLATION)
Definition: pns_node.cpp:1561
void Remove(ARC *aArc)
Remove an item from this branch.
Definition: pns_node.cpp:909
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
Definition: pns_node.cpp:1047
Perform various optimizations of the lines being routed, attempting to make the lines shorter and les...
Definition: pns_optimizer.h:95
void SetRestrictArea(const BOX2I &aArea, bool aStrict=true)
void SetCollisionMask(int aMask)
void SetEffortLevel(int aEffort)
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
@ LIMIT_CORNER_COUNT
Do not attempt to optimize if the resulting line's corner count is outside the predefined range.
@ SMART_PADS
Reroute pad exits.
@ MERGE_SEGMENTS
Reduce corner cost iteratively.
Definition: pns_optimizer.h:99
@ MERGE_OBTUSE
Reduce corner cost by merging obtuse segments.
virtual wxString GetNetName(PNS::NET_HANDLE aNet) const =0
virtual DEBUG_DECORATOR * GetDebugDecorator()=0
ROUTER_IFACE * GetInterface() const
Definition: pns_router.h:223
TIME_LIMIT ShoveTimeLimit() const
PNS_OPTIMIZATION_EFFORT OptimizerEffort() const
Set the optimizer effort. Bigger means cleaner traces, but slower routing.
DIRECTION_45::CORNER_MODE GetCornerMode() const
const SEG & Seg() const
Definition: pns_segment.h:90
int Width() const override
Definition: pns_segment.h:85
const SHAPE_LINE_CHAIN Hull(int aClearance, int aWalkaroundThickness, int aLayer=-1) const override
Definition: pns_line.cpp:569
void SetSpringbackDoNotTouchNode(const NODE *aNode)
Definition: pns_shove.cpp:2167
SHOVE_STATUS shoveIteration(int aIter)
Definition: pns_shove.cpp:1587
SHOVE_STATUS shoveMainLoop()
Definition: pns_shove.cpp:1831
std::vector< SHAPE_LINE_CHAIN > HULL_SET
Definition: pns_shove.h:110
void reconstructHeads(bool aShoveFailed)
Definition: pns_shove.cpp:2258
int m_iter
Definition: pns_shove.h:241
std::pair< LINE, LINE > LINE_PAIR
Definition: pns_shove.h:112
SHOVE_STATUS pushOrShoveVia(VIA *aVia, const VECTOR2I &aForce, int aNextRank, bool aDontUnwindStack=false)
Definition: pns_shove.cpp:1023
SHOVE_STATUS onReverseCollidingVia(LINE &aCurrent, VIA *aObstacleVia, OBSTACLE &aObstacleInfo)
Definition: pns_shove.cpp:1250
OPT_BOX2I m_affectedArea
Definition: pns_shove.h:249
std::vector< LINE > m_lineStack
Definition: pns_shove.h:230
void popLineStack()
Definition: pns_shove.cpp:1494
ROOT_LINE_ENTRY * findRootLine(const LINE &aLine)
Definition: pns_shove.cpp:1893
std::vector< SPRINGBACK_TAG > m_nodeStack
Definition: pns_shove.h:229
@ SHP_DONT_OPTIMIZE
Definition: pns_shove.h:65
@ SHP_IGNORE
Definition: pns_shove.h:64
@ SHP_DEFAULT
Definition: pns_shove.h:60
@ SHP_WALK_FORWARD
Definition: pns_shove.h:62
bool shoveLineToHullSet(const LINE &aCurLine, const LINE &aObstacleLine, LINE &aResultLine, const HULL_SET &aHulls, bool aPermitAdjustingEndpoints=false)
Definition: pns_shove.cpp:305
SHOVE_STATUS onCollidingArc(LINE &aCurrent, ARC *aObstacleArc)
Definition: pns_shove.cpp:669
NODE * m_currentNode
Definition: pns_shove.h:237
SHOVE_STATUS Run()
Definition: pns_shove.cpp:2359
LINE assembleLine(const LINKED_ITEM *aSeg, int *aIndex=nullptr, bool aPreCleanup=false)
Definition: pns_shove.cpp:215
bool checkShoveDirection(const LINE &aCurLine, const LINE &aObstacleLine, const LINE &aShovedLine) const
Definition: pns_shove.cpp:238
int m_forceClearance
Definition: pns_shove.h:243
SHOVE_STATUS onCollidingSegment(LINE &aCurrent, SEGMENT *aObstacleSeg)
Definition: pns_shove.cpp:607
SHOVE(NODE *aWorld, ROUTER *aRouter)
Definition: pns_shove.cpp:190
void DisablePostShoveOptimizations(int aMask)
Definition: pns_shove.cpp:2161
bool RewindSpringbackTo(NODE *aNode)
Definition: pns_shove.cpp:2096
bool pushLineStack(const LINE &aL, bool aKeepCurrentOnTop=false)
Definition: pns_shove.cpp:1440
@ SH_INCOMPLETE
Definition: pns_shove.h:53
@ SH_HEAD_MODIFIED
Definition: pns_shove.h:54
@ SH_TRY_WALK
Definition: pns_shove.h:55
OPT_BOX2I totalAffectedArea() const
Definition: pns_shove.cpp:1877
bool RewindToLastLockedNode()
Definition: pns_shove.cpp:2130
std::deque< HEAD_LINE_ENTRY > m_headLines
Definition: pns_shove.h:232
int m_defaultPolicy
Definition: pns_shove.h:248
const wxString formatPolicy(int aPolicy)
Definition: pns_shove.cpp:2582
bool ShoveObstacleLine(const LINE &aCurLine, const LINE &aObstacleLine, LINE &aResultLine)
Definition: pns_shove.cpp:504
SHOVE_STATUS onCollidingVia(ITEM *aCurrent, VIA *aObstacleVia, OBSTACLE &aObstacleInfo, int aNextRank)
Definition: pns_shove.cpp:1157
void sanityCheck(LINE *aOld, LINE *aNew)
Definition: pns_shove.cpp:183
int m_restrictSpringbackTagId
Definition: pns_shove.h:239
void runOptimizer(NODE *aNode)
Definition: pns_shove.cpp:1964
ROOT_LINE_ENTRY * touchRootLine(const LINE &aLine)
Definition: pns_shove.cpp:1917
const PNS::LINE GetModifiedHead(int aIndex) const
Definition: pns_shove.cpp:2611
bool HeadsModified(int aIndex=-1) const
Definition: pns_shove.cpp:2603
void UnlockSpringbackNode(NODE *aNode)
Definition: pns_shove.cpp:2144
NODE * m_root
Definition: pns_shove.h:236
NODE * reduceSpringback(const ITEM_SET &aHeadSet)
Definition: pns_shove.cpp:904
bool AddLockedSpringbackNode(NODE *aNode)
Definition: pns_shove.cpp:2082
void SetDefaultShovePolicy(int aPolicy)
Definition: pns_shove.cpp:2173
void AddHeads(const LINE &aHead, int aPolicy=SHP_DEFAULT)
Definition: pns_shove.cpp:2198
void SetShovePolicy(const LINKED_ITEM *aItem, int aPolicy)
Definition: pns_shove.cpp:2179
void unwindLineStack(const LINKED_ITEM *aSeg)
Definition: pns_shove.cpp:1371
bool preShoveCleanup(LINE *aOld, LINE *aNew)
Definition: pns_shove.cpp:2330
NODE * CurrentNode()
Definition: pns_shove.cpp:2076
int getClearance(const ITEM *aA, const ITEM *aB) const
Definition: pns_shove.cpp:166
bool m_multiLineMode
Definition: pns_shove.h:244
std::vector< LINE > m_optimizerQueue
Definition: pns_shove.h:231
SHOVE_STATUS onCollidingSolid(LINE &aCurrent, ITEM *aObstacle, OBSTACLE &aObstacleInfo)
Definition: pns_shove.cpp:756
bool pushSpringback(NODE *aNode, const OPT_BOX2I &aAffectedArea)
Definition: pns_shove.cpp:965
int m_optFlagDisableMask
Definition: pns_shove.h:246
SHOVE_STATUS onCollidingLine(LINE &aCurrent, LINE &aObstacle, int aNextRank)
Definition: pns_shove.cpp:718
bool fixupViaCollisions(const LINE *aCurrent, OBSTACLE &obs)
Definition: pns_shove.cpp:1502
bool pruneLineFromOptimizerQueue(const LINE &aLine)
Definition: pns_shove.cpp:1466
void replaceItems(ITEM *aOld, std::unique_ptr< ITEM > aNew)
Definition: pns_shove.cpp:49
bool shoveLineFromLoneVia(const LINE &aCurLine, const LINE &aObstacleLine, LINE &aResultLine)
Definition: pns_shove.cpp:258
VIA * m_draggedVia
Definition: pns_shove.h:240
bool m_headsModified
Definition: pns_shove.h:242
const NODE * m_springbackDoNotTouchNode
Definition: pns_shove.h:238
void ClearHeads()
Definition: pns_shove.cpp:2192
std::unordered_map< LINKED_ITEM::UNIQ_ID, ROOT_LINE_ENTRY * > m_rootLineHistory
Definition: pns_shove.h:234
void pruneRootLines(NODE *aRemovedNode)
Definition: pns_shove.cpp:881
const VIA_HANDLE GetModifiedHeadVia(int aIndex) const
Definition: pns_shove.cpp:2616
ROOT_LINE_ENTRY * replaceLine(LINE &aOld, LINE &aNew, bool aIncludeInChangedArea=true, bool aAllowRedundantSegments=true, NODE *aNode=nullptr)
Definition: pns_shove.cpp:80
std::vector< LINE_PAIR > LINE_PAIR_VEC
Definition: pns_shove.h:113
void removeHeads()
Definition: pns_shove.cpp:2222
bool Expired() const
Definition: time_limit.cpp:39
const CLUSTER AssembleCluster(ITEM *aStart, int aLayer, double aAreaExpansionLimit=0.0, NET_HANDLE aExcludedNet=nullptr)
int Diameter(int aLayer) const
Definition: pns_via.h:191
void SetDiameter(int aLayer, int aDiameter)
Definition: pns_via.h:198
const VECTOR2I & Pos() const
Definition: pns_via.h:175
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition: pns_via.h:229
const SHAPE_LINE_CHAIN Hull(int aClearance=0, int aWalkaroundThickness=0, int aLayer=-1) const override
Definition: pns_via.cpp:217
VIA * Clone() const override
Return a deep copy of the item.
Definition: pns_via.cpp:235
void SetIterationLimit(const int aIterLimit)
void RestrictToCluster(bool aEnabled, const TOPOLOGY::CLUSTER &aCluster)
void SetSolidsOnly(bool aSolidsOnly)
STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
void SetAllowedPolicies(std::vector< WALK_POLICY > aPolicies)
int Start() const
Definition: pns_layerset.h:86
int End() const
Definition: pns_layerset.h:91
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
static double DefaultAccuracyForPCB()
Definition: shape_arc.h:276
A dynamic state checking if a point lies within polygon with a dynamically built outline ( with each ...
void AddPolyline(const SHAPE_LINE_CHAIN &aPolyline)
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Clear()
Remove all points from the line chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
const std::string Format(bool aCplusPlus=true) const override
bool IsArcSegment(size_t aSegment) const
void Insert(size_t aVertex, const VECTOR2I &aP)
long long int Length() const
Return length of the line chain in Euclidean metric.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:181
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:307
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition: vector2d.h:73
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
@ LIGHTGREEN
Definition: color4d.h:63
@ WHITE
Definition: color4d.h:48
@ BLUE
Definition: color4d.h:56
@ LIGHTGRAY
Definition: color4d.h:47
@ LIGHTYELLOW
Definition: color4d.h:49
@ GREEN
Definition: color4d.h:57
@ CYAN
Definition: color4d.h:58
@ YELLOW
Definition: color4d.h:67
@ LIGHTRED
Definition: color4d.h:65
@ RED
Definition: color4d.h:59
Push and Shove diff pair dimensions (gap) settings dialog.
SHOVE_POLICY
Definition: pns_shove.cpp:2573
@ SHP_DEFAULT
Definition: pns_shove.cpp:2574
@ SHP_WALK_BACK
Definition: pns_shove.cpp:2577
@ SHP_IGNORE
Definition: pns_shove.cpp:2578
@ SHP_WALK_FORWARD
Definition: pns_shove.cpp:2576
@ SHP_SHOVE
Definition: pns_shove.cpp:2575
@ SHP_DONT_OPTIMIZE
Definition: pns_shove.cpp:2579
@ MK_HEAD
Definition: pns_item.h:43
OPT_BOX2I ChangedArea(const ITEM *aItemA, const ITEM *aItemB)
Definition: pns_utils.cpp:369
void removeHead(NODE *aNode, LINE &head)
Definition: pns_shove.cpp:2213
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition: pns_item.h:328
#define PNS_DBG(dbg, method,...)
#define PNS_DBGN(dbg, method)
VECTOR2I::extended_type ecoord
Definition: pns_shove.cpp:45
std::vector< FAB_LAYER_COLOR > dummy
std::function< bool(const ITEM *)> m_filter
Definition: pns_node.h:119
Hold an object colliding with another object, along with some useful data about the collision.
Definition: pns_node.h:87
int m_maxFanoutWidth
worst case (largest) width of the tracks connected to the item
Definition: pns_node.h:94
ITEM * m_item
Item found to be colliding with m_head.
Definition: pns_node.h:89
Definition: pns_shove.h:130
std::optional< VIA_HANDLE > theVia
Definition: pns_shove.h:145
std::optional< VIA_HANDLE > prevVia
Definition: pns_shove.h:144
VECTOR2I viaNewPos
Definition: pns_shove.h:147
Definition: pns_shove.h:116
int policy
Definition: pns_shove.h:125
std::optional< LINE > newLine
Definition: pns_shove.h:124
VIA * newVia
Definition: pns_shove.h:123
std::vector< VIA_HANDLE > m_draggedVias
Definition: pns_shove.h:163
std::set< ITEM * > m_items
Definition: pns_topology.h:46
VECTOR2I pos
Definition: pns_via.h:53
NET_HANDLE net
Definition: pns_via.h:55
PNS_LAYER_RANGE layers
Definition: pns_via.h:54
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
int clearance
VECTOR2I v2(1, 0)
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695