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