KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_shove.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <deque>
23#include <cassert>
24#include <math/box2.h>
25
26#include <wx/log.h>
27
28#include "pns_arc.h"
29#include "pns_line.h"
30#include "pns_node.h"
31#include "pns_debug_decorator.h"
32#include "pns_walkaround.h"
33#include "pns_shove.h"
34#include "pns_solid.h"
35#include "pns_optimizer.h"
36#include "pns_via.h"
37#include "pns_utils.h"
38#include "pns_router.h"
39#include "pns_topology.h"
40
41#include "time_limit.h"
42
43// fixme - move all logger calls to debug decorator
44
46
47namespace PNS {
48
49void SHOVE::replaceItems( ITEM* aOld, std::unique_ptr< ITEM > aNew )
50{
51 OPT_BOX2I changed_area = ChangedArea( aOld, aNew.get() );
52
53 if( changed_area )
54 m_affectedArea = m_affectedArea ? m_affectedArea->Merge( *changed_area ) : *changed_area;
55
56 ROOT_LINE_ENTRY *re = nullptr;
58
59 if( aOld->OfKind( ITEM::VIA_T ) )
60 {
61 VIA* vold = static_cast<VIA*>( aOld );
62 VIA* vnew = static_cast<VIA*>( aNew.get() );
63 re = touchRootLine( vold );
64 re->newVia = vnew;
65 newId = static_cast<VIA*>( aNew.get() )->Uid();
66
67 PNS_DBG( Dbg(), Message,
68 wxString::Format( "replace-via node=%p vold=%p [%d %d]-> vnew=%p [%d %d] nid %llu", m_currentNode, aOld,
69 vold->Pos().x, vold->Pos().y, aNew.get(), vnew->Pos().x,
70 vnew->Pos().y, newId ) );
71 }
72
73 m_currentNode->Replace( aOld, std::move( aNew ) );
74
75 if( re )
76 m_rootLineHistory[ newId ] = re;
77}
78
79
80SHOVE::ROOT_LINE_ENTRY* SHOVE::replaceLine( LINE& aOld, LINE& aNew, bool aIncludeInChangedArea, bool aAllowRedundantSegments, NODE* aNode )
81{
82 if( aIncludeInChangedArea )
83 {
84 OPT_BOX2I changed_area = ChangedArea( aOld, aNew );
85
86 if( changed_area )
87 {
88 SHAPE_RECT r( *changed_area );
89 PNS_DBG( Dbg(), AddShape, &r, BLUE, 0, wxT( "shove-changed-area" ) );
90
91 m_affectedArea = m_affectedArea ? m_affectedArea->Merge( *changed_area )
92 : *changed_area;
93 }
94 }
95
96 int old_sc = aOld.SegmentCount();
97 int old_lc = aOld.LinkCount();
98
99 if( aOld.EndsWithVia() )
100 {
101 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 = new ROOT_LINE_ENTRY( 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, but nonetheless Tom's dumb
240// as.... (censored)
241// Two open curves put together make a closed polygon... Tom should learn high school geometry!
242bool SHOVE::checkShoveDirection( const LINE& aCurLine, const LINE& aObstacleLine,
243 const LINE& aShovedLine ) const
244{
245 SHAPE_LINE_CHAIN::POINT_INSIDE_TRACKER checker( aCurLine.CPoint( 0) );
246 checker.AddPolyline( aObstacleLine.CLine() );
247 checker.AddPolyline( aShovedLine.CLine().Reverse() );
248
249 bool inside = checker.IsInside();
250
251 return !inside;
252}
253
254
255/*
256 * Push aObstacleLine away from aCurLine's via by the clearance distance, returning the result
257 * in aResultLine.
258 *
259 * Must be called only when aCurLine itself is on another layer (or has no segments) so that it
260 * can be ignored.
261 */
262bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLine,
263 LINE& aResultLine )
264{
265 // Build a hull for aCurLine's via and re-walk aObstacleLine around it.
266
267 int obstacleLineWidth = aObstacleLine.Width();
268 const VIA& via = aCurLine.Via();
269 int clearance = getClearance( &via, &aObstacleLine );
270 HOLE* viaHole = via.Hole();
271 int holeClearance = getClearance( viaHole, &aObstacleLine );
272
273 if( holeClearance + via.Drill() / 2 > clearance + via.Diameter( aObstacleLine.Layer() ) / 2 )
274 clearance = holeClearance + via.Drill() / 2 - via.Diameter( aObstacleLine.Layer() ) / 2;
275
276 SHAPE_LINE_CHAIN hull = aCurLine.Via().Hull( clearance, obstacleLineWidth, aCurLine.Layer() );
277 SHAPE_LINE_CHAIN path_cw;
278 SHAPE_LINE_CHAIN path_ccw;
279
280 if( ! aObstacleLine.Walkaround( hull, path_cw, true ) )
281 return false;
282
283 if( ! aObstacleLine.Walkaround( hull, path_ccw, false ) )
284 return false;
285
286 const SHAPE_LINE_CHAIN& shortest = path_ccw.Length() < path_cw.Length() ? path_ccw : path_cw;
287
288 if( shortest.PointCount() < 2 )
289 return false;
290
291 if( aObstacleLine.CLastPoint() != shortest.CLastPoint() )
292 return false;
293
294 if( aObstacleLine.CPoint( 0 ) != shortest.CPoint( 0 ) )
295 return false;
296
297 aResultLine.SetShape( shortest );
298
299 if( aResultLine.Collide( &aCurLine, m_currentNode, aResultLine.Layer() ) )
300 return false;
301
302 return true;
303}
304
305
306/*
307 * Re-walk aObstacleLine around the given set of hulls, returning the result in aResultLine.
308 */
309bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine, LINE& aResultLine,
310 const HULL_SET& aHulls, bool aPermitAdjustingStart,
311 bool aPermitAdjustingEnd )
312{
313 const int c_ENDPOINT_ON_HULL_THRESHOLD = 1000;
314 int attempt;
315 bool permitAdjustingEndpoints = aPermitAdjustingStart || aPermitAdjustingEnd;
316
317 PNS_DBG( Dbg(), BeginGroup, "shove-details", 1 );
318
319 for( attempt = 0; attempt < 4; attempt++ )
320 {
321 bool invertTraversal = ( attempt >= 2 );
322 bool clockwise = attempt % 2;
323 int vFirst = -1, vLast = -1;
324 SHAPE_LINE_CHAIN obs = aObstacleLine.CLine();
325 LINE l( aObstacleLine );
327
328 if( permitAdjustingEndpoints && l.SegmentCount() >= 1 )
329 {
330 auto minDistP = [&]( VECTOR2I pref, int& mdist, int& minhull ) -> VECTOR2I
331 {
332 int min_dist = std::numeric_limits<int>::max();
333 VECTOR2I nearestP;
334
335 for( int i = 0; i < (int) aHulls.size(); i++ )
336 {
337 const SHAPE_LINE_CHAIN& hull =
338 aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
339 int dist;
340 const VECTOR2I p = hull.NearestPoint( pref, true );
341
342 if( hull.PointInside( pref ) )
343 dist = 0;
344 else
345 dist = ( p - pref ).EuclideanNorm();
346
347 if( dist < c_ENDPOINT_ON_HULL_THRESHOLD && dist < min_dist )
348 {
349 bool reject = false;
350
351 if( !reject )
352 {
353 min_dist = dist;
354 nearestP = p;
355 minhull = invertTraversal ? aHulls.size() - 1 - i : i;
356 }
357 }
358 }
359 mdist = min_dist;
360 return nearestP;
361 };
362
363 int minDist0, minDist1, minhull0, minhull1 ;
364 VECTOR2I p0 = minDistP( l.CPoint( 0 ), minDist0, minhull0 );
365 VECTOR2I p1 = minDistP( l.CLastPoint(), minDist1, minhull1 );
366
367 PNS_DBG( Dbg(), Message, wxString::Format( "mindists : %d %d hulls %d %d\n", minDist0, minDist1, minhull0, minhull1 ) );
368
369 if( minDist1 < c_ENDPOINT_ON_HULL_THRESHOLD && aPermitAdjustingEnd )
370 {
371 l.Line().Append( p1 );
372 obs = l.CLine();
373 path = l.CLine();
374 }
375
376 if( minDist0 < c_ENDPOINT_ON_HULL_THRESHOLD && aPermitAdjustingStart )
377 {
378 l.Line().Insert( 0, p0 );
379 obs = l.CLine();
380 path = l.CLine();
381 }
382 }
383
384
385 bool failWalk = false;
386
387 for( int i = 0; i < (int) aHulls.size(); i++ )
388 {
389 const SHAPE_LINE_CHAIN& hull = aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
390
391 PNS_DBG( Dbg(), AddShape, &hull, YELLOW, 10000, wxString::Format( "hull[%d]", i ) );
392 PNS_DBG( Dbg(), AddShape, &path, WHITE, l.Width(), wxString::Format( "path[%d]", i ) );
393 PNS_DBG( Dbg(), AddShape, &obs, LIGHTGRAY, aObstacleLine.Width(), wxString::Format( "obs[%d]", i ) );
394
395 if( !l.Walkaround( hull, path, clockwise ) )
396 {
397 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "Fail-Walk %s %s %d\n" ),
398 hull.Format().c_str(),
399 l.CLine().Format().c_str(),
400 clockwise? 1 : 0) );
401
402 failWalk = true;
403 break;
404 }
405
406 path.Simplify2();
407 l.SetShape( path );
408 }
409
410 if( failWalk )
411 continue;
412
413 for( int i = 0; i < std::min( path.PointCount(), obs.PointCount() ); i++ )
414 {
415 if( path.CPoint( i ) != obs.CPoint( i ) )
416 {
417 vFirst = i;
418 break;
419 }
420 }
421
422 int k = obs.PointCount() - 1;
423
424 for( int i = path.PointCount() - 1; i >= 0 && k >= 0; i--, k-- )
425 {
426 if( path.CPoint( i ) != obs.CPoint( k ) )
427 {
428 vLast = i;
429 break;
430 }
431 }
432
433 if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( obs ) )
434 {
435 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail vfirst-last" ),
436 attempt ) );
437 continue;
438 }
439
440 if( path.CLastPoint() != obs.CLastPoint() || path.CPoint( 0 ) != obs.CPoint( 0 ) )
441 {
442 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail vend-start\n" ),
443 attempt ) );
444 continue;
445 }
446
447 if( !checkShoveDirection( aCurLine, aObstacleLine, l ) )
448 {
449 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail direction-check" ),
450 attempt ) );
451 aResultLine.SetShape( l.CLine() );
452 continue;
453 }
454
455 if( path.SelfIntersecting() )
456 {
457 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail self-intersect" ),
458 attempt ) );
459 continue;
460 }
461
462 bool colliding = l.Collide( &aCurLine, m_currentNode, l.Layer() );
463
464 #if 0
465 if(( aCurLine.Marker() & MK_HEAD ) && !colliding )
466 {
467 const JOINT* jtStart = m_currentNode->FindJoint( aCurLine.CPoint( 0 ), &aCurLine );
468
469 for( ITEM* item : jtStart->LinkList() )
470 {
471 if( item->Collide( &l, m_currentNode, l.Layer() ) )
472 colliding = true;
473 }
474 }
475 #endif
476
477 if( colliding )
478 {
479 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "attempt %d fail coll-check" ),
480 attempt ) );
481 continue;
482 }
483
484 aResultLine.SetShape( l.CLine() );
485
486 PNS_DBGN( Dbg(), EndGroup );
487
488 return true;
489 }
490
491 PNS_DBGN( Dbg(), EndGroup );
492
493 return false;
494}
495
496
497/*
498 * Push aObstacleLine line away from aCurLine by the clearance distance, and return the result in
499 * aResultLine.
500 */
501bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
502 LINE& aResultLine )
503{
504 const int cHullFailureExpansionFactor = 1000;
505 int extraHullExpansion = 0;
506
507 bool voeStart = false, voeEnd = false;
508 const JOINT* jtStart = nullptr;
509 const JOINT* jtEnd = nullptr;
510
511 if( aObstacleLine.PointCount() >= 2 )
512 {
513 jtStart = m_currentNode->FindJoint( aObstacleLine.CPoint( 0 ), &aObstacleLine );
514 jtEnd = m_currentNode->FindJoint( aObstacleLine.CLastPoint(), &aObstacleLine );
515 }
516
517 if( jtStart )
518 voeStart = jtStart->Via() != nullptr;
519 if( jtEnd )
520 voeEnd = jtEnd->Via() != nullptr;
521
522 aResultLine.ClearLinks();
523 bool viaOnEnd = aCurLine.EndsWithVia();
524
525 LINE obstacleLine( aObstacleLine );
526 std::optional<VIA> obsVia;
527
528 if( obstacleLine.EndsWithVia() )
529 {
530 obsVia = aObstacleLine.Via();
531 obstacleLine.RemoveVia();
532 }
533
534 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove process-single: voe-cur %d voe-obs %d" ),
535 aCurLine.EndsWithVia()?1:0, aObstacleLine.EndsWithVia()?1:0 ) );
536
537
538 if( viaOnEnd && ( !aCurLine.LayersOverlap( &obstacleLine ) || aCurLine.SegmentCount() == 0 ) )
539 {
540 // Shove obstacleLine to the hull of aCurLine's via.
541 return shoveLineFromLoneVia( aCurLine, obstacleLine, aResultLine );
542 }
543 else
544 {
545 // Build a set of hulls around the segments of aCurLine. Hulls are at the clearance
546 // distance + obstacleLine's linewidth so that when re-walking obstacleLine along the
547 // hull it will be at the appropriate clearance.
548
549 int obstacleLineWidth = obstacleLine.Width();
550 int clearance = getClearance( &aCurLine, &obstacleLine );
551 int currentLineSegmentCount = aCurLine.SegmentCount();
552
553 /*PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove process-single: cur net %d obs %d cl %d" ),
554 m_router->GetInterface()->GetNetCode( aCurLine.Net() ),
555 m_router->GetInterface()->GetNetCode( obstacleLine.Net() ),
556 clearance ) );*/
557
558 for( int attempt = 0; attempt < 3; attempt++ )
559 {
560 HULL_SET hulls;
561
562 hulls.reserve( currentLineSegmentCount + 1 );
563
564 for( int i = 0; i < currentLineSegmentCount; i++ )
565 {
566 SEGMENT seg( aCurLine, aCurLine.CSegment( i ) );
567
568 // Arcs need additional clearance to ensure the hulls are always bigger than the arc
569 if( aCurLine.CLine().IsArcSegment( i ) )
570 {
571 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "shove add-extra-clearance %d" ),
574 }
575
576 SHAPE_LINE_CHAIN hull = seg.Hull( clearance + extraHullExpansion, obstacleLineWidth, obstacleLine.Layer() );
577
578 hulls.push_back( hull );
579 }
580
581 if( viaOnEnd )
582 {
583 const VIA& via = aCurLine.Via();
584 int viaClearance = getClearance( &via, &obstacleLine );
585 HOLE* viaHole = via.Hole();
586 int holeClearance = getClearance( viaHole, &obstacleLine );
587 int layer = aObstacleLine.Layer();
588
589 if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter( layer ) / 2 )
590 {
591 viaClearance = holeClearance + via.Drill() / 2 - via.Diameter( layer ) / 2;
592 }
593
594 hulls.push_back( aCurLine.Via().Hull( viaClearance, obstacleLineWidth, layer ) );
595 }
596
597 bool permitMovingStart = (attempt >= 2) && !voeStart;
598 bool permitMovingEnd = (attempt >= 2) && !voeEnd;
599
600 if (shoveLineToHullSet( aCurLine, obstacleLine, aResultLine, hulls, permitMovingStart, permitMovingEnd ) )
601 {
602 if( obsVia )
603 aResultLine.AppendVia( *obsVia );
604
605 return true;
606 }
607
608 extraHullExpansion += cHullFailureExpansionFactor;
609 }
610 }
611
612 return false;
613}
614
615
616/*
617 * TODO describe....
618 */
620{
621 int segIndex;
622
623 LINE obstacleLine = assembleLine( aObstacleSeg, &segIndex, true );
624 LINE shovedLine( obstacleLine );
625 SEGMENT tmp( *aObstacleSeg );
626
627 if( obstacleLine.HasLockedSegments() )
628 {
629 PNS_DBG(Dbg(), Message, "try walk (locked segments)");
630 return SH_TRY_WALK;
631 }
632
633 bool shoveOK = ShoveObstacleLine( aCurrent, obstacleLine, shovedLine );
634
635 const double extensionWalkThreshold = 1.0;
636
637 double obsLen = obstacleLine.CLine().Length();
638 double shovedLen = shovedLine.CLine().Length();
639 double extensionFactor = 0.0;
640
641 if( obsLen != 0.0f )
642 extensionFactor = shovedLen / obsLen - 1.0;
643
644 //if( extensionFactor > extensionWalkThreshold )
645 // return SH_TRY_WALK;
646
647 assert( obstacleLine.LayersOverlap( &shovedLine ) );
648
649 if( Dbg() )
650 {
651 PNS_DBG( Dbg(), AddItem, aObstacleSeg, BLUE, 0, wxT( "colliding-segment" ) );
652 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxString::Format( "current-line [links %d l %d v %d]", aCurrent.LinkCount(), aCurrent.Layer(), aCurrent.EndsWithVia() ) );
653 PNS_DBG( Dbg(), AddItem, &obstacleLine, GREEN, 10000, wxString::Format( "obstacle-line [links %d l %d v %d]", obstacleLine.LinkCount(), obstacleLine.Layer(), obstacleLine.EndsWithVia() ) );
654 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 10000, wxT( "shoved-line" ) );
655 }
656
657 if( shoveOK )
658 {
659 int rank = aCurrent.Rank();
660
661 shovedLine.SetRank( rank - 1 );
662 shovedLine.Line().Simplify2();
663
664 unwindLineStack( &obstacleLine );
665
666 replaceLine( obstacleLine, shovedLine, true, false );
667
668 if( !pushLineStack( shovedLine ) )
669 return SH_INCOMPLETE;
670
671 return SH_OK;
672 }
673
674 return SH_INCOMPLETE;
675}
676
677
678/*
679 * TODO describe....
680 */
682{
683 int segIndex;
684 LINE obstacleLine = assembleLine( aObstacleArc, &segIndex );
685 LINE shovedLine( obstacleLine );
686 ARC tmp( *aObstacleArc );
687
688 if( obstacleLine.HasLockedSegments() )
689 return SH_TRY_WALK;
690
691 bool shoveOK = ShoveObstacleLine( aCurrent, obstacleLine, shovedLine );
692
693 const double extensionWalkThreshold = 1.0;
694
695 double obsLen = obstacleLine.CLine().Length();
696 double shovedLen = shovedLine.CLine().Length();
697 double extensionFactor = 0.0;
698
699 if( obsLen != 0.0f )
700 extensionFactor = shovedLen / obsLen - 1.0;
701
702 if( extensionFactor > extensionWalkThreshold )
703 return SH_TRY_WALK;
704
705 assert( obstacleLine.LayersOverlap( &shovedLine ) );
706
707 PNS_DBG( Dbg(), AddItem, &tmp, WHITE, 10000, wxT( "obstacle-arc" ) );
708 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxT( "current-line" ) );
709 PNS_DBG( Dbg(), AddItem, &obstacleLine, GREEN, 10000, wxT( "obstacle-line" ) );
710 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 10000, wxT( "shoved-line" ) );
711
712 if( shoveOK )
713 {
714 int rank = aCurrent.Rank();
715 shovedLine.SetRank( rank - 1 );
716
717 replaceLine( obstacleLine, shovedLine, true, false );
718
719 if( !pushLineStack( shovedLine ) )
720 return SH_INCOMPLETE;
721 }
722
723 return SH_OK;
724}
725
726
727/*
728 * TODO describe....
729 */
730SHOVE::SHOVE_STATUS SHOVE::onCollidingLine( LINE& aCurrent, LINE& aObstacle, int aNextRank )
731{
732 LINE shovedLine( aObstacle );
733
734 bool shoveOK = ShoveObstacleLine( aCurrent, aObstacle, shovedLine );
735
736 PNS_DBG( Dbg(), AddItem, &aObstacle, RED, 100000, wxT( "obstacle-line" ) );
737 PNS_DBG( Dbg(), AddItem, &aCurrent, GREEN, 150000, wxT( "current-line" ) );
738 PNS_DBG( Dbg(), AddItem, &shovedLine, BLUE, 200000, wxT( "shoved-line" ) );
739
740 if( shoveOK )
741 {
742#if 0
743 if( shovedLine.Marker() & MK_HEAD )
744 {
745 if( m_multiLineMode )
746 return SH_INCOMPLETE;
747
748 m_newHead = shovedLine;
749 }
750#endif
751 replaceLine( aObstacle, shovedLine, true, false );
752
753 shovedLine.SetRank( aNextRank );
754
755 if( !pushLineStack( shovedLine ) )
756 return SH_INCOMPLETE;
757
758 return SH_OK;
759 }
760
761 return SH_INCOMPLETE;
762}
763
764
765/*
766 * TODO describe....
767 */
768SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OBSTACLE& aObstacleInfo )
769{
770 LINE walkaroundLine( aCurrent );
771
772 if( aCurrent.EndsWithVia() )
773 {
774 VIA vh = aCurrent.Via();
775 VIA* via = nullptr;
776 const JOINT* jtStart = m_currentNode->FindJoint( vh.Pos(), &aCurrent );
777
778 if( !jtStart )
779 return SH_INCOMPLETE;
780
781 for( ITEM* item : jtStart->LinkList() )
782 {
783 if( item->OfKind( ITEM::VIA_T ) )
784 {
785 via = (VIA*) item;
786 break;
787 }
788 }
789
790 // TODO(JE) viastacks -- can aObstacle be a via?
791 if( via && via->Collide( aObstacle, m_currentNode, aObstacle->Layer() ) )
792 return onCollidingVia( aObstacle, via, aObstacleInfo, aObstacle->Rank() - 1 );
793 }
794
795 TOPOLOGY topo( m_currentNode );
796 TOPOLOGY::CLUSTER cluster = topo.AssembleCluster( aObstacle, aCurrent.Layers().Start(), 10.0 );
797
798 PNS_DBG( Dbg(), BeginGroup, "walk-cluster", 1 );
799
800 for( ITEM* item : cluster.m_items )
801 PNS_DBG( Dbg(), AddItem, item, RED, 10000, wxT( "cl-item" ) );
802
803 PNS_DBGN( Dbg(), EndGroup );
804
805 WALKAROUND walkaround( m_currentNode, Router() );
806 walkaround.SetDebugDecorator( Dbg() );
807 walkaround.SetSolidsOnly( false );
808 walkaround.RestrictToCluster( true, cluster );
810 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() ); // fixme: make configurable
811
812 int currentRank = aCurrent.Rank();
813 int nextRank;
814
815 bool success = false;
816
817 PNS_DBG( Dbg(), AddItem, &aCurrent, RED, 10000, wxT( "current-line" ) );
818
819 for( int attempt = 0; attempt < 2; attempt++ )
820 {
821 if( attempt == 1 || Settings().JumpOverObstacles() )
822 nextRank = currentRank - 1;
823 else
824 nextRank = currentRank + 10000;
825
826 WALKAROUND::RESULT status = walkaround.Route( aCurrent );
827
828 if( status.status[ WALKAROUND::WP_SHORTEST ] != WALKAROUND::ST_DONE )//fixme policies!
829 continue;
830
831 walkaroundLine = status.lines[ WALKAROUND::WP_SHORTEST ];
832
833 walkaroundLine.ClearLinks();
834 walkaroundLine.Unmark();
835 walkaroundLine.Line().Simplify2();
836
837 if( walkaroundLine.HasLoops() )
838 continue;
839
840 PNS_DBG( Dbg(), AddItem, &walkaroundLine, BLUE, 10000, wxT( "walk-line" ) );
841
842#if 0
843 if( aCurrent.Marker() & MK_HEAD )
844 {
845 walkaroundLine.Mark( MK_HEAD );
846
847 if( m_multiLineMode )
848 continue;
849
850 m_newHead = walkaroundLine;
851 }
852#endif
853
854
855 if( !m_lineStack.empty() )
856 {
857 LINE lastLine = m_lineStack.front();
858
859 if( lastLine.Collide( &walkaroundLine, m_currentNode, lastLine.Layer() ) )
860 {
861 LINE dummy( lastLine );
862
863 if( ShoveObstacleLine( walkaroundLine, lastLine, dummy ) )
864 {
865 success = true;
866 break;
867 }
868 }
869 else
870 {
871 success = true;
872 break;
873 }
874 }
875 }
876
877 if(!success)
878 return SH_INCOMPLETE;
879
880 replaceLine( aCurrent, walkaroundLine, true, false );
881 walkaroundLine.SetRank( nextRank );
882
883
884 popLineStack();
885
886 if( !pushLineStack( walkaroundLine ) )
887 return SH_INCOMPLETE;
888
889 return SH_OK;
890}
891
892
893void SHOVE::pruneRootLines( NODE *aRemovedNode )
894{
895 PNS_DBG( Dbg(), Message, wxString::Format("prune called" ) );
896
897 NODE::ITEM_VECTOR added, removed;
898 aRemovedNode->GetUpdatedItems( removed, added );
899
900 for( const ITEM* item : added )
901 {
902 if( item->OfKind( ITEM::LINKED_ITEM_MASK_T ) )
903 {
904 const LINKED_ITEM* litem = static_cast<const LINKED_ITEM*>( item );
905
906 m_rootLineHistory.erase( litem->Uid() );
907 }
908 }
909}
910
911
912/*
913 * Pops NODE stackframes which no longer collide with aHeadSet. Optionally sets aDraggedVia
914 * to the dragged via of the last unpopped state.
915 */
917{
918 while( m_nodeStack.size() > 1 )
919 {
920 SPRINGBACK_TAG& spTag = m_nodeStack.back();
921
922 // Prevent the springback algo from erasing NODEs that might contain items used by the ROUTER_TOOL/LINE_PLACER.
923 // I noticed this can happen for the endItem provided to LINE_PLACER::Move() and cause a nasty crash.
925 break;
926
927 std::optional<OBSTACLE> obs = spTag.m_node->CheckColliding( aHeadSet );
928
929 if( !obs && !spTag.m_locked )
930 {
931 int i;
932
933 PNS_DBG( Dbg(), Message, wxString::Format( "pop-sp node=%p depth=%d", spTag.m_node, spTag.m_node->Depth() ) );
934
935 pruneRootLines( spTag.m_node );
936
937
938 delete spTag.m_node;
939 m_nodeStack.pop_back();
940 }
941 else
942 {
943 break;
944 }
945 }
946
947 if( m_nodeStack.empty() )
948 return m_root;
949
950 SPRINGBACK_TAG& spTag = m_nodeStack.back();
951
952 for( int i = 0; i < spTag.m_draggedVias.size(); i++ )
953 {
954 if (spTag.m_draggedVias[i].valid)
955 {
956 m_headLines[i].prevVia = m_headLines[i].theVia = spTag.m_draggedVias[i];
957 m_headLines[i].geometryModified = true;
958 PNS_DBG( Dbg(), Message,
959 wxString::Format( "restore-springback-via depth=%d %d %d %d %d ",
960 spTag.m_node->Depth(),
961 (int) m_nodeStack.size(),
962 m_headLines[i].theVia->pos.x,
963 m_headLines[i].theVia->pos.y,
964 m_headLines[i].theVia->layers.Start(),
965 m_headLines[i].theVia->layers.End() ) );
966 }
967 }
968
969 return m_nodeStack.back().m_node;
970}
971
972
973/*
974 * Push the current NODE on to the stack. aDraggedVia is the dragged via *before* the push
975 * (which will be restored in the event the stackframe is popped).
976 */
977bool SHOVE::pushSpringback( NODE* aNode, const OPT_BOX2I& aAffectedArea )
978{
980 OPT_BOX2I prev_area;
981
982 if( !m_nodeStack.empty() )
983 prev_area = m_nodeStack.back().m_affectedArea;
984
985 st.m_draggedVias.resize( m_headLines.size() );
986 int n = 0;
987
988 for( HEAD_LINE_ENTRY& head : m_headLines )
989 {
990 if ( head.theVia )
991 {
992 VIA_HANDLE vhandle = *head.theVia;
993
994 PNS_DBG( Dbg(), Message,
995 wxString::Format( "push-sp via depth=%d %d %d %d %d ", aNode->Depth(), vhandle.pos.x,
996 vhandle.pos.y,
997 vhandle.layers.Start(),
998 vhandle.layers.End() ) );
999
1000 st.m_draggedVias[n] = vhandle;
1001 }
1002
1003 n++;
1004 }
1005
1006 st.m_node = aNode;
1007
1008 if( aAffectedArea )
1009 {
1010 if( prev_area )
1011 st.m_affectedArea = prev_area->Merge( *aAffectedArea );
1012 else
1013 st.m_affectedArea = aAffectedArea;
1014 }
1015 else
1016 {
1017 st.m_affectedArea = prev_area;
1018 }
1019
1020 st.m_seq = ( m_nodeStack.empty() ? 1 : m_nodeStack.back().m_seq + 1 );
1021 st.m_locked = false;
1022
1023 m_nodeStack.push_back( st );
1024
1025 PNS_DBG( Dbg(), Message, wxString::Format( "push-sp depth=%d node=%p", st.m_node->Depth(), st.m_node ) );
1026
1027 return true;
1028}
1029
1030
1031/*
1032 * Push or shove a via by at least aForce. (The via might be pushed or shoved slightly further
1033 * to keep it from landing on an existing joint.)
1034 */
1035SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, int aNewRank, bool aDontUnwindStack )
1036{
1037 LINE_PAIR_VEC draggedLines;
1038 VECTOR2I p0( aVia->Pos() );
1039 const JOINT* jt = m_currentNode->FindJoint( p0, aVia );
1040 VECTOR2I p0_pushed( p0 + aForce );
1041
1042 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "via force [%d %d]\n" ), aForce.x, aForce.y ) );
1043
1044 // nothing to do...
1045 if ( aForce.x == 0 && aForce.y == 0 )
1046 return SH_OK;
1047
1048 if( !jt )
1049 {
1050 PNS_DBG( Dbg(), Message, wxT( "weird, can't find the center-of-via joint\n" ) );
1051 return SH_INCOMPLETE;
1052 }
1053
1054 if( Settings().ShoveVias() == false || aVia->IsLocked() )
1055 return SH_TRY_WALK;
1056
1057 if( jt->IsLocked() )
1058 return SH_INCOMPLETE;
1059
1060 // make sure pushed via does not overlap with any existing joint
1061 while( true )
1062 {
1063 const JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia );
1064
1065 if( !jt_next )
1066 break;
1067
1068 p0_pushed += aForce.Resize( 2 );
1069 }
1070
1071 std::unique_ptr<VIA> pushedVia = Clone( *aVia );
1072 pushedVia->SetPos( p0_pushed );
1073 pushedVia->Mark( aVia->Marker() );
1074
1075 for( ITEM* item : jt->LinkList() )
1076 {
1077 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
1078 {
1079 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
1080 LINE_PAIR lp;
1081 int segIndex;
1082
1083 lp.first = assembleLine( li, &segIndex );
1084
1085 if( lp.first.HasLockedSegments() )
1086 return SH_TRY_WALK;
1087
1088 assert( segIndex == 0 || ( segIndex == ( lp.first.SegmentCount() - 1 ) ) );
1089
1090 if( segIndex == 0 )
1091 lp.first.Reverse();
1092
1093 lp.second = lp.first;
1094 lp.second.ClearLinks();
1095 lp.second.DragCorner( p0_pushed, lp.second.CLine().Find( p0 ) );
1096 lp.second.Line().Simplify2();
1097 draggedLines.push_back( std::move( lp ) );
1098 }
1099 }
1100
1101 pushedVia->SetRank( aNewRank );
1102 PNS_DBG( Dbg(), Message, wxString::Format("via rank %d, fanout %d\n", pushedVia->Rank(), (int) draggedLines.size() ) );
1103
1104 PNS_DBG( Dbg(), AddPoint, aVia->Pos(), LIGHTGREEN, 100000, "via-pre" );
1105 PNS_DBG( Dbg(), AddPoint, pushedVia->Pos(), LIGHTRED, 100000, "via-post" );
1106
1107 VIA *v2 = pushedVia.get();
1108
1109 if( !aDontUnwindStack )
1110 unwindLineStack( aVia );
1111
1112 replaceItems( aVia, std::move( pushedVia ) );
1113
1114 if( draggedLines.empty() ) // stitching via? make sure the router won't forget about it
1115 {
1116 LINE tmpLine;
1117 tmpLine.LinkVia( v2 );
1118 if( !pushLineStack( tmpLine ) )
1119 return SH_INCOMPLETE;
1120 }
1121
1122 int n = 0;
1123 for( LINE_PAIR lp : draggedLines )
1124 {
1125 if( !aDontUnwindStack )
1126 unwindLineStack( &lp.first );
1127
1128 PNS_DBG( Dbg(), Message, wxString::Format("fan %d/%d\n", n, (int) draggedLines.size() ) );
1129 n++;
1130
1131 if( lp.second.SegmentCount() )
1132 {
1133 lp.second.ClearLinks();
1134 ROOT_LINE_ENTRY* rootEntry = replaceLine( lp.first, lp.second, true, true );
1135
1136 lp.second.LinkVia( v2 );
1137
1138 if( !aDontUnwindStack )
1139 unwindLineStack( &lp.second );
1140
1141 lp.second.SetRank( aNewRank );
1142
1143 if( rootEntry )
1144 rootEntry->newLine = lp.second; // fixme: it's inelegant
1145
1146
1147 PNS_DBG( Dbg(), Message, wxString::Format("PushViaF %p %d eov %d\n", &lp.second, lp.second.SegmentCount(), lp.second.EndsWithVia()?1:0 ) );
1148
1149 if( !pushLineStack( lp.second ) ) //, true ) ) // WHY?
1150 return SH_INCOMPLETE;
1151 }
1152 else
1153 {
1154 m_currentNode->Remove( lp.first );
1155 }
1156
1157 PNS_DBG( Dbg(), AddItem, &lp.first, LIGHTGREEN, 10000, wxT( "fan-pre" ) );
1158 PNS_DBG( Dbg(), AddItem, &lp.second, LIGHTRED, 10000, wxT( "fan-post" ) );
1159 }
1160
1161 return SH_OK;
1162}
1163
1164
1165/*
1166 * Calculate the minimum translation vector required to resolve a collision with a via and
1167 * shove the via by that distance.
1168 */
1169SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo, int aNextRank )
1170{
1171 assert( aObstacleVia );
1172
1173 int clearance = getClearance( aCurrent, aObstacleVia );
1174 VECTOR2I mtv;
1175 int rank = -1;
1176
1177 bool lineCollision = false;
1178 bool viaCollision = false;
1179 bool solidCollision = false;
1180 VECTOR2I mtvLine, mtvVia, mtvSolid;
1181
1182 PNS_DBG( Dbg(), BeginGroup, "push-via-by-line", 1 );
1183
1184 if( aCurrent->OfKind( ITEM::LINE_T ) )
1185 {
1186 VIA vtmp ( *aObstacleVia );
1187 int layer = aCurrent->Layer();
1188
1189 if( aObstacleInfo.m_maxFanoutWidth > 0
1190 && aObstacleInfo.m_maxFanoutWidth > aObstacleVia->Diameter( layer ) )
1191 {
1192 vtmp.SetDiameter( layer, aObstacleInfo.m_maxFanoutWidth );
1193 }
1194
1195 LINE* currentLine = (LINE*) aCurrent;
1196
1197 PNS_DBG( Dbg(), AddItem, currentLine, LIGHTRED, 10000, wxT( "current-line" ) );
1198
1199 if( currentLine->EndsWithVia() )
1200 {
1201 PNS_DBG( Dbg(), AddItem, &currentLine->Via(), LIGHTRED, 10000, wxT( "current-line-via" ) );
1202 }
1203
1204 PNS_DBG( Dbg(), AddItem, vtmp.Clone(), LIGHTRED, 100000, wxT( "orig-via" ) );
1205
1206 lineCollision = vtmp.Shape( layer )->Collide( currentLine->Shape( -1 ),
1207 clearance + currentLine->Width() / 2,
1208 &mtvLine );
1209
1210 // Check the via if present. Via takes priority.
1211 if( currentLine->EndsWithVia() )
1212 {
1213 const VIA& currentVia = currentLine->Via();
1214 int viaClearance = getClearance( &currentVia, &vtmp );
1215 VECTOR2I layerMtv;
1216
1217 for( int viaLayer : currentVia.RelevantShapeLayers( &vtmp ) )
1218 {
1219 viaCollision |= currentVia.Shape( viaLayer )->Collide( vtmp.Shape( viaLayer ),
1220 viaClearance,
1221 &layerMtv );
1222
1223 if( layerMtv.SquaredEuclideanNorm() > mtvVia.SquaredEuclideanNorm() )
1224 mtvVia = layerMtv;
1225 }
1226 }
1227 }
1228 else if( aCurrent->OfKind( ITEM::SOLID_T ) )
1229 {
1230 PNS_DBG( Dbg(), Message, wxT("collidee-is-solid" ) );
1231 // TODO(JE) if this case is real, handle via stacks
1232 solidCollision = aCurrent->Shape( -1 )->Collide( aObstacleVia->Shape( -1 ), clearance,
1233 &mtvSolid );
1234 //PNS_DBGN( Dbg(), EndGroup );
1235
1236 // TODO: is this possible at all? We don't shove solids.
1237 //return SH_INCOMPLETE;
1238 }
1239
1240 // fixme: we may have a sign issue in Collide(CIRCLE, LINE_CHAIN)
1241 if( viaCollision )
1242 mtv = -mtvVia;
1243 else if ( lineCollision )
1244 mtv = -mtvLine;
1245 else if ( solidCollision )
1246 mtv = -mtvSolid;
1247 else
1248 mtv = VECTOR2I(0, 0);
1249
1250 SHOVE::SHOVE_STATUS st = pushOrShoveVia( aObstacleVia, -mtv, aNextRank );
1251 PNS_DBG( Dbg(), Message, wxString::Format("push-or-shove-via st %d", st ) );
1252
1253 PNS_DBGN( Dbg(), EndGroup );
1254
1255 return st;
1256}
1257
1258
1259/*
1260 * TODO describe....
1261 */
1262SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacleVia, OBSTACLE& aObstacleInfo )
1263{
1264 int n = 0;
1265
1266 if( aCurrent.EndsWithVia() )
1267 {
1268 const VECTOR2I p0 = aCurrent.Via().Pos();
1269 const VECTOR2I p1 = aObstacleVia->Pos();
1270
1271 int layer = aCurrent.Layer();
1272 int dist = (p0 - p1).EuclideanNorm() - aCurrent.Via().Diameter( layer ) / 2
1273 - aObstacleVia->Diameter( layer ) / 2;
1274
1275 int clearance = getClearance( &aCurrent.Via(), aObstacleVia );
1276
1277 const SHAPE_LINE_CHAIN& hull = m_currentNode->GetRuleResolver()->HullCache(
1278 aObstacleVia, clearance, aCurrent.Width(), layer );
1279
1280 bool epInsideHull = hull.PointInside( p0 );
1281
1282 PNS_DBG( Dbg(), AddShape, &hull, LIGHTYELLOW, 100000, wxT( "obstacle-via-hull" ) );
1283 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) );
1284
1285 bool viaCollision = false;
1286
1287 for( int viaLayer : aCurrent.Via().RelevantShapeLayers( aObstacleVia ) )
1288 {
1289 viaCollision |=
1290 aCurrent.Via().Shape( viaLayer )->Collide( aObstacleVia->Shape( viaLayer ),
1291 clearance );
1292 }
1293
1294 if( viaCollision )
1295 {
1296 return onCollidingVia( &aCurrent, aObstacleVia, aObstacleInfo, aCurrent.Rank() - 1 );
1297 }
1298 }
1299
1300 LINE cur( aCurrent );
1301 cur.ClearLinks();
1302
1303 const JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
1304 LINE shoved( aCurrent );
1305 shoved.ClearLinks();
1306
1307 cur.RemoveVia();
1308 unwindLineStack( &aCurrent );
1309
1310 for( ITEM* item : jt->LinkList() )
1311 {
1312 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->LayersOverlap( &aCurrent ) )
1313 {
1314 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
1315 LINE head = assembleLine( li );
1316
1317 head.AppendVia( *aObstacleVia );
1318
1319 bool shoveOK = ShoveObstacleLine( head, cur, shoved );
1320
1321 if( !shoveOK )
1322 {
1323 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via-fail-shove", m_iter );
1324 PNS_DBG( Dbg(), AddItem, aObstacleVia, LIGHTRED, 100000, wxT( "obstacle-via" ) );
1325 PNS_DBG( Dbg(), AddItem, &aCurrent, LIGHTGREEN, 10000, wxT( "current-line" ) );
1326 PNS_DBG( Dbg(), AddItem, &shoved, LIGHTRED, 10000, wxT( "shoved-line" ) );
1327
1328 if( aCurrent.EndsWithVia() )
1329 {
1330 PNS_DBG( Dbg(), AddItem, &aCurrent.Via(), LIGHTGREEN, 100000, wxT( "current-line-via" ) );
1331 }
1332
1333 PNS_DBGN( Dbg(), EndGroup );
1334
1335 return SH_INCOMPLETE;
1336 }
1337
1338 cur.SetShape( shoved.CLine() );
1339 n++;
1340 }
1341 }
1342
1343 if( !n )
1344 {
1345 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via-fail-lonevia", m_iter );
1346 PNS_DBG( Dbg(), AddItem, aObstacleVia, LIGHTRED, 100000, wxT( "the-via" ) );
1347 PNS_DBG( Dbg(), AddItem, &aCurrent, LIGHTGREEN, 10000, wxT( "current-line" ) );
1348 PNS_DBGN( Dbg(), EndGroup );
1349
1350 LINE head( aCurrent );
1351 head.Line().Clear();
1352 head.AppendVia( *aObstacleVia );
1353 head.ClearLinks();
1354
1355 bool shoveOK = ShoveObstacleLine( head, aCurrent, shoved );
1356
1357 if( !shoveOK )
1358 return SH_INCOMPLETE;
1359
1360 cur.SetShape( shoved.CLine() );
1361 }
1362
1363 if( aCurrent.EndsWithVia() )
1364 shoved.AppendVia( aCurrent.Via() );
1365
1366 PNS_DBG( Dbg(), BeginGroup, "on-reverse-via", m_iter );
1367 PNS_DBG( Dbg(), AddItem, aObstacleVia, YELLOW, 0, wxT( "rr-the-via" ) );
1368 PNS_DBG( Dbg(), AddItem, &aCurrent, BLUE, 0, wxT( "rr-current-line" ) );
1369 PNS_DBG( Dbg(), AddItem, &shoved, GREEN, 0, wxT( "rr-shoved-line" ) );
1370 PNS_DBGN( Dbg(), EndGroup );
1371
1372 int currentRank = aCurrent.Rank();
1373 unwindLineStack( &aCurrent );
1374 replaceLine( aCurrent, shoved, true, false );
1375
1376 if( !pushLineStack( shoved ) )
1377 return SH_INCOMPLETE;
1378
1379 shoved.SetRank( currentRank );
1380
1381 return SH_OK;
1382}
1383
1384
1386{
1387 int d = 0;
1388
1389 for( std::vector<LINE>::iterator i = m_lineStack.begin(); i != m_lineStack.end() ; )
1390 {
1391 if( i->ContainsLink( aSeg ) )
1392 {
1393
1394// 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.
1395// otherwise - the via will be ignored in the case of collisions with tracks on another layer. Can happen pretty often in densely packed PCBs.
1396 if( i->EndsWithVia() && !aSeg->OfKind( ITEM::VIA_T ) )
1397 {
1398 VIA* via = nullptr;
1399
1400 for( LINKED_ITEM* l : i->Links() )
1401 {
1402 if( l->OfKind( ITEM::VIA_T ) )
1403 {
1404 via = static_cast<VIA*>( l );
1405 }
1406 }
1407
1408 if( via )
1409 {
1410 i->ClearLinks();
1411 i->Line().Clear();
1412 i->LinkVia( via );
1413 }
1414 i++;
1415 }
1416 else
1417 {
1418 i = m_lineStack.erase( i );
1419 }
1420 }
1421 else
1422 {
1423 i++;
1424 }
1425
1426 d++;
1427 }
1428
1429 for( std::vector<LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end() ; )
1430 {
1431 if( i->ContainsLink( aSeg ) && !aSeg->OfKind( ITEM::VIA_T ) )
1432 i = m_optimizerQueue.erase( i );
1433 else
1434 i++;
1435 }
1436}
1437
1438
1439void SHOVE::unwindLineStack( const ITEM* aItem )
1440{
1441 if( aItem->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
1442 {
1443 unwindLineStack( static_cast<const LINKED_ITEM*>( aItem ) );
1444 }
1445 else if( aItem->OfKind( ITEM::LINE_T ) )
1446 {
1447 const LINE* l = static_cast<const LINE*>( aItem );
1448
1449 for( const LINKED_ITEM* seg : l->Links() )
1450 unwindLineStack( seg );
1451 }
1452}
1453
1454
1455bool SHOVE::pushLineStack( const LINE& aL, bool aKeepCurrentOnTop )
1456{
1457 if( !aL.IsLinked() && aL.SegmentCount() != 0 )
1458 {
1459 PNS_DBG( Dbg(), AddItem, &aL, BLUE, 10000, wxT( "push line stack failed" ) );
1460
1461 return false;
1462 }
1463
1464 if( aKeepCurrentOnTop && m_lineStack.size() > 0)
1465 {
1466 m_lineStack.insert( m_lineStack.begin() + m_lineStack.size() - 1, aL );
1467 }
1468 else
1469 {
1470 m_lineStack.push_back( aL );
1471 }
1472
1473
1475 m_optimizerQueue.push_back( aL );
1476
1477 return true;
1478}
1479
1480
1482{
1483 int pre = m_optimizerQueue.size();
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
1943{
1944 for( const LINKED_ITEM* link : aLine.Links() )
1945 {
1946 auto it = m_rootLineHistory.find( link->Uid() );
1947
1948 if( it != m_rootLineHistory.end() )
1949 return it->second;
1950 }
1951
1952 return nullptr;
1953}
1954
1956{
1957 auto it = m_rootLineHistory.find( aItem->Uid() );
1958
1959 if( it != m_rootLineHistory.end() )
1960 return it->second;
1961
1962 return nullptr;
1963}
1964
1965
1967{
1968 for( const LINKED_ITEM* link : aLine.Links() )
1969 {
1970 auto it = m_rootLineHistory.find( link->Uid() );
1971
1972 if( it != m_rootLineHistory.end() )
1973 {
1974 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [found] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1975
1976 return it->second;
1977 }
1978 }
1979
1980 auto rootEntry = new ROOT_LINE_ENTRY( aLine.Clone() );
1981
1982
1983 for( const LINKED_ITEM* link : aLine.Links() )
1984 {
1985 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu type=%s"), link->Uid(), link->KindStr() ) );
1986 m_rootLineHistory[link->Uid()] = rootEntry;
1987 }
1988
1989
1990 return rootEntry;
1991}
1992
1993
1995{
1996 auto it = m_rootLineHistory.find( aItem->Uid() );
1997
1998 if( it != m_rootLineHistory.end() )
1999 {
2000 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
2001 return it->second;
2002 }
2003
2004 auto rootEntry = new ROOT_LINE_ENTRY( nullptr );
2005
2006 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "touch [create] uid=%llu"), aItem->Uid() ) );
2007 m_rootLineHistory[ aItem->Uid() ] = rootEntry;
2008
2009 return rootEntry;
2010}
2011
2012
2014{
2015 OPTIMIZER optimizer( aNode );
2016 int optFlags = 0;
2017 int n_passes = 0;
2018
2020
2022
2023 int maxWidth = 0;
2024
2025 for( LINE& line : m_optimizerQueue )
2026 maxWidth = std::max( line.Width(), maxWidth );
2027
2028 if( area )
2029 {
2030 area->Inflate( maxWidth );
2031 area = area->Intersect( VisibleViewArea() );
2032 }
2033
2034 switch( effort )
2035 {
2036 case OE_LOW:
2037 optFlags |= OPTIMIZER::MERGE_OBTUSE;
2038 n_passes = 1;
2039 break;
2040
2041 case OE_MEDIUM:
2042 optFlags |= OPTIMIZER::MERGE_SEGMENTS;
2043 n_passes = 2;
2044 break;
2045
2046 case OE_FULL:
2047 optFlags = OPTIMIZER::MERGE_SEGMENTS;
2048 n_passes = 2;
2049 break;
2050
2051 default:
2052 break;
2053 }
2054
2056
2057 if( area )
2058 {
2059 SHAPE_RECT r( *area );
2060
2061 PNS_DBG( Dbg(), AddShape, &r, BLUE, 0, wxT( "opt-area" ) );
2062
2063 optFlags |= OPTIMIZER::RESTRICT_AREA;
2064 optimizer.SetRestrictArea( *area, false );
2065 }
2066
2068
2069 // Smart Pads is incompatible with 90-degree mode for now
2070 if( Settings().SmartPads()
2071 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 ) )
2072 {
2073 optFlags |= OPTIMIZER::SMART_PADS;
2074 }
2075
2076
2077 optimizer.SetEffortLevel( optFlags & ~m_optFlagDisableMask );
2078 optimizer.SetCollisionMask( ITEM::ANY_T );
2079
2080 std::set<const ITEM*> itemsChk;
2081
2082 auto iface = Router()->GetInterface();
2083
2084 for( int pass = 0; pass < n_passes; pass++ )
2085 {
2086 std::reverse( m_optimizerQueue.begin(), m_optimizerQueue.end() );
2087
2088 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "optimize %d lines, pass %d"), (int)m_optimizerQueue.size(), (int)pass ) );
2089
2090 for( int i = 0; i < m_optimizerQueue.size(); i++ )
2091 {
2092 LINE& lineToOpt = m_optimizerQueue[i];
2093 LINE* rootLine = nullptr;
2094 auto rootEntry = findRootLine( lineToOpt );
2095
2096 if( rootEntry )
2097 {
2098 rootLine = rootEntry->rootLine;
2099
2100 if( rootEntry->policy & SHP_DONT_OPTIMIZE )
2101 continue;
2102 if( rootEntry->isHead )
2103 continue;
2104 }
2105
2106 LINE optimized;
2107 if( optimizer.Optimize( &lineToOpt, &optimized, rootLine ) )
2108 {
2109 assert( optimized.LinkCount() == 0 );
2110
2111 //PNS_DBG( Dbg(), AddShape, &lineToOpt.CLine(), BLUE, 0, wxT( "shove-pre-opt" ) );
2112 //if( rootLine )
2113 // PNS_DBG( Dbg(), AddItem, rootLine, RED, 0, wxT( "shove-root-opt" ) );
2114
2115 replaceLine( lineToOpt, optimized, false, aNode );
2116 m_optimizerQueue[i] = std::move( optimized ); // keep links in the lines in the queue up to date
2117
2118 //PNS_DBG( Dbg(), AddShape, &optimized.CLine(), GREEN, 0, wxT( "shove-post-opt" ) );
2119 }
2120 }
2121 }
2122}
2123
2124
2126{
2127 return m_currentNode ? m_currentNode : m_root; //m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
2128}
2129
2130
2132{
2133 SPRINGBACK_TAG sp;
2134 sp.m_node = aNode;
2135 sp.m_locked = true;
2136
2137 m_nodeStack.push_back(sp);
2138
2139 PNS_DBG( Dbg(), Message, wxString::Format( "addLockedSPNode node=%p stack=%d\n", sp.m_node, (int) m_nodeStack.size() ) );
2140
2141 return true;
2142}
2143
2144
2146{
2147 bool found = false;
2148
2149 auto iter = m_nodeStack.begin();
2150
2151 while( iter != m_nodeStack.end() )
2152 {
2153 if ( iter->m_node == aNode )
2154 {
2155 found = true;
2156 break;
2157 }
2158
2159 iter++;
2160 }
2161
2162 if( !found )
2163 return false;
2164
2165 auto start = iter;
2166
2167 aNode->KillChildren();
2168 m_nodeStack.erase( start, m_nodeStack.end() );
2169
2170 if( !m_nodeStack.empty() )
2171 m_currentNode = m_nodeStack.back().m_node;
2172 else
2174
2175 return true;
2176}
2177
2178
2180{
2181 if( m_nodeStack.empty() )
2182 return false;
2183
2184 while( !m_nodeStack.back().m_locked && m_nodeStack.size() > 1 )
2185 m_nodeStack.pop_back();
2186
2187 m_currentNode = m_nodeStack.back().m_node;
2188
2189 return m_nodeStack.back().m_locked;
2190}
2191
2192
2194{
2195 auto iter = m_nodeStack.begin();
2196
2197 while( iter != m_nodeStack.end() )
2198 {
2199 if ( iter->m_node == aNode )
2200 {
2201 iter->m_locked = false;
2202 break;
2203 }
2204
2205 iter++;
2206 }
2207}
2208
2209
2211{
2212 m_optFlagDisableMask = aMask;
2213}
2214
2215
2217{
2219}
2220
2221
2223{
2224 m_defaultPolicy = aPolicy;
2225}
2226
2227
2228void SHOVE::SetShovePolicy( const LINKED_ITEM* aItem, int aPolicy )
2229{
2230 auto rl = touchRootLine( aItem );
2231 rl->policy = aPolicy;
2232}
2233
2234void SHOVE::SetShovePolicy( const LINE& aLine, int aPolicy )
2235{
2236 auto rl = touchRootLine( aLine );
2237 rl->policy = aPolicy;
2238}
2239
2240
2242{
2243 m_headLines.clear();
2244}
2245
2246
2247void SHOVE::AddHeads( const LINE& aHead, int aPolicy )
2248{
2249 m_headLines.push_back( SHOVE::HEAD_LINE_ENTRY( aHead, aPolicy ) );
2250}
2251
2252
2253void SHOVE::AddHeads( VIA_HANDLE aHead, VECTOR2I aNewPos, int aPolicy )
2254{
2255 SHOVE::HEAD_LINE_ENTRY ent( aHead, aPolicy );
2256 ent.viaNewPos = aNewPos;
2257 ent.prevVia = aHead;
2258 ent.theVia = aHead;
2259 m_headLines.push_back( std::move( ent ) );
2260}
2261
2262void removeHead( NODE *aNode, LINE& head )
2263{
2264 for (auto lnk : head.Links() )
2265 {
2266 if( lnk->BelongsTo( aNode ) )
2267 aNode->Remove( lnk );
2268 }
2269}
2270
2272{
2273 auto iface = Router()->GetInterface();
2274
2275 NODE::ITEM_VECTOR removed, added;
2276
2277 m_currentNode->GetUpdatedItems( removed, added );
2278
2279 for( auto& item : added )
2280 {
2281 auto rootEntry = findRootLine( static_cast<LINKED_ITEM*>( item ) );
2282 if( rootEntry && rootEntry->isHead )
2283 {
2284 m_currentNode->Remove( item );
2285 }
2286 }
2287}
2288
2289
2290void SHOVE::reconstructHeads( bool aShoveFailed )
2291{
2292 int i = 0;
2293 auto iface = Router()->GetInterface();
2294
2295 PNS_DBG( Dbg(), Message, wxString::Format("reconstructing %zu heads", m_headLines.size() ) );
2296
2297 for( auto& headEntry : m_headLines )
2298 {
2299 if( headEntry.origHead )
2300 {
2301 auto rootEntry = findRootLine( *headEntry.origHead );
2302
2303 PNS_DBG( Dbg(), Message, wxString::Format("orig LinkC=%d RE=%p", headEntry.origHead->LinkCount(), rootEntry ) );
2304
2305 assert( rootEntry );
2306 assert( rootEntry->rootLine );
2307
2308 if( rootEntry->newLine )
2309 {
2310 headEntry.newHead = rootEntry->newLine;
2311 headEntry.geometryModified = !rootEntry->newLine->CLine().CompareGeometry( rootEntry->rootLine->CLine() );
2312
2313 wxString msg = wxString::Format(
2314 "head %d/%d [net %-20s]: root %p, lc-root %d, lc-new %d\n", i, (int) m_headLines.size(),
2315 iface->GetNetName( rootEntry->rootLine->Net() ).c_str().AsChar(), rootEntry->rootLine, rootEntry->rootLine->LinkCount(), headEntry.newHead->LinkCount() );
2316 PNS_DBG( Dbg(), AddItem, rootEntry->rootLine, CYAN, 0, msg );
2317 PNS_DBG( Dbg(), Message, msg );
2318
2319 }
2320 else
2321 {
2322 wxString msg = wxString::Format(
2323 "head %d/%d [net %-20s]: unmodified, lc-orig %d\n", i, (int) m_headLines.size(),
2324 iface->GetNetName( headEntry.origHead->Net() ).c_str().AsChar(),
2325 headEntry.origHead->LinkCount() );
2326 PNS_DBG( Dbg(), Message, msg );
2327 }
2328
2329 i++;
2330 } else {
2331 auto rootEntry = findRootLine( headEntry.draggedVia );
2332
2333 if( rootEntry->newVia )
2334 {
2335 headEntry.geometryModified = true;
2336 headEntry.theVia = VIA_HANDLE( rootEntry->newVia->Pos(), rootEntry->newVia->Layers(), rootEntry->newVia->Net() );
2337 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2338 wxString msg = wxString::Format( "[modif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2339
2340 PNS_DBG( Dbg(), Message, msg );
2341 assert( chk != nullptr );
2342 }
2343 else
2344 {
2345 headEntry.theVia = VIA_HANDLE( rootEntry->oldVia->Pos(), rootEntry->oldVia->Layers(), rootEntry->oldVia->Net() );
2346 auto chk = m_currentNode->FindViaByHandle( *headEntry.theVia );
2347 wxString msg = wxString::Format( "[unmodif] via orig %p chk %p\n", headEntry.draggedVia, chk );
2348 PNS_DBG( Dbg(), Message, msg );
2349 assert( chk != nullptr );
2350
2351 }
2352
2353
2354 }
2355
2356 m_headsModified |= headEntry.geometryModified;
2357 }
2358}
2359
2360
2361
2363{
2364 //COLLISION_SEARCH_CONTEXT ctx;
2365
2366 //ctx.options.m_differentNetsOnly = false;
2367 //ctx.options.m_kindMask = ITEM::SEGMENT_T; // fixme arcs
2368
2369 SHAPE_LINE_CHAIN orig( aOld->CLine() );
2370
2371 int vc_prev = orig.PointCount();
2372 orig.Simplify2();
2373 int vc_post = orig.PointCount();
2374
2375 *aNew = *aOld;
2376
2377 PNS_DBG( Dbg(), Message, wxString::Format( "**** PreshoveCleanup %d -> %d\n", vc_prev, vc_post ) );
2378
2379 if( vc_prev != vc_post )
2380 {
2381 aNew->ClearLinks();
2382 aNew->SetShape( orig );
2383 replaceLine( *aOld, *aNew );
2384 return true;
2385 }
2386
2387 return false;
2388}
2389
2390// new algo
2392{
2393 SHOVE_STATUS st = SH_OK;
2394
2395 m_multiLineMode = false;
2396 int currentHeadId = 0;
2397 int totalHeads = m_headLines.size();
2398
2399 m_headsModified = false;
2400 m_lineStack.clear();
2401 m_optimizerQueue.clear();
2402
2403 ITEM_SET headSet;
2404
2405 PNS_DBG( Dbg(), Message, wxString::Format("shove run (heads: %d, currentNode=%p, depth=%d)", (int) m_headLines.size(), m_currentNode, m_currentNode->Depth() ) );
2406
2407 for( auto& l : m_headLines )
2408 {
2409 if( l.theVia )
2410 {
2411 PNS_DBG( Dbg(), Message, wxString::Format("process head-via [%d %d] node=%p", l.theVia->pos.x, l.theVia->pos.y, m_currentNode ) );
2412 auto realVia = m_currentNode->FindViaByHandle( *l.theVia );
2413 assert( realVia != nullptr );
2414 headSet.Add( realVia->Clone() );
2415 }
2416 else
2417 {
2418 headSet.Add( *l.origHead->Clone() );
2419 }
2420 }
2421
2422 // Pop NODEs containing previous shoves which are no longer necessary
2423 NODE* parent = reduceSpringback( headSet );
2424 m_currentNode = parent->Branch();
2425 m_currentNode->ClearRanks();
2426
2427
2428
2429
2430
2431
2432 //nodeStats( Dbg(), m_currentNode, "right-after-branch" );
2433
2434 auto iface = Router()->GetInterface();
2435
2436 // for ( auto& hq : m_headLines )
2437 // if( hq.oldHead )
2438 // m_currentNode->Remove( *hq.oldHead );
2439
2440
2441 // Push the via to its new location
2442 for( auto& headLineEntry : m_headLines )
2443 {
2444 //if( rootEntry->line ) // head already processed in previous steps
2445 //{
2446 // PNS_DBG( Dbg(), Message, wxString::Format( "RL found" ) );
2447
2448 //continue;
2449 //}
2450 m_currentNode->ClearRanks();
2451
2452 if( headLineEntry.theVia )
2453 {
2454 VIA* viaToDrag = m_currentNode->FindViaByHandle( *headLineEntry.theVia );
2455
2456 if( !viaToDrag )
2457 {
2458 st = SH_INCOMPLETE;
2459 break;
2460 }
2461
2462 auto viaRoot = touchRootLine( viaToDrag );
2463 viaRoot->oldVia = viaToDrag;
2464 headLineEntry.draggedVia = viaToDrag;
2465
2466 st = pushOrShoveVia( viaToDrag, ( headLineEntry.viaNewPos - viaToDrag->Pos() ), 0, true );
2467
2468 if( st != SH_OK )
2469 break;
2470 }
2471 else
2472 {
2473 // Create a new NODE to store this version of the world
2474 assert( headLineEntry.origHead->LinkCount() == 0 );
2475 m_currentNode->Add( *headLineEntry.origHead, true );
2476
2477 //nodeStats( Dbg(), m_currentNode, "add-head" );
2478
2479
2480
2481 PNS_DBG( Dbg(), Message,
2482 wxString::Format( "touchRoot ohlc %d roots %d re=%p\n",
2483 headLineEntry.origHead->LinkCount(),
2484 (int) m_rootLineHistory.size(),
2485 findRootLine( *headLineEntry.origHead ) ) );
2486
2487
2488 LINE head( *headLineEntry.origHead );
2489
2490 // empty head? nothing to shove...
2491 if( !head.SegmentCount() && !head.EndsWithVia() )
2492 {
2493 st = SH_INCOMPLETE;
2494 break;
2495 }
2496
2497 currentHeadId++;
2498
2499 if( !( headLineEntry.policy & SHP_DONT_LOCK_ENDPOINTS ) )
2500 {
2501 if( head.PointCount() > 0 )
2502 m_currentNode->LockJoint( head.CPoint( 0 ), &head, true );
2503
2504 if( !head.EndsWithVia() )
2505 m_currentNode->LockJoint( head.CLastPoint(), &head, true );
2506 }
2507
2508 SetShovePolicy( head, headLineEntry.policy );
2509
2510 //head.Mark( MK_HEAD );
2511 head.SetRank( 100000 ); //- 100 * currentHeadId );
2512
2513 if( head.EndsWithVia() )
2514 {
2515 auto headVia = Clone( head.Via() );
2516 headVia->SetRank( 100000 ); // - 100 * currentHeadId );
2517 headLineEntry.origHead->LinkVia( headVia.get() );
2518 head.LinkVia( headVia.get() );
2519 m_currentNode->Add( std::move( headVia ) );
2520 }
2521
2522 auto headRoot = touchRootLine( *headLineEntry.origHead );
2523 headRoot->isHead = true;
2524 headRoot->rootLine = new PNS::LINE( *headLineEntry.origHead );
2525 headRoot->policy = headLineEntry.policy;
2526 if( head.EndsWithVia() )
2527 {
2528 m_rootLineHistory[ headLineEntry.origHead->Via().Uid() ] = headRoot;
2529 }
2530
2531
2532 PNS_DBG( Dbg(), Message,
2533 wxString::Format( "headLC %d, rlLC %d oolc %d eov %d\n", head.LinkCount(),
2534 headRoot->rootLine->LinkCount(),
2535 headLineEntry.origHead->LinkCount(),
2536 head.EndsWithVia()?1:0 ) );
2537
2538 //auto rootEntry = findRootLine( &head );
2539
2540 PNS_DBG( Dbg(), Message,
2541 wxString::Format( "Shove heads %d/%d h-lc=%d net=%s Line=%d Policy=%s",
2542 currentHeadId, totalHeads, head.LinkCount(),
2543 iface->GetNetName( head.Net() ), headRoot->newLine ? 1 : 0,
2544 headRoot ? formatPolicy( headRoot->policy )
2545 : wxString( wxT( "default[ne]" ) ) ) );
2546
2547
2548 // nodeStats( Dbg(), m_currentNode, "pre-push-stack" );
2549
2550 if( !pushLineStack( head ) )
2551 {
2552 st = SH_INCOMPLETE;
2553 break;
2554 }
2555 }
2556
2557 st = shoveMainLoop();
2558
2559 //nodeStats( Dbg(), m_currentNode, "post-main-loop" );
2560
2561 if( st != SH_OK )
2562 break;
2563 };
2564
2565 PNS_DBG( Dbg(), Message,
2566 wxString::Format( "Shove status : %s after %d iterations, heads: %d",
2567 ( ( st == SH_OK || st == SH_HEAD_MODIFIED ) ? "OK" : "FAILURE" ),
2568 m_iter, (int) m_headLines.size() ) );
2569 if( st == SH_OK )
2570 {
2571 //nodeStats( Dbg(), m_currentNode, "pre-opt" );
2572
2574
2575 reconstructHeads( false );
2576 removeHeads();
2577
2578 // this must be called afrter reconstructHeads as it requires up-to-date via handles
2580 }
2581 else
2582 {
2583 //reconstructHeads( true );
2584
2585 for( auto& headEntry : m_headLines )
2586 {
2587 if( headEntry.prevVia )
2588 {
2589
2590 PNS_DBG( Dbg(), Message,
2591 wxString::Format( "Fail-restore via mod [%d, %d] orig [%d, %d]",
2592 headEntry.theVia->pos.x,
2593 headEntry.theVia->pos.y,
2594 headEntry.prevVia->pos.x,
2595 headEntry.prevVia->pos.y ) );
2596
2597 headEntry.theVia = headEntry.prevVia;
2598 headEntry.geometryModified = true;
2599 m_headsModified = true;
2600 }
2601 }
2602
2604
2605 delete m_currentNode;
2606 m_currentNode = parent;
2607 }
2608
2609 return st;
2610}
2611
2621
2622const wxString SHOVE::formatPolicy( int aPolicy )
2623{
2624 if( aPolicy == SHP_DEFAULT )
2625 return wxT( "default" );
2626
2627 wxString rv;
2628
2629 if( aPolicy & SHP_SHOVE )
2630 rv.Append( "shove ");
2631 if( aPolicy & SHP_WALK_FORWARD )
2632 rv.Append( "walk-forward ");
2633 if( aPolicy & SHP_WALK_FORWARD )
2634 rv.Append( "walk-back ");
2635 if( aPolicy & SHP_IGNORE )
2636 rv.Append( "ignore ");
2637 if( aPolicy & SHP_IGNORE )
2638 rv.Append( "dont-optimize ");
2639
2640 return rv;
2641}
2642
2643bool SHOVE::HeadsModified( int aIndex ) const
2644{
2645 if( aIndex < 0 )
2646 return m_headsModified;
2647 else
2648 return m_headLines[ aIndex ].geometryModified;
2649}
2650
2651const PNS::LINE SHOVE::GetModifiedHead( int aIndex ) const
2652{
2653 return *m_headLines[ aIndex ].newHead;
2654}
2655
2656const VIA_HANDLE SHOVE::GetModifiedHeadVia( int aIndex ) const
2657{
2658 return *m_headLines[ aIndex ].theVia;
2659}
2660
2661
2662
2663}
2664
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:926
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
CORNER_MODE
Corner modes.
Definition direction45.h:67
@ ROUNDED_45
H/V/45 with filleted corners.
Definition direction45.h:69
@ MITERED_45
H/V/45 with mitered corners (default)
Definition direction45.h:68
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:144
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:94
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:170
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:189
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:180
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:162
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:240
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:155
std::vector< ITEM * > ITEM_VECTOR
Definition pns_node.h:251
void Replace(ITEM *aOldItem, std::unique_ptr< ITEM > aNewItem)
Replace an item with another one.
Definition pns_node.cpp:899
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:440
std::optional< OBSTACLE > OPT_OBSTACLE
Definition pns_node.h:250
int Depth() const
Definition pns_node.h:290
void KillChildren()
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:939
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:232
TIME_LIMIT ShoveTimeLimit() const
PNS_OPTIMIZATION_EFFORT OptimizerEffort() const
Set the optimizer effort. Bigger means cleaner traces, but slower routing.
DIRECTION_45::CORNER_MODE GetCornerMode() const
const SEG & Seg() const
Definition pns_segment.h:93
int Width() const override
Definition pns_segment.h:88
const SHAPE_LINE_CHAIN Hull(int aClearance, int aWalkaroundThickness, int aLayer=-1) const override
Definition pns_line.cpp:615
void SetSpringbackDoNotTouchNode(const NODE *aNode)
SHOVE_STATUS shoveIteration(int aIter)
SHOVE_STATUS shoveMainLoop()
std::vector< SHAPE_LINE_CHAIN > HULL_SET
Definition pns_shove.h:111
void reconstructHeads(bool aShoveFailed)
std::pair< LINE, LINE > LINE_PAIR
Definition pns_shove.h:113
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:291
std::vector< LINE > m_lineStack
Definition pns_shove.h:272
void popLineStack()
ROOT_LINE_ENTRY * findRootLine(const LINE &aLine)
std::vector< SPRINGBACK_TAG > m_nodeStack
Definition pns_shove.h:271
@ SHP_DONT_OPTIMIZE
Definition pns_shove.h:65
@ SHP_DONT_LOCK_ENDPOINTS
Definition pns_shove.h:66
@ SHP_WALK_FORWARD
Definition pns_shove.h:62
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)
NODE * m_currentNode
Definition pns_shove.h:279
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:285
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:53
@ SH_HEAD_MODIFIED
Definition pns_shove.h:54
OPT_BOX2I totalAffectedArea() const
bool RewindToLastLockedNode()
std::deque< HEAD_LINE_ENTRY > m_headLines
Definition pns_shove.h:274
int m_defaultPolicy
Definition pns_shove.h:290
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:281
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:278
NODE * reduceSpringback(const ITEM_SET &aHeadSet)
bool AddLockedSpringbackNode(NODE *aNode)
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:286
std::vector< LINE > m_optimizerQueue
Definition pns_shove.h:273
SHOVE_STATUS onCollidingSolid(LINE &aCurrent, ITEM *aObstacle, OBSTACLE &aObstacleInfo)
bool pushSpringback(NODE *aNode, const OPT_BOX2I &aAffectedArea)
int m_optFlagDisableMask
Definition pns_shove.h:288
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:49
bool shoveLineFromLoneVia(const LINE &aCurLine, const LINE &aObstacleLine, LINE &aResultLine)
VIA * m_draggedVia
Definition pns_shove.h:282
bool m_headsModified
Definition pns_shove.h:284
const NODE * m_springbackDoNotTouchNode
Definition pns_shove.h:280
void ClearHeads()
std::unordered_map< LINKED_ITEM::UNIQ_ID, ROOT_LINE_ENTRY * > m_rootLineHistory
Definition pns_shove.h:276
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:80
std::vector< LINE_PAIR > LINE_PAIR_VEC
Definition pns_shove.h:114
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:226
void SetDiameter(int aLayer, int aDiameter)
Definition pns_via.h:233
const VECTOR2I & Pos() const
Definition pns_via.h:205
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition pns_via.h:301
const SHAPE_LINE_CHAIN Hull(int aClearance=0, int aWalkaroundThickness=0, int aLayer=-1) const override
Definition pns_via.cpp:235
VIA * Clone() const override
Return a deep copy of the item.
Definition pns_via.cpp:253
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:49
VECTOR2I B
Definition seg.h:50
static int DefaultAccuracyForPCB()
Definition shape_arc.h:283
A dynamic state checking if a point lies within polygon with a dynamically built outline ( with each ...
void AddPolyline(const SHAPE_LINE_CHAIN &aPolyline)
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Clear()
Remove all points from the line chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
const 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)
long long int Length() const
Return length of the line chain in Euclidean metric.
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:181
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:307
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:73
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
@ LIGHTGREEN
Definition color4d.h:63
@ WHITE
Definition color4d.h:48
@ BLUE
Definition color4d.h:56
@ LIGHTGRAY
Definition color4d.h:47
@ LIGHTYELLOW
Definition color4d.h:49
@ GREEN
Definition color4d.h:57
@ CYAN
Definition color4d.h:58
@ YELLOW
Definition color4d.h:67
@ LIGHTRED
Definition color4d.h:65
@ RED
Definition color4d.h:59
Push and Shove diff pair dimensions (gap) settings dialog.
@ 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:120
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:88
int m_maxFanoutWidth
worst case (largest) width of the tracks connected to the item
Definition pns_node.h:95
ITEM * m_item
Item found to be colliding with m_head.
Definition pns_node.h:90
Definition pns_shove.h:132
std::optional< VIA_HANDLE > theVia
Definition pns_shove.h:184
std::optional< VIA_HANDLE > prevVia
Definition pns_shove.h:183
VECTOR2I viaNewPos
Definition pns_shove.h:186
Definition pns_shove.h:117
int policy
Definition pns_shove.h:127
std::optional< LINE > newLine
Definition pns_shove.h:126
VIA * newVia
Definition pns_shove.h:125
std::vector< VIA_HANDLE > m_draggedVias
Definition pns_shove.h:202
std::set< ITEM * > m_items
VECTOR2I pos
Definition pns_via.h:54
NET_HANDLE net
Definition pns_via.h:56
PNS_LAYER_RANGE layers
Definition pns_via.h:55
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
std::string path
int clearance
VECTOR2I v2(1, 0)
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695