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, const ITEM* aRefItem ) -> bool
1655 {
1656 bool rv = true;
1657
1658 if( m_collisionFilter && ! m_collisionFilter( item, aRefItem ) )
1659 return false;
1660
1662 {
1663 const LINKED_ITEM* litem = static_cast<const LINKED_ITEM*>( item );
1664 ROOT_LINE_ENTRY* ent = this->findRootLine( litem );
1665
1666 if( !ent && m_defaultPolicy & SHP_IGNORE )
1667 rv = false;
1668
1669 if( ent && ent->policy & SHP_IGNORE )
1670 rv = false;
1671 }
1672 else
1673 {
1675 rv = false;
1676 }
1677
1678 return rv;
1679 };
1680
1681 nearest = m_currentNode->NearestObstacle( &currentLine, opts );
1682
1683 if( nearest )
1684 {
1685 PNS_DBG( Dbg(), AddShape, nearest->m_item->Shape( currentLine.Layer() ), YELLOW, 10000,
1686 wxString::Format( "nearest %p %s rank %d",
1687 nearest->m_item,
1688 nearest->m_item->KindStr(),
1689 nearest->m_item->Rank() ) );
1690 }
1691
1692 if( nearest )
1693 break;
1694 }
1695
1696 if( !nearest )
1697 {
1698 m_lineStack.pop_back();
1699 PNS_DBG( Dbg(), Message, wxT( "no-nearest-item ") );
1700 return SH_OK;
1701 }
1702
1703 bool viaFixup = fixupViaCollisions( &currentLine, *nearest );
1704
1705 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "iter %d: via-fixup %d" ), aIter, viaFixup?1:0 ) );
1706
1707
1708 ITEM* ni = nearest->m_item;
1709
1711 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "NI: %s (%s) %p %d" ),
1712 ni->Format(),
1713 ni->Parent() ? ni->Parent()->GetItemDescription( &up, false )
1714 : wxString( wxT( "null" ) ),
1715 ni,
1716 ni->OwningNode()->Depth() ) );
1717
1718 unwindLineStack( ni );
1719
1720 if( !ni->OfKind( ITEM::SOLID_T ) && ni->Rank() >= 0 && ni->Rank() > currentLine.Rank() )
1721 {
1722 // Collision with a higher-ranking object (ie: one that we've already shoved)
1723 //
1724 switch( ni->Kind() )
1725 {
1726 case ITEM::VIA_T:
1727 {
1728 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-via" ), aIter ), 0 );
1729
1730 patchTadpoleVia( ni, currentLine );
1731
1732 // TODO(JE) viastacks -- via-via collisions here?
1733 if( currentLine.EndsWithVia()
1734 && nearest->m_item->Collide( &currentLine.Via(), m_currentNode,
1735 nearest->m_item->Layer() ) )
1736 {
1737 PNS_DBG( Dbg(), AddItem, nearest->m_item, YELLOW, 100000, wxT("v2v nearesti" ) );
1738 //PNS_DBG( Dbg(), AddItem, nearest->m_head,RED, 100000, wxString::Format("v2v nearesth force=%d,%d" ) );
1739
1740 st = onCollidingVia( &currentLine, (VIA*) ni, *nearest, ni->Rank() + 1 );
1741
1742 //ni->SetRank( currentLine.Rank() );
1743 }
1744 else
1745 {
1746 st = onReverseCollidingVia( currentLine, (VIA*) ni, *nearest );
1747 }
1748
1749 PNS_DBGN( Dbg(), EndGroup );
1750
1751 break;
1752 }
1753
1754 case ITEM::SEGMENT_T:
1755 {
1756 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-segment" ),
1757 aIter ), 0 );
1758
1759 PNS_DBG( Dbg(), AddItem, ni, YELLOW, 100000, wxT("head" ) );
1760
1761 LINE revLine = assembleLine( static_cast<SEGMENT*>( ni ) );
1762
1763 popLineStack();
1764 unwindLineStack( &revLine );
1765 patchTadpoleVia( ni, currentLine );
1766
1767 if( currentLine.EndsWithVia()
1768 && currentLine.Via().Collide( (SEGMENT*) ni, m_currentNode, currentLine.Layer() ) )
1769 {
1770 VIA_HANDLE vh;
1771 vh.layers = currentLine.Via().Layers();
1772 vh.net = currentLine.Via().Net();
1773 vh.pos = currentLine.Via().Pos();
1774 vh.valid = true;
1775 auto rvia = m_currentNode->FindViaByHandle( vh );
1776 if ( !rvia )
1777 st = SH_INCOMPLETE;
1778 else
1779 st = onCollidingVia( &revLine, rvia, *nearest, revLine.Rank() + 1 );
1780 }
1781 else
1782 st = onCollidingLine( revLine, currentLine, revLine.Rank() + 1 );
1783
1784
1785 if( !pushLineStack( revLine ) )
1786 {
1787 return SH_INCOMPLETE;
1788 }
1789
1790
1791 PNS_DBGN( Dbg(), EndGroup );
1792
1793 break;
1794 }
1795
1796 case ITEM::ARC_T:
1797 {
1798 //TODO(snh): Handle Arc shove separate from track
1799 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-arc " ), aIter ), 0 );
1800 LINE revLine = assembleLine( static_cast<ARC*>( ni ) );
1801
1802 popLineStack();
1803 st = onCollidingLine( revLine, currentLine, revLine.Rank() - 1 );
1804
1805 PNS_DBGN( Dbg(), EndGroup );
1806
1807 if( !pushLineStack( revLine ) )
1808 return SH_INCOMPLETE;
1809
1810 break;
1811 }
1812
1813 default:
1814 assert( false );
1815 }
1816 }
1817 else
1818 {
1819 // Collision with a lower-ranking object or a solid
1820 //
1821 switch( ni->Kind() )
1822 {
1823 case ITEM::SEGMENT_T:
1824 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-segment " ), aIter ), 0 );
1825
1826 st = onCollidingSegment( currentLine, (SEGMENT*) ni );
1827
1828 if( st == SH_TRY_WALK )
1829 st = onCollidingSolid( currentLine, ni, *nearest );
1830
1831 PNS_DBGN( Dbg(), EndGroup );
1832
1833 break;
1834
1835 //TODO(snh): Customize Arc collide
1836 case ITEM::ARC_T:
1837 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-arc " ), aIter ), 0 );
1838
1839 st = onCollidingArc( currentLine, static_cast<ARC*>( ni ) );
1840
1841 if( st == SH_TRY_WALK )
1842 st = onCollidingSolid( currentLine, ni, *nearest );
1843
1844 PNS_DBGN( Dbg(), EndGroup );
1845
1846 break;
1847
1848 case ITEM::VIA_T:
1849 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: collide-via (fixup: %d)" ), aIter, 0 ), 0 );
1850 st = onCollidingVia( &currentLine, (VIA*) ni, *nearest, currentLine.Rank() - 1 );
1851
1852 if( st == SH_TRY_WALK )
1853 st = onCollidingSolid( currentLine, ni, *nearest );
1854
1855 PNS_DBGN( Dbg(), EndGroup );
1856
1857 break;
1858
1859 case ITEM::HOLE_T:
1860 case ITEM::SOLID_T:
1861 PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: walk-solid " ), aIter ), 0);
1862 st = onCollidingSolid( currentLine, ni, *nearest );
1863
1864 PNS_DBGN( Dbg(), EndGroup );
1865
1866 break;
1867
1868 default:
1869 break;
1870 }
1871 }
1872
1873 return st;
1874}
1875
1876
1877/*
1878 * Resolve collisions.
1879 * Each iteration pushes the next colliding object out of the way. Iterations are continued as
1880 * long as they propagate further collisions, or until the iteration timeout or max iteration
1881 * count is reached.
1882 */
1884{
1885 SHOVE_STATUS st = SH_OK;
1886
1888
1889 PNS_DBG( Dbg(), Message, wxString::Format( "ShoveStart [root: %d jts, current: %d jts]",
1890 m_root->JointCount(),
1891 m_currentNode->JointCount() ) );
1892
1893 int iterLimit = Settings().ShoveIterationLimit();
1894 TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
1895
1896 m_iter = 0;
1897
1898 timeLimit.Restart();
1899
1900 if( m_lineStack.empty() && m_draggedVia )
1901 {
1902 // If we're shoving a free via then push a proxy LINE (with the via on the end) onto
1903 // the stack.
1905 }
1906
1907 while( !m_lineStack.empty() )
1908 {
1909 PNS_DBG( Dbg(), Message, wxString::Format( "iter %d: node %p stack %d ", m_iter,
1910 m_currentNode, (int) m_lineStack.size() ) );
1911
1912 st = shoveIteration( m_iter );
1913
1914 m_iter++;
1915
1916 if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
1917 {
1918 PNS_DBG( Dbg(), Message, wxString::Format( "Fail [time limit expired: %d iter %d iter limit %d",
1919 timeLimit.Expired()?1:0, m_iter, iterLimit ) );
1920 st = SH_INCOMPLETE;
1921 break;
1922 }
1923 }
1924
1925 return st;
1926}
1927
1928
1930{
1931 OPT_BOX2I area;
1932
1933 if( !m_nodeStack.empty() )
1934 area = m_nodeStack.back().m_affectedArea;
1935
1936 if( area && m_affectedArea)
1937 area->Merge( *m_affectedArea );
1938 else if( !area )
1939 area = m_affectedArea;
1940
1941 return area;
1942}
1943
1944
1945SHOVE::ROOT_LINE_ENTRY* SHOVE::allocRootLine( std::unique_ptr<LINE> aLine, int aPolicy )
1946{
1947 m_rootLineHistoryEntries.push_back(
1948 std::make_unique<ROOT_LINE_ENTRY>( std::move( aLine ), aPolicy ) );
1949
1950 return m_rootLineHistoryEntries.back().get();
1951}
1952
1953
1955{
1956 for( const LINKED_ITEM* link : aLine.Links() )
1957 {
1958 auto it = m_rootLineHistory.find( link->Uid() );
1959
1960 if( it != m_rootLineHistory.end() )
1961 return it->second;
1962 }
1963
1964 return nullptr;
1965}
1966
1968{
1969 auto it = m_rootLineHistory.find( aItem->Uid() );
1970
1971 if( it != m_rootLineHistory.end() )
1972 return it->second;
1973
1974 return nullptr;
1975}
1976
1977
1979{
1980 for( const LINKED_ITEM* link : aLine.Links() )
1981 {
1982 auto it = m_rootLineHistory.find( link->Uid() );
1983
1984 if( it != m_rootLineHistory.end() )
1985 {
1986 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [found] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1987
1988 return it->second;
1989 }
1990 }
1991
1992 auto rootEntry = allocRootLine( std::unique_ptr<LINE>( aLine.Clone() ) );
1993
1994
1995 for( const LINKED_ITEM* link : aLine.Links() )
1996 {
1997 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1998 m_rootLineHistory[link->Uid()] = rootEntry;
1999 }
2000
2001
2002 return rootEntry;
2003}
2004
2005
2007{
2008 auto it = m_rootLineHistory.find( aItem->Uid() );
2009
2010 if( it != m_rootLineHistory.end() )
2011 {
2012 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
2013 return it->second;
2014 }
2015
2016 auto rootEntry = allocRootLine( nullptr );
2017
2018 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
2019 m_rootLineHistory[ aItem->Uid() ] = rootEntry;
2020
2021 return rootEntry;
2022}
2023
2024
2026{
2027 OPTIMIZER optimizer( aNode );
2028 int optFlags = 0;
2029 int n_passes = 0;
2030
2032
2034
2035 int maxWidth = 0;
2036
2037 for( LINE& line : m_optimizerQueue )
2038 maxWidth = std::max( line.Width(), maxWidth );
2039
2040 if( area )
2041 {
2042 area->Inflate( maxWidth );
2043 area = area->Intersect( VisibleViewArea() );
2044 }
2045
2046 switch( effort )
2047 {
2048 case OE_LOW:
2049 optFlags |= OPTIMIZER::MERGE_OBTUSE;
2050 n_passes = 1;
2051 break;
2052
2053 case OE_MEDIUM:
2054 optFlags |= OPTIMIZER::MERGE_SEGMENTS;
2055 n_passes = 2;
2056 break;
2057
2058 case OE_FULL:
2059 optFlags = OPTIMIZER::MERGE_SEGMENTS;
2060 n_passes = 2;
2061 break;
2062
2063 default:
2064 break;
2065 }
2066
2068
2069 if( area )
2070 {
2071 SHAPE_RECT r( *area );
2072
2073 PNS_DBG( Dbg(), AddShape, &r, BLUE, 0, wxT( "opt-area" ) );
2074
2075 optFlags |= OPTIMIZER::RESTRICT_AREA;
2076 optimizer.SetRestrictArea( *area, false );
2077 }
2078
2080
2081 // Smart Pads is incompatible with 90-degree mode for now
2082 if( Settings().SmartPads()
2083 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 ) )
2084 {
2085 optFlags |= OPTIMIZER::SMART_PADS;
2086 }
2087
2088
2089 optimizer.SetEffortLevel( optFlags & ~m_optFlagDisableMask );
2090 optimizer.SetCollisionMask( ITEM::ANY_T );
2091
2092 std::set<const ITEM*> itemsChk;
2093
2094 for( int pass = 0; pass < n_passes; pass++ )
2095 {
2096 std::reverse( m_optimizerQueue.begin(), m_optimizerQueue.end() );
2097
2098 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "optimize %d lines, pass %d"), (int)m_optimizerQueue.size(), (int)pass ) );
2099
2100 for( int i = 0; i < (int) m_optimizerQueue.size(); i++ )
2101 {
2102 LINE& lineToOpt = m_optimizerQueue[i];
2103 LINE* rootLine = nullptr;
2104 auto rootEntry = findRootLine( lineToOpt );
2105
2106 if( rootEntry )
2107 {
2108 rootLine = rootEntry->rootLine.get();
2109
2110 if( rootEntry->policy & SHP_DONT_OPTIMIZE )
2111 continue;
2112 if( rootEntry->isHead )
2113 continue;
2114 }
2115
2116 LINE optimized;
2117 if( optimizer.Optimize( &lineToOpt, &optimized, rootLine ) )
2118 {
2119 assert( optimized.LinkCount() == 0 );
2120
2121 //PNS_DBG( Dbg(), AddShape, &lineToOpt.CLine(), BLUE, 0, wxT( "shove-pre-opt" ) );
2122 //if( rootLine )
2123 // PNS_DBG( Dbg(), AddItem, rootLine, RED, 0, wxT( "shove-root-opt" ) );
2124
2125 replaceLine( lineToOpt, optimized, false, aNode );
2126 m_optimizerQueue[i] = std::move( optimized ); // keep links in the lines in the queue up to date
2127
2128 //PNS_DBG( Dbg(), AddShape, &optimized.CLine(), GREEN, 0, wxT( "shove-post-opt" ) );
2129 }
2130 }
2131 }
2132}
2133
2134
2136{
2137 return m_currentNode ? m_currentNode : m_root; //m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
2138}
2139
2140
2142{
2143 SPRINGBACK_TAG sp;
2144 sp.m_node = aNode;
2145 sp.m_locked = true;
2146
2147 m_nodeStack.push_back(sp);
2148
2149 PNS_DBG( Dbg(), Message, wxString::Format( "addLockedSPNode node=%p stack=%d\n", sp.m_node, (int) m_nodeStack.size() ) );
2150
2151 return true;
2152}
2153
2154
2156{
2157 bool found = false;
2158
2159 auto iter = m_nodeStack.begin();
2160
2161 while( iter != m_nodeStack.end() )
2162 {
2163 if ( iter->m_node == aNode )
2164 {
2165 found = true;
2166 break;
2167 }
2168
2169 iter++;
2170 }
2171
2172 if( !found )
2173 return false;
2174
2175 auto start = iter;
2176
2177 aNode->KillChildren();
2178 m_nodeStack.erase( start, m_nodeStack.end() );
2179
2180 if( !m_nodeStack.empty() )
2181 m_currentNode = m_nodeStack.back().m_node;
2182 else
2184
2185 return true;
2186}
2187
2188
2190{
2191 if( m_nodeStack.empty() )
2192 return false;
2193
2194 while( !m_nodeStack.back().m_locked && m_nodeStack.size() > 1 )
2195 m_nodeStack.pop_back();
2196
2197 m_currentNode = m_nodeStack.back().m_node;
2198
2199 return m_nodeStack.back().m_locked;
2200}
2201
2202
2204{
2205 auto iter = m_nodeStack.begin();
2206
2207 while( iter != m_nodeStack.end() )
2208 {
2209 if ( iter->m_node == aNode )
2210 {
2211 iter->m_locked = false;
2212 break;
2213 }
2214
2215 iter++;
2216 }
2217}
2218
2219
2221{
2222 m_optFlagDisableMask = aMask;
2223}
2224
2225
2227{
2229}
2230
2231
2233{
2234 m_defaultPolicy = aPolicy;
2235}
2236
2237
2238void SHOVE::SetShovePolicy( const LINKED_ITEM* aItem, int aPolicy )
2239{
2240 auto rl = touchRootLine( aItem );
2241 rl->policy = aPolicy;
2242}
2243
2244void SHOVE::SetShovePolicy( const LINE& aLine, int aPolicy )
2245{
2246 auto rl = touchRootLine( aLine );
2247 rl->policy = aPolicy;
2248}
2249
2250
2252{
2253 m_headLines.clear();
2254}
2255
2256
2257void SHOVE::AddHeads( const LINE& aHead, int aPolicy )
2258{
2259 m_headLines.push_back( SHOVE::HEAD_LINE_ENTRY( aHead, aPolicy ) );
2260 SetShovePolicy( aHead, aPolicy );
2261}
2262
2263
2264void SHOVE::AddHeads( VIA_HANDLE aHead, VECTOR2I aNewPos, int aPolicy )
2265{
2266 SHOVE::HEAD_LINE_ENTRY ent( aHead, aPolicy );
2267 ent.viaNewPos = aNewPos;
2268 ent.prevVia = aHead;
2269 ent.theVia = aHead;
2270 m_headLines.push_back( std::move( ent ) );
2271}
2272
2273void removeHead( NODE *aNode, LINE& head )
2274{
2275 for (auto lnk : head.Links() )
2276 {
2277 if( lnk->BelongsTo( aNode ) )
2278 aNode->Remove( lnk );
2279 }
2280}
2281
2283{
2284 NODE::ITEM_VECTOR removed, added;
2285
2286 m_currentNode->GetUpdatedItems( removed, added );
2287
2288 for( auto& item : added )
2289 {
2290 auto rootEntry = findRootLine( static_cast<LINKED_ITEM*>( item ) );
2291 if( rootEntry && rootEntry->isHead )
2292 {
2293 m_currentNode->Remove( item );
2294 }
2295 }
2296}
2297
2298
2299void SHOVE::reconstructHeads( bool aShoveFailed )
2300{
2301 int i = 0;
2302 auto iface = Router()->GetInterface();
2303
2304 PNS_DBG( Dbg(), Message, wxString::Format("reconstructing %zu heads", m_headLines.size() ) );
2305
2306 for( auto& headEntry : m_headLines )
2307 {
2308 if( headEntry.origHead )
2309 {
2310 auto rootEntry = findRootLine( *headEntry.origHead );
2311
2312 PNS_DBG( Dbg(), Message, wxString::Format("orig LinkC=%d RE=%p", headEntry.origHead->LinkCount(), rootEntry ) );
2313
2314 assert( rootEntry );
2315 assert( rootEntry->rootLine );
2316
2317 if( rootEntry->newLine )
2318 {
2319 headEntry.newHead = rootEntry->newLine;
2320 headEntry.geometryModified = !rootEntry->newLine->CLine().CompareGeometry( rootEntry->rootLine->CLine() );
2321
2322 wxString msg = wxString::Format(
2323 "head %d/%d [net %-20s]: root %p, lc-root %d, lc-new %d\n", i, (int) m_headLines.size(),
2324 iface->GetNetName( rootEntry->rootLine->Net() ).c_str().AsChar(), rootEntry->rootLine.get(), rootEntry->rootLine->LinkCount(), headEntry.newHead->LinkCount() );
2325 PNS_DBG( Dbg(), AddItem, rootEntry->rootLine.get(), CYAN, 0, msg );
2326 PNS_DBG( Dbg(), Message, msg );
2327
2328 }
2329 else
2330 {
2331 wxString msg = wxString::Format(
2332 "head %d/%d [net %-20s]: unmodified, lc-orig %d\n", i, (int) m_headLines.size(),
2333 iface->GetNetName( headEntry.origHead->Net() ).c_str().AsChar(),
2334 headEntry.origHead->LinkCount() );
2335 PNS_DBG( Dbg(), Message, msg );
2336 }
2337
2338 i++;
2339 } else {
2340 auto rootEntry = findRootLine( headEntry.draggedVia );
2341
2342 if( rootEntry->newVia )
2343 {
2344 headEntry.geometryModified = true;
2345 headEntry.theVia = VIA_HANDLE( rootEntry->newVia->Pos(), rootEntry->newVia->Layers(), rootEntry->newVia->Net() );
2346 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2347 wxString msg = wxString::Format( "[modif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2348
2349 PNS_DBG( Dbg(), Message, msg );
2350 assert( chk != nullptr );
2351 }
2352 else
2353 {
2354 headEntry.theVia = VIA_HANDLE( rootEntry->oldVia->Pos(), rootEntry->oldVia->Layers(), rootEntry->oldVia->Net() );
2355 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2356 wxString msg = wxString::Format( "[unmodif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2357 PNS_DBG( Dbg(), Message, msg );
2358 assert( chk != nullptr );
2359
2360 }
2361
2362
2363 }
2364
2365 m_headsModified |= headEntry.geometryModified;
2366 }
2367}
2368
2369
2370
2372{
2373 //COLLISION_SEARCH_CONTEXT ctx;
2374
2375 //ctx.options.m_differentNetsOnly = false;
2376 //ctx.options.m_kindMask = ITEM::SEGMENT_T; // fixme arcs
2377
2378 SHAPE_LINE_CHAIN orig( aOld->CLine() );
2379
2380 int vc_prev = orig.PointCount();
2381 orig.Simplify2();
2382 int vc_post = orig.PointCount();
2383
2384 *aNew = *aOld;
2385
2386 PNS_DBG( Dbg(), Message, wxString::Format( "**** PreshoveCleanup %d -> %d\n", vc_prev, vc_post ) );
2387
2388 if( vc_prev != vc_post )
2389 {
2390 aNew->ClearLinks();
2391 aNew->SetShape( orig );
2392 replaceLine( *aOld, *aNew );
2393 return true;
2394 }
2395
2396 return false;
2397}
2398
2399// new algo
2401{
2402 SHOVE_STATUS st = SH_OK;
2403
2404 m_multiLineMode = false;
2405 int currentHeadId = 0;
2406 int totalHeads = m_headLines.size();
2407
2408 m_headsModified = false;
2409 m_lineStack.clear();
2410 m_optimizerQueue.clear();
2411
2412 ITEM_SET headSet;
2413
2414 PNS_DBG( Dbg(), Message, wxString::Format("shove run (heads: %d, currentNode=%p, depth=%d)", (int) m_headLines.size(), m_currentNode, m_currentNode->Depth() ) );
2415
2416 for( auto& l : m_headLines )
2417 {
2418 if( l.theVia )
2419 {
2420 PNS_DBG( Dbg(), Message, wxString::Format("process head-via [%d %d] node=%p", l.theVia->pos.x, l.theVia->pos.y, m_currentNode ) );
2421 auto realVia = m_currentNode->FindViaByHandle( *l.theVia );
2422 assert( realVia != nullptr );
2423 headSet.Add( realVia->Clone() );
2424 }
2425 else
2426 {
2427 headSet.Add( *l.origHead->Clone() );
2428 }
2429 }
2430
2431 // Pop NODEs containing previous shoves which are no longer necessary
2432 NODE* parent = reduceSpringback( headSet );
2433 m_currentNode = parent->Branch();
2434 m_currentNode->ClearRanks();
2435
2436 //nodeStats( Dbg(), m_currentNode, "right-after-branch" );
2437
2438 auto iface = Router()->GetInterface();
2439
2440 // Push the via to its new location
2441 for( auto& headLineEntry : m_headLines )
2442 {
2443 m_currentNode->ClearRanks();
2444
2445 if( headLineEntry.theVia )
2446 {
2447 VIA* viaToDrag = m_currentNode->FindViaByHandle( *headLineEntry.theVia );
2448
2449 if( !viaToDrag )
2450 {
2451 st = SH_INCOMPLETE;
2452 break;
2453 }
2454
2455 auto viaRoot = touchRootLine( viaToDrag );
2456 viaRoot->oldVia = viaToDrag;
2457 headLineEntry.draggedVia = viaToDrag;
2458
2459 st = pushOrShoveVia( viaToDrag, ( headLineEntry.viaNewPos - viaToDrag->Pos() ), 0, true );
2460
2461 if( st != SH_OK )
2462 break;
2463 }
2464 else
2465 {
2466 // Create a new NODE to store this version of the world
2467 assert( headLineEntry.origHead->LinkCount() == 0 );
2468 m_currentNode->Add( *headLineEntry.origHead, true );
2469
2470 //nodeStats( Dbg(), m_currentNode, "add-head" );
2471
2472
2473
2474 PNS_DBG( Dbg(), Message,
2475 wxString::Format( "touchRoot ohlc %d roots %d re=%p\n",
2476 headLineEntry.origHead->LinkCount(),
2477 (int) m_rootLineHistory.size(),
2478 findRootLine( *headLineEntry.origHead ) ) );
2479
2480
2481 LINE head( *headLineEntry.origHead );
2482
2483 // empty head? nothing to shove...
2484 if( !head.SegmentCount() && !head.EndsWithVia() )
2485 {
2486 st = SH_INCOMPLETE;
2487 break;
2488 }
2489
2490 currentHeadId++;
2491
2492 if( !( headLineEntry.policy & SHP_DONT_LOCK_ENDPOINTS ) )
2493 {
2494 if( head.PointCount() > 0 )
2495 m_currentNode->LockJoint( head.CPoint( 0 ), &head, true );
2496
2497 if( !head.EndsWithVia() )
2498 m_currentNode->LockJoint( head.CLastPoint(), &head, true );
2499 }
2500
2501 SetShovePolicy( head, headLineEntry.policy );
2502
2503 //head.Mark( MK_HEAD );
2504 head.SetRank( 100000 ); //- 100 * currentHeadId );
2505
2506 if( head.EndsWithVia() )
2507 {
2508 auto headVia = Clone( head.Via() );
2509 headVia->SetRank( 100000 ); // - 100 * currentHeadId );
2510 headLineEntry.origHead->LinkVia( headVia.get() );
2511 head.LinkVia( headVia.get() );
2512 m_currentNode->Add( std::move( headVia ) );
2513 }
2514
2515 auto headRoot = touchRootLine( *headLineEntry.origHead );
2516 headRoot->isHead = true;
2517 headRoot->rootLine = std::make_unique<PNS::LINE>( *headLineEntry.origHead );
2518 headRoot->policy = headLineEntry.policy;
2519 if( head.EndsWithVia() )
2520 {
2521 m_rootLineHistory[ headLineEntry.origHead->Via().Uid() ] = headRoot;
2522 }
2523
2524
2525 PNS_DBG( Dbg(), Message,
2526 wxString::Format( "headLC %d, rlLC %d oolc %d eov %d\n", head.LinkCount(),
2527 headRoot->rootLine->LinkCount(),
2528 headLineEntry.origHead->LinkCount(),
2529 head.EndsWithVia()?1:0 ) );
2530
2531 //auto rootEntry = findRootLine( &head );
2532
2533 PNS_DBG( Dbg(), Message,
2534 wxString::Format( "Shove heads %d/%d h-lc=%d net=%s Line=%d Policy=%s",
2535 currentHeadId, totalHeads, head.LinkCount(),
2536 iface->GetNetName( head.Net() ), headRoot->newLine ? 1 : 0,
2537 headRoot ? formatPolicy( headRoot->policy )
2538 : wxString( wxT( "default[ne]" ) ) ) );
2539
2540
2541 // nodeStats( Dbg(), m_currentNode, "pre-push-stack" );
2542
2543 if( !pushLineStack( head ) )
2544 {
2545 st = SH_INCOMPLETE;
2546 break;
2547 }
2548 }
2549
2550 st = shoveMainLoop();
2551
2552 //nodeStats( Dbg(), m_currentNode, "post-main-loop" );
2553
2554 if( st != SH_OK )
2555 break;
2556 };
2557
2558 PNS_DBG( Dbg(), Message,
2559 wxString::Format( "Shove status : %s after %d iterations, heads: %d",
2560 ( ( st == SH_OK || st == SH_HEAD_MODIFIED ) ? "OK" : "FAILURE" ),
2561 m_iter, (int) m_headLines.size() ) );
2562 if( st == SH_OK )
2563 {
2564 //nodeStats( Dbg(), m_currentNode, "pre-opt" );
2565
2567
2568 reconstructHeads( false );
2569 removeHeads();
2570
2571 // this must be called afrter reconstructHeads as it requires up-to-date via handles
2573 }
2574 else
2575 {
2576 //reconstructHeads( true );
2577
2578 for( auto& headEntry : m_headLines )
2579 {
2580 if( headEntry.prevVia )
2581 {
2582
2583 PNS_DBG( Dbg(), Message,
2584 wxString::Format( "Fail-restore via mod [%d, %d] orig [%d, %d]",
2585 headEntry.theVia->pos.x,
2586 headEntry.theVia->pos.y,
2587 headEntry.prevVia->pos.x,
2588 headEntry.prevVia->pos.y ) );
2589
2590 headEntry.theVia = headEntry.prevVia;
2591 headEntry.geometryModified = true;
2592 m_headsModified = true;
2593 }
2594 }
2595
2596 // NODE's destructor invalidates all linked items in the LINEs stored in the stack/queue
2597 // Erase them first to avoid a use-after-free issue.
2598 m_lineStack.clear();
2599 m_optimizerQueue.clear();
2600
2602
2603 delete m_currentNode;
2604 m_currentNode = parent;
2605 }
2606
2607 return st;
2608}
2609
2619
2620const wxString SHOVE::formatPolicy( int aPolicy )
2621{
2622 if( aPolicy == SHP_DEFAULT )
2623 return wxT( "default" );
2624
2625 wxString rv;
2626
2627 if( aPolicy & SHP_SHOVE )
2628 rv.Append( "shove ");
2629 if( aPolicy & SHP_WALK_FORWARD )
2630 rv.Append( "walk-forward ");
2631 if( aPolicy & SHP_WALK_FORWARD )
2632 rv.Append( "walk-back ");
2633 if( aPolicy & SHP_IGNORE )
2634 rv.Append( "ignore ");
2635 if( aPolicy & SHP_IGNORE )
2636 rv.Append( "dont-optimize ");
2637
2638 return rv;
2639}
2640
2641bool SHOVE::HeadsModified( int aIndex ) const
2642{
2643 if( aIndex < 0 )
2644 return m_headsModified;
2645 else
2646 return m_headLines[ aIndex ].geometryModified;
2647}
2648
2649const PNS::LINE SHOVE::GetModifiedHead( int aIndex ) const
2650{
2651 return *m_headLines[ aIndex ].newHead;
2652}
2653
2654const VIA_HANDLE SHOVE::GetModifiedHeadVia( int aIndex ) const
2655{
2656 return *m_headLines[ aIndex ].theVia;
2657}
2658
2663
2664
2665}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:931
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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:304
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:154
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:135
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:146
const VECTOR2I & CLastPoint() const
Definition pns_line.h:155
void RemoveVia()
void SetRank(int aRank) override
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:145
const SHAPE * Shape(int aLayer) const override
Modifiable accessor to the underlying shape.
Definition pns_line.h:142
virtual int Marker() const override
Definition pns_line.cpp:193
void AppendVia(const VIA &aVia)
VIA & Via()
Definition pns_line.h:207
int SegmentCount() const
Definition pns_line.h:148
virtual void Unmark(int aMarker=-1) const override
Definition pns_line.cpp:184
int PointCount() const
Definition pns_line.h:149
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:199
const SEG CSegment(int aIdx) const
Set line width.
Definition pns_line.h:156
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:166
UNIQ_ID Uid() const
Keep the router "world" - i.e.
Definition pns_node.h:243
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:254
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:253
int Depth() const
Definition pns_node.h:304
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:254
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:668
void SetSpringbackDoNotTouchNode(const NODE *aNode)
SHOVE_STATUS shoveIteration(int aIter)
SHOVE_STATUS shoveMainLoop()
std::vector< SHAPE_LINE_CHAIN > HULL_SET
Definition pns_shove.h:115
void reconstructHeads(bool aShoveFailed)
std::pair< LINE, LINE > LINE_PAIR
Definition pns_shove.h:117
SHOVE_STATUS pushOrShoveVia(VIA *aVia, const VECTOR2I &aForce, int aNextRank, bool aDontUnwindStack=false)
SHOVE_STATUS onReverseCollidingVia(LINE &aCurrent, VIA *aObstacleVia, OBSTACLE &aObstacleInfo)
COLLISION_FILTER_FUNC m_collisionFilter
Definition pns_shove.h:281
void SetCollisionFilter(COLLISION_FILTER_FUNC aFunc)
OPT_BOX2I m_affectedArea
Definition pns_shove.h:300
std::vector< LINE > m_lineStack
Definition pns_shove.h:278
void popLineStack()
std::vector< SPRINGBACK_TAG > m_nodeStack
Definition pns_shove.h:277
@ 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:284
NODE * m_currentNode
Definition pns_shove.h:288
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:294
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:280
int m_defaultPolicy
Definition pns_shove.h:299
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:290
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:287
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:295
std::vector< LINE > m_optimizerQueue
Definition pns_shove.h:279
SHOVE_STATUS onCollidingSolid(LINE &aCurrent, ITEM *aObstacle, OBSTACLE &aObstacleInfo)
bool pushSpringback(NODE *aNode, const OPT_BOX2I &aAffectedArea)
int m_optFlagDisableMask
Definition pns_shove.h:297
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:291
bool m_headsModified
Definition pns_shove.h:293
const NODE * m_springbackDoNotTouchNode
Definition pns_shove.h:289
void ClearHeads()
std::unordered_map< LINKED_ITEM::UNIQ_ID, ROOT_LINE_ENTRY * > m_rootLineHistory
Definition pns_shove.h:285
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:118
void removeHeads()
bool Expired() const
const CLUSTER AssembleCluster(ITEM *aStart, int aLayer, double aAreaExpansionLimit=0.0, NET_HANDLE aExcludedNet=nullptr, int aOverrideClearance=0)
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.
std::function< bool(const ITEM *aTestItem, const ITEM *aRefItem)> COLLISION_FILTER_FUNC
Definition pns_node.h:113
@ 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
COLLISION_FILTER_FUNC m_filter
Definition pns_node.h:122
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:136
std::optional< VIA_HANDLE > theVia
Definition pns_shove.h:188
std::optional< VIA_HANDLE > prevVia
Definition pns_shove.h:187
VECTOR2I viaNewPos
Definition pns_shove.h:190
Definition pns_shove.h:121
int policy
Definition pns_shove.h:131
std::optional< LINE > newLine
Definition pns_shove.h:130
VIA * newVia
Definition pns_shove.h:129
std::vector< VIA_HANDLE > m_draggedVias
Definition pns_shove.h:206
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