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