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