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