KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_line_placer.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2017 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 <optional>
23#include <memory>
24
25#include "pns_arc.h"
26#include "pns_debug_decorator.h"
27#include "pns_line_placer.h"
28#include "pns_node.h"
29#include "pns_router.h"
30#include "pns_shove.h"
31#include "pns_solid.h"
32#include "pns_topology.h"
33#include "pns_walkaround.h"
35
36#include <wx/log.h>
37
38namespace PNS {
39
41 PLACEMENT_ALGO( aRouter )
42{
44 m_world = nullptr;
45 m_shove = nullptr;
46 m_currentNode = nullptr;
47 m_idle = true;
48
49 // Init temporary variables (do not leave uninitialized members)
50 m_lastNode = nullptr;
51 m_placingVia = false;
52 m_currentNet = nullptr;
54 m_startItem = nullptr;
55 m_endItem = nullptr;
56 m_chainedPlacement = false;
57 m_orthoMode = false;
58 m_placementCorrect = false;
59}
60
61
63{
64}
65
66
68{
69 m_world = aWorld;
70}
71
72
74{
75 // fixme: should belong to KICAD_IFACE
76 auto iface = Router()->GetInterface();
77
78 int start = m_sizes.ViaType() == VIATYPE::THROUGH ?iface->GetPNSLayerFromBoardLayer( F_Cu ) : m_sizes.GetLayerTop();
79 int end = m_sizes.ViaType() == VIATYPE::THROUGH ? iface->GetPNSLayerFromBoardLayer( B_Cu ) : m_sizes.GetLayerBottom();
80
81 const PNS_LAYER_RANGE layers(
82 start ,
83 end
84 );
85
86 return VIA( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), nullptr, m_sizes.ViaType() );
87}
88
89
90bool LINE_PLACER::ToggleVia( bool aEnabled )
91{
92 m_placingVia = aEnabled;
93
94 if( !aEnabled )
96
97 return true;
98}
99
100
102{
103 m_initial_direction = aDirection;
104
105 if( m_tail.SegmentCount() == 0 )
106 m_direction = aDirection;
107}
108
109
111{
113 SHAPE_LINE_CHAIN& head = m_head.Line();
114 SHAPE_LINE_CHAIN& tail = m_tail.Line();
115
116 // if there is no tail, there is nothing to intersect with
117 if( tail.PointCount() < 2 )
118 return false;
119
120 if( head.PointCount() < 2 )
121 return false;
122
123 // completely new head trace? chop off the tail
124 if( tail.CPoint(0) == head.CPoint(0) )
125 {
127 tail.Clear();
128 return true;
129 }
130
131 tail.Intersect( head, ips );
132
133 // no intesection points - nothing to reduce
134 if( ips.empty() )
135 return false;
136
137 int n = INT_MAX;
138 VECTOR2I ipoint;
139
140 // if there is more than one intersection, find the one that is
141 // closest to the beginning of the tail.
142 for( const SHAPE_LINE_CHAIN::INTERSECTION& i : ips )
143 {
144 if( i.index_our < n )
145 {
146 n = i.index_our;
147 ipoint = i.p;
148 }
149 }
150
151 // ignore the point where head and tail meet
152 if( ipoint == head.CPoint( 0 ) || ipoint == tail.CPoint( -1 ) )
153 return false;
154
155 // Intersection point is on the first or the second segment: just start routing
156 // from the beginning
157 if( n < 2 )
158 {
160 tail.Clear();
161 head.Clear();
162
163 return true;
164 }
165 else
166 {
167 // Clip till the last tail segment before intersection.
168 // Set the direction to the one of this segment.
169 const SEG last = tail.CSegment( n - 1 );
170 m_direction = DIRECTION_45( last );
171 tail.Remove( n, -1 );
172 return true;
173 }
174
175 return false;
176}
177
178
180{
181 SHAPE_LINE_CHAIN& head = m_head.Line();
182 SHAPE_LINE_CHAIN& tail = m_tail.Line();
183
184 if( head.PointCount() < 2 )
185 return false;
186
187 int n = tail.PointCount();
188
189 if( n == 0 )
190 {
191 return false;
192 }
193 else if( n == 1 )
194 {
195 tail.Clear();
196 return true;
197 }
198
199 DIRECTION_45 first_head, last_tail;
200
201 wxASSERT( tail.PointCount() >= 2 );
202
203 if( !head.IsPtOnArc( 0 ) )
204 first_head = DIRECTION_45( head.CSegment( 0 ) );
205 else
206 first_head = DIRECTION_45( head.CArcs()[head.ArcIndex(0)] );
207
208 int lastSegIdx = tail.PointCount() - 2;
209
210 if( !tail.IsPtOnArc( lastSegIdx ) )
211 last_tail = DIRECTION_45( tail.CSegment( lastSegIdx ) );
212 else
213 last_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex(lastSegIdx)] );
214
215 DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
216
217 // case 1: we have a defined routing direction, and the currently computed
218 // head goes in different one.
219 bool pullback_1 = false; // (m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
220
221 // case 2: regardless of the current routing direction, if the tail/head
222 // extremities form an acute or right angle, reduce the tail by one segment
223 // (and hope that further iterations) will result with a cleaner trace
224 bool pullback_2 = ( angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE );
225
226 if( pullback_1 || pullback_2 )
227 {
228 if( !tail.IsArcSegment( lastSegIdx ) )
229 {
230 const SEG& seg = tail.CSegment( lastSegIdx );
231 m_direction = DIRECTION_45( seg );
232 PNS_DBG( Dbg(), AddPoint, m_p_start, WHITE, 10000, wxT( "new-pstart [pullback3]" ) );
233
234 }
235 else
236 {
237 const SHAPE_ARC& arc = tail.CArcs()[tail.ArcIndex( lastSegIdx )];
238 m_direction = DIRECTION_45( arc );
239 }
240
241 PNS_DBG( Dbg(), Message, wxString::Format( "Placer: pullback triggered [%d] [%s %s]",
242 n, last_tail.Format(), first_head.Format() ) );
243
244 // erase the last point in the tail, hoping that the next iteration will
245 // result with a head trace that starts with a segment following our
246 // current direction.
247 if( n < 2 )
248 tail.Clear(); // don't leave a single-point tail
249 else
250 tail.RemoveShape( -1 );
251
252 if( !tail.SegmentCount() )
254
255 return true;
256 }
257
258 return false;
259}
260
261
263{
264 SHAPE_LINE_CHAIN& head = m_head.Line();
265 SHAPE_LINE_CHAIN& tail = m_tail.Line();
266
267 int n = tail.SegmentCount();
268
269 if( head.SegmentCount() < 1 )
270 return false;
271
272 // Don't attempt this for too short tails
273 if( n < 2 )
274 return false;
275
276 // Start from the segment farthest from the end of the tail
277 // int start_index = std::max(n - 1 - ReductionDepth, 0);
278
279 DIRECTION_45 new_direction;
280 VECTOR2I new_start;
281 int reduce_index = -1;
282
283 for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
284 {
285 const SEG s = tail.CSegment( i );
286 DIRECTION_45 dir( s );
287
288 // calculate a replacement route and check if it matches
289 // the direction of the segment to be replaced
290 SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace( s.A, aEnd );
291
292 if( replacement.SegmentCount() < 1 )
293 continue;
294
295 LINE tmp( m_tail, replacement );
296
298 break;
299
300 if( DIRECTION_45( replacement.CSegment( 0 ) ) == dir )
301 {
302 new_start = s.A;
303 new_direction = dir;
304 reduce_index = i;
305 }
306 }
307
308 if( reduce_index >= 0 )
309 {
310 PNS_DBG( Dbg(), Message, wxString::Format( "Placer: reducing tail: %d" , reduce_index ) );
311 SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace( new_start, aEnd );
312
313 m_direction = new_direction;
314 tail.Remove( reduce_index + 1, -1 );
315 head.Clear();
316 return true;
317 }
318
319 if( !tail.SegmentCount() )
321
322 return false;
323}
324
325
327{
328 SHAPE_LINE_CHAIN& head = m_head.Line();
329 SHAPE_LINE_CHAIN& tail = m_tail.Line();
330
331 const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE
334
335 head.Simplify();
336 tail.Simplify();
337
338 int n_head = head.ShapeCount();
339 int n_tail = tail.ShapeCount();
340
341 if( n_head < 3 )
342 {
343 PNS_DBG( Dbg(), Message, wxT( "Merge failed: not enough head segs." ) );
344 return false;
345 }
346
347 if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
348 {
349 PNS_DBG( Dbg(), Message, wxT( "Merge failed: head and tail discontinuous." ) );
350 return false;
351 }
352
353 if( m_head.CountCorners( ForbiddenAngles ) != 0 )
354 return false;
355
356 DIRECTION_45 dir_tail, dir_head;
357
358 if( !head.IsPtOnArc( 0 ) )
359 dir_head = DIRECTION_45( head.CSegment( 0 ) );
360 else
361 dir_head = DIRECTION_45( head.CArcs()[head.ArcIndex( 0 )] );
362
363 if( n_tail )
364 {
365 wxASSERT( tail.PointCount() >= 2 );
366 int lastSegIdx = tail.PointCount() - 2;
367
368 if( !tail.IsPtOnArc( lastSegIdx ) )
369 dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
370 else
371 dir_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
372
373 if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
374 return false;
375 }
376
377 tail.Append( head );
378
379 tail.Simplify();
380
381 int lastSegIdx = tail.PointCount() - 2;
382
383 if( !tail.IsArcSegment( lastSegIdx ) )
384 m_direction = DIRECTION_45( tail.CSegment( -1 ) );
385 else
386 m_direction = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
387
388 head.Remove( 0, -1 );
389
390 PNS_DBG( Dbg(), Message, wxString::Format( "Placer: merge %d, new direction: %s" , n_head,
391 m_direction.Format() ) );
392
393 head.Simplify();
394 tail.Simplify();
395
396 return true;
397}
398
399
401 SHAPE_LINE_CHAIN& aOut, int &thresholdDist )
402{
403 SHAPE_LINE_CHAIN l( aL );
404 int idx = l.Split( aP );
405
406 if( idx < 0)
407 return false;
408
409 bool rv = true;
410
411 SHAPE_LINE_CHAIN l2 = l.Slice( 0, idx );
412 int dist = l2.Length();
413
414 PNS_DBG( Dbg(), AddPoint, aP, BLUE, 500000, wxString::Format( "hug-target-check-%d", idx ) );
415 PNS_DBG( Dbg(), AddShape, &l2, BLUE, 500000, wxT( "hug-target-line" ) );
416
417 if( dist < thresholdDist )
418 rv = false;
419
420 LINE ctest( m_head, l2 );
421
422 if( m_currentNode->CheckColliding( &ctest ).has_value() )
423 rv = false;
424
425 if( rv )
426 {
427 aOut = l2;
428 thresholdDist = dist;
429 }
430
431 return rv;
432}
433
434
436 double lengthThreshold, SHAPE_LINE_CHAIN &aOut )
437{
438 std::vector<int> dists;
439 std::vector<VECTOR2I> pts;
440
441 if( aL.PointCount() == 0 )
442 return false;
443
444 VECTOR2I lastP = aL.CPoint(-1);
445 int accumulatedDist = 0;
446
447 dists.reserve( 2 * aL.PointCount() );
448
449 for( int i = 0; i < aL.SegmentCount(); i++ )
450 {
451 const SEG& s = aL.CSegment( i );
452
453 dists.push_back( ( aCursor - s.A ).EuclideanNorm() );
454 pts.push_back( s.A );
455 auto pn = s.NearestPoint( aCursor );
456
457 if( pn != s.A && pn != s.B )
458 {
459 dists.push_back( ( pn - aCursor ).EuclideanNorm() );
460 pts.push_back( pn );
461 }
462
463 accumulatedDist += s.Length();
464
465 if ( accumulatedDist > lengthThreshold )
466 {
467 lastP = s.B;
468 break;
469 }
470 }
471
472 dists.push_back( ( aCursor - lastP ).EuclideanNorm() );
473 pts.push_back( lastP );
474
475 int minDistLoc = std::numeric_limits<int>::max();
476 int minPLoc = -1;
477 int minDistGlob = std::numeric_limits<int>::max();
478 int minPGlob = -1;
479
480 for( int i = 0; i < dists.size(); i++ )
481 {
482 int d = dists[i];
483
484 if( d < minDistGlob )
485 {
486 minDistGlob = d;
487 minPGlob = i;
488 }
489 }
490
491 if( dists.size() >= 3 )
492 {
493 for( int i = 0; i < dists.size() - 3; i++ )
494 {
495 if( dists[i + 2] > dists[i + 1] && dists[i] > dists[i + 1] )
496 {
497 int d = dists[i + 1];
498 if( d < minDistLoc )
499 {
500 minDistLoc = d;
501 minPLoc = i + 1;
502 }
503 }
504 }
505
506 if( dists.back() < minDistLoc && minPLoc >= 0 )
507 {
508 minDistLoc = dists.back();
509 minPLoc = dists.size() - 1;
510 }
511 }
512 else
513 {
514 // Too few points: just use the global
515 minDistLoc = minDistGlob;
516 minPLoc = minPGlob;
517 }
518
519// fixme: I didn't make my mind yet if local or global minimum feels better. I'm leaving both
520// in the code, enabling the global one by default
521 minPLoc = -1;
522 int preferred;
523
524 if( minPLoc < 0 )
525 {
526 preferred = minPGlob;
527 }
528 else
529 {
530 preferred = minPLoc;
531 }
532
533 int thresholdDist = 0;
534
535 if( clipAndCheckCollisions( pts[preferred], aL, aOut, thresholdDist ) )
536 return true;
537
538 thresholdDist = 0;
539
540 SHAPE_LINE_CHAIN l( aL ), prefL;
541 int minDist = std::numeric_limits<int>::max();
542
543 bool ok = false;
544
545 for( int i = 0; i < pts.size() ; i++)
546 {
547 //PNS_DBG( Dbg(), AddPoint, pts[i], BLUE, 500000, wxT( "hug-target-fallback" ) );
548
549 ok |= clipAndCheckCollisions( pts[i], aL, aOut, thresholdDist );
550 }
551
552 return ok;
553}
554
555
556bool LINE_PLACER::rhWalkBase( const VECTOR2I& aP, LINE& aWalkLine, int aCollisionMask, PNS::PNS_MODE aMode,
557 bool& aViaOk )
558{
559 LINE walkFull( m_head );
560 LINE l1( m_head );
561
562 PNS_DBG( Dbg(), AddItem, &m_tail, GREEN, 100000, wxT( "walk-base-old-tail" ) );
563 PNS_DBG( Dbg(), AddItem, &m_head, BLUE, 100000, wxT( "walk-base-old-head" ) );
564
565 VECTOR2I walkP = aP;
566
567 WALKAROUND walkaround( m_currentNode, Router() );
568
569 walkaround.SetSolidsOnly( false );
570 walkaround.SetDebugDecorator( Dbg() );
571 walkaround.SetLogger( Logger() );
572 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
573 walkaround.SetItemMask( aCollisionMask );
575
576 int round = 0;
577
578 do
579 {
580 l1.Clear();
581
582 PNS_DBG( Dbg(), BeginGroup, wxString::Format( "walk-round-%d", round ), 0 );
583 round++;
584
585 aViaOk = buildInitialLine( walkP, l1, aMode, round == 0 );
586 PNS_DBG( Dbg(), AddItem, &l1, BLUE, 20000, wxT( "walk-base-l1" ) );
587
588 if( l1.EndsWithVia() )
589 PNS_DBG( Dbg(), AddPoint, l1.Via().Pos(), BLUE, 100000, wxT( "walk-base-l1-via" ) );
590
591 LINE initTrack( m_tail );
592 initTrack.Line().Append( l1.CLine() );
593 initTrack.Line().Simplify();
594
595
596 double initialLength = initTrack.CLine().Length();
597 double hugThresholdLength = initialLength * Settings().WalkaroundHugLengthThreshold();
598 double hugThresholdLengthComplete =
599 2.0 * initialLength * Settings().WalkaroundHugLengthThreshold();
600
601 WALKAROUND::RESULT wr = walkaround.Route( initTrack );
602 std::optional<LINE> bestLine;
603
604 OPTIMIZER optimizer( m_currentNode );
605
607 optimizer.SetCollisionMask( aCollisionMask );
608
609 using WALKAROUND::WP_CW;
610 using WALKAROUND::WP_CCW;
611
612 int len_cw = wr.status[WP_CW] != WALKAROUND::ST_STUCK ? wr.lines[WP_CW].CLine().Length()
613 : std::numeric_limits<int>::max();
614 int len_ccw = wr.status[WP_CCW] != WALKAROUND::ST_STUCK ? wr.lines[WP_CCW].CLine().Length()
615 : std::numeric_limits<int>::max();
616
617
618 if( wr.status[ WP_CW ] == WALKAROUND::ST_DONE )
619 {
620 PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CW], BLUE, 20000, wxT( "wf-result-cw-preopt" ) );
621 LINE tmpHead, tmpTail;
622
623
625
626 if( splitHeadTail( wr.lines[WP_CW], m_tail, tmpHead, tmpTail ) )
627 {
628 optimizer.Optimize( &tmpHead );
629 wr.lines[WP_CW].SetShape( tmpTail.CLine () );
630 wr.lines[WP_CW].Line().Append( tmpHead.CLine( ) );
631 }
632
633 PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CW], RED, 20000, wxT( "wf-result-cw-postopt" ) );
634 len_cw = wr.lines[WP_CW].CLine().Length();
635 bestLine = wr.lines[WP_CW];
636 }
637
638 if( wr.status[WP_CCW] == WALKAROUND::ST_DONE )
639 {
640 PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CCW], BLUE, 20000, wxT( "wf-result-ccw-preopt" ) );
641
642 LINE tmpHead, tmpTail;
643
645
646 if( splitHeadTail( wr.lines[WP_CCW], m_tail, tmpHead, tmpTail ) )
647 {
648 optimizer.Optimize( &tmpHead );
649 wr.lines[WP_CCW].SetShape( tmpTail.CLine () );
650 wr.lines[WP_CCW].Line().Append( tmpHead.CLine( ) );
651 }
652
653 PNS_DBG( Dbg(), AddItem, &wr.lines[WP_CCW], RED, 20000, wxT( "wf-result-ccw-postopt" ) );
654 len_ccw = wr.lines[WP_CCW].CLine().Length();
655
656 if( len_ccw < len_cw )
657 bestLine = wr.lines[WP_CCW];
658 }
659
660 int bestLength = len_cw < len_ccw ? len_cw : len_ccw;
661
662 if( bestLength < hugThresholdLengthComplete && bestLine.has_value() )
663 {
664 walkFull.SetShape( bestLine->CLine() );
665 walkP = walkFull.CLine().CPoint(-1);
666 PNS_DBGN( Dbg(), EndGroup );
667 continue;
668 }
669
670 bool validCw = false;
671 bool validCcw = false;
672 int distCcw = std::numeric_limits<int>::max();
673 int distCw = std::numeric_limits<int>::max();
674
675 SHAPE_LINE_CHAIN l_cw, l_ccw;
676
677
678 if( wr.status[WP_CW] != WALKAROUND::ST_STUCK )
679 {
680 validCw = cursorDistMinimum( wr.lines[WP_CW].CLine(), aP, hugThresholdLength, l_cw );
681
682 if( validCw )
683 distCw = ( aP - l_cw.CPoint( -1 ) ).EuclideanNorm();
684
685 PNS_DBG( Dbg(), AddShape, &l_cw, MAGENTA, 200000, wxString::Format( "wh-result-cw %s",
686 validCw ? "non-colliding"
687 : "colliding" ) );
688 }
689
690 if( wr.status[WP_CCW] != WALKAROUND::ST_STUCK )
691 {
692 validCcw = cursorDistMinimum( wr.lines[WP_CCW].CLine(), aP, hugThresholdLength, l_ccw );
693
694 if( validCcw )
695 distCcw = ( aP - l_ccw.CPoint( -1 ) ).EuclideanNorm();
696
697 PNS_DBG( Dbg(), AddShape, &l_ccw, MAGENTA, 200000, wxString::Format( "wh-result-ccw %s",
698 validCcw ? "non-colliding"
699 : "colliding" ) );
700 }
701
702
703 if( distCw < distCcw && validCw )
704 {
705 walkFull.SetShape( l_cw );
706 walkP = l_cw.CPoint(-1);
707 }
708 else if( validCcw )
709 {
710 walkFull.SetShape( l_ccw );
711 walkP = l_ccw.CPoint(-1);
712 }
713 else
714 {
715 PNS_DBGN( Dbg(), EndGroup );
716 return false;
717 }
718
719 PNS_DBGN( Dbg(), EndGroup );
720 } while( round < 2 && m_placingVia );
721
722
723 if( l1.EndsWithVia() )
724 {
725 VIA v ( l1.Via() );
726 v.SetPos( walkFull.CPoint( -1 ) );
727 walkFull.AppendVia( v );
728 }
729
730 PNS_DBG( Dbg(), AddItem, &walkFull, GREEN, 200000, wxT( "walk-full" ) );
731
732 if( walkFull.EndsWithVia() )
733 {
734 PNS_DBG( Dbg(), AddPoint, walkFull.Via().Pos(), GREEN, 200000,
735 wxString::Format( "walk-via ok %d", aViaOk ? 1 : 0 ) );
736 }
737
738 aWalkLine = walkFull;
739
740 return !walkFull.EndsWithVia() || aViaOk;
741}
742
743
744bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
745{
746 LINE walkFull;
747
748 int effort = 0;
749 bool viaOk = false;
750
751 if( ! rhWalkBase( aP, walkFull, ITEM::ANY_T, RM_Walkaround, viaOk ) )
752 return false;
753
754 switch( Settings().OptimizerEffort() )
755 {
756 case OE_LOW:
757 effort = 0;
758 break;
759
760 case OE_MEDIUM:
761 case OE_FULL:
763 break;
764 }
765
767
768 // Smart Pads is incompatible with 90-degree mode for now
769 if( Settings().SmartPads()
770 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
772 {
773 effort |= OPTIMIZER::SMART_PADS;
774 }
775
776 if( m_currentNode->CheckColliding( &walkFull ) )
777 {
778 PNS_DBG( Dbg(), AddItem, &walkFull, GREEN, 100000, wxString::Format( "collision check fail" ) );
779 return false;
780 }
781
782 // OK, this deserves a bit of explanation. We used to calculate the walk path for the head only,
783 // but then the clearance epsilon was added, with the intent of improving collision resolution robustness
784 // (now a hull or a walk/shove line cannot collide with the 'owner' of the hull under any circumstances).
785 // This, however, introduced a subtle bug. For a row/column/any other 'regular' arrangement
786 // of overlapping hulls (think of pads of a SOP/SOIC chip or a regular via grid), walking around may
787 // produce a new 'head' that is not considered colliding (due to the clearance epsilon), but with
788 // its start point inside one of the subsequent hulls to process.
789 // We can't have head[0] inside any hull for the algorithm to work - therefore, we now consider the entire
790 // 'tail+head' trace when walking around and in case of success, reconstruct the
791 // 'head' and 'tail' by splitting the walk line at a point that is as close as possible to the original
792 // head[0], but not inside any obstacle hull.
793 //
794 // EXECUTIVE SUMMARY: asinine heuristic to make the router get stuck much less often.
795
796 if( ! splitHeadTail( walkFull, m_tail, aNewHead, aNewTail ) )
797 return false;
798
799 if( m_placingVia && viaOk )
800 {
801 PNS_DBG( Dbg(), AddPoint, aNewHead.CPoint(-1), RED, 1000000, wxString::Format( "VIA" ) );
802
803 aNewHead.AppendVia( makeVia( aNewHead.CPoint( -1 ) ) );
804 }
805
806 OPTIMIZER::Optimize( &aNewHead, effort, m_currentNode );
807
808 PNS_DBG( Dbg(), AddItem, &aNewHead, GREEN, 100000, wxString::Format( "walk-new-head" ) );
809 PNS_DBG( Dbg(), AddItem, &aNewTail, BLUE, 100000, wxT( "walk-new-tail" ) );
810
811 return true;
812}
813
814
815bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
816{
818 m_head.SetBlockingObstacle( nullptr );
819
820 auto obs = m_currentNode->NearestObstacle( &m_head );
821
822 // If the head is in colliding state, snap to the hull of the first obstacle.
823 // This way, one can route tracks as tightly as possible without enabling
824 // the shove/walk mode that certain users find too intrusive.
825 if( obs )
826 {
827 int clearance = m_currentNode->GetClearance( obs->m_item, &m_head, false );
828 SHAPE_LINE_CHAIN hull = obs->m_item->Hull( clearance, m_head.Width(), m_head.Layer() );
829 VECTOR2I nearest;
830
832
833 if( cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90 )
834 nearest = hull.BBox().NearestPoint( aP );
835 else
836 nearest = hull.NearestPoint( aP );
837
838 if( ( nearest - aP ).EuclideanNorm() < m_head.Width() / 2 )
840 }
841
842 // Note: Something like the below could be used to implement a "stop at first obstacle" mode,
843 // but we don't have one right now and there isn't a lot of demand for one. If we do end up
844 // doing that, put it in a new routing mode as "highlight collisions" mode should not have
845 // collision handling other than highlighting.
846#if 0
847 if( !Settings().AllowDRCViolations() )
848 {
850
851 if( obs && obs->m_distFirst != INT_MAX )
852 {
853 buildInitialLine( obs->m_ipFirst, m_head );
854 m_head.SetBlockingObstacle( obs->m_item );
855 }
856 }
857#endif
858
859 aNewHead = m_head;
860 aNewTail = m_tail;
861
862 return true;
863}
864
865
866bool LINE_PLACER::splitHeadTail( const LINE& aNewLine, const LINE& aOldTail, LINE& aNewHead,
867 LINE& aNewTail )
868{
869 LINE newTail( aOldTail );
870 LINE newHead( aOldTail );
871 LINE l2( aNewLine );
872
873 newTail.RemoveVia();
874 newHead.Clear();
875
876 int i;
877 bool found = false;
878 int n = l2.PointCount();
879
880 if( n > 1 && aOldTail.PointCount() > 1 )
881 {
882 if( l2.CLine().PointOnEdge( aOldTail.CPoint( -1 ) ) )
883 {
884 l2.Line().Split( aOldTail.CPoint( -1 ) );
885 }
886
887 for( i = 0; i < aOldTail.PointCount(); i++ )
888 {
889 if( l2.CLine().Find( aOldTail.CPoint( i ) ) < 0 )
890 {
891 found = true;
892 break;
893 }
894 }
895
896 if( !found )
897 i--;
898
899 // If the old tail doesn't have any points of the new line, we can't split it.
900 if( i >= l2.PointCount() )
901 i = l2.PointCount() - 1;
902
903 newHead.Clear();
904
905 if( i == 0 )
906 newTail.Clear();
907 else
908 newTail.SetShape( l2.CLine().Slice( 0, i ) );
909
910 newHead.SetShape( l2.CLine().Slice( i, -1 ) );
911 }
912 else
913 {
914 newTail.Clear();
915 newHead = l2;
916 }
917
918 PNS_DBG( Dbg(), AddItem, &newHead, BLUE, 500000, wxT( "head-post-split" ) );
919
920 aNewHead = newHead;
921 aNewTail = newTail;
922
923 return true;
924}
925
926
927bool LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
928{
929 LINE walkSolids;
930
931 bool viaOk = false;
932
933 if( ! rhWalkBase( aP, walkSolids, ITEM::SOLID_T, RM_Shove, viaOk ) )
934 return false;
935
936 m_currentNode = m_shove->CurrentNode();
937
938 m_shove->SetLogger( Logger() );
939 m_shove->SetDebugDecorator( Dbg() );
940
941 if( m_endItem )
942 {
943 // Make sure the springback algorithm won't erase the NODE that owns m_endItem.
944 m_shove->SetSpringbackDoNotTouchNode( static_cast<const NODE*>( m_endItem->Owner() ) );
945 }
946
947 LINE newHead( walkSolids );
948
949 if( walkSolids.EndsWithVia() )
950 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), RED, 1000000, wxString::Format( "SVIA [%d]", viaOk?1:0 ) );
951
952 if( m_placingVia && viaOk )
953 {
954 newHead.AppendVia( makeVia( newHead.CPoint( -1 ) ) );
955 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-new-via" );
956
957 }
958
959 m_shove->ClearHeads();
960 m_shove->AddHeads( newHead, SHOVE::SHP_SHOVE );
961 bool shoveOk = m_shove->Run() == SHOVE::SH_OK;
962
963 m_currentNode = m_shove->CurrentNode();
964
965 int effort = 0;
966
967 switch( Settings().OptimizerEffort() )
968 {
969 case OE_LOW:
970 effort = 0;
971 break;
972
973 case OE_MEDIUM:
974 case OE_FULL:
976 break;
977 }
978
980
981 // Smart Pads is incompatible with 90-degree mode for now
982 if( Settings().SmartPads()
983 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
985 {
986 effort |= OPTIMIZER::SMART_PADS;
987 }
988
989 if( shoveOk )
990 {
991 if( m_shove->HeadsModified() )
992 newHead = m_shove->GetModifiedHead( 0 );
993
994 if( newHead.EndsWithVia() )
995 {
996 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-preopt" );
997 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-postopt" );
998 }
999
1000 if( ! splitHeadTail( newHead, m_tail, aNewHead, aNewTail ) )
1001 return false;
1002
1003 if( newHead.EndsWithVia() )
1004 aNewHead.AppendVia( newHead.Via() );
1005
1006 OPTIMIZER::Optimize( &aNewHead, effort, m_currentNode );
1007 PNS_DBG( Dbg(), AddItem, aNewHead.Clone(), GREEN, 1000000, "head-sh-postopt" );
1008
1009 return true;
1010 }
1011 else
1012 {
1013 return rhWalkOnly( aP, aNewHead, aNewTail );
1014 }
1015
1016 return false;
1017}
1018
1019
1020bool LINE_PLACER::routeHead( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
1021{
1022 switch( Settings().Mode() )
1023 {
1024 case RM_MarkObstacles:
1025 return rhMarkObstacles( aP, aNewHead, aNewTail );
1026 case RM_Walkaround:
1027 return rhWalkOnly( aP, aNewHead, aNewTail );
1028 case RM_Shove:
1029 return rhShoveOnly( aP, aNewHead, aNewTail );
1030 default:
1031 break;
1032 }
1033
1034 return false;
1035}
1036
1037
1039{
1040 LINE linetmp = Trace();
1041
1042 PNS_DBG( Dbg(), Message, "optimize HT" );
1043
1044 // NOTE: FANOUT_CLEANUP can override posture setting at the moment
1047 {
1048 if( linetmp.SegmentCount() < 1 )
1049 return false;
1050
1051 m_head = linetmp;
1052 m_direction = DIRECTION_45( linetmp.CSegment( 0 ) );
1053 m_tail.Line().Clear();
1054
1055 return true;
1056 }
1057
1058 SHAPE_LINE_CHAIN& head = m_head.Line();
1059 SHAPE_LINE_CHAIN& tail = m_tail.Line();
1060
1061 int tailLookbackSegments = 3;
1062
1063 //if(m_currentMode() == RM_Walkaround)
1064 // tailLookbackSegments = 10000;
1065
1066 int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
1067
1068 if( tail.ShapeCount() < 3 )
1069 return false;
1070
1071 // assemble TailLookbackSegments tail segments with the current head
1072 SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
1073
1074 int end = std::min(2, head.PointCount() - 1 );
1075
1076 opt_line.Append( head.Slice( 0, end ) );
1077
1078 LINE new_head( m_tail, opt_line );
1079
1080 // and see if it could be made simpler by merging obtuse/collnear segments.
1081 // If so, replace the (threshold) last tail points and the head with
1082 // the optimized line
1083
1084 PNS_DBG( Dbg(), AddItem, &new_head, LIGHTCYAN, 10000, wxT( "ht-newline" ) );
1085
1087 {
1088 LINE tmp( m_tail, opt_line );
1089
1090 head.Clear();
1091 tail.Replace( -threshold, -1, new_head.CLine() );
1092 tail.Simplify();
1093
1094 m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
1095
1096 return true;
1097 }
1098
1099 return false;
1100}
1101
1103{
1104 if( tail.CLine().PointCount() )
1105 m_p_start = tail.CLine().CPoint(-1);
1106 else
1108}
1109
1111{
1112 bool fail = false;
1113 bool go_back = false;
1114
1115 int i, n_iter = 1;
1116
1117
1118 PNS_DBG( Dbg(), Message, wxString::Format( "routeStep: direction: %s head: %d, tail: %d shapes" ,
1121 m_tail.ShapeCount() ) );
1122
1123 PNS_DBG( Dbg(), BeginGroup, wxT( "route-step" ), 0 );
1124
1125 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-init" ) );
1126 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-init" ) );
1127
1128 for( i = 0; i < n_iter; i++ )
1129 {
1130 LINE prevTail( m_tail );
1131 LINE prevHead( m_head );
1132 LINE newHead, newTail;
1133
1134 if( !go_back && Settings().FollowMouse() )
1135 reduceTail( aP );
1136
1137 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-reduce" ) );
1138 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-reduce" ) );
1139
1140 go_back = false;
1141
1143
1144 if( !routeHead( aP, newHead, newTail ) )
1145 {
1146 m_tail = prevTail;
1147 m_head = prevHead;
1148
1149 // If we fail to walk out of the initial point (no tail), instead of returning an empty
1150 // line, return a zero-length line so that the user gets some feedback that routing is
1151 // happening. This will get pruned later.
1152 if( m_tail.PointCount() == 0 )
1153 {
1155 m_tail.Line().Append( m_p_start, true );
1156 }
1157
1158 fail = true;
1159 }
1160
1162
1163 PNS_DBG( Dbg(), AddItem, &newHead, LIGHTGREEN, 100000, wxString::Format( "new_head [fail: %d]", fail?1:0 ) );
1164
1165 if( fail )
1166 break;
1167
1168 PNS_DBG( Dbg(), Message, wxString::Format( "N VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1169
1170 m_head = newHead;
1171 m_tail = newTail;
1172
1174 {
1175 n_iter++;
1176 go_back = true;
1177 }
1178
1179 PNS_DBG( Dbg(), Message, wxString::Format( "SI VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1180
1181 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-si" ) );
1182 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-si" ) );
1183
1184 if( !go_back && handlePullback() )
1185 {
1186 n_iter++;
1187 m_head.Clear();
1188 go_back = true;
1189 }
1190
1191 PNS_DBG( Dbg(), Message, wxString::Format( "PB VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1192
1193 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-after-pb" ) );
1194 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-after-pb" ) );
1195 }
1196
1197
1198 if( !fail && Settings().FollowMouse() )
1199 {
1200 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-pre-merge" ) );
1201 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-pre-merge" ) );
1202
1204 {
1205 PNS_DBG( Dbg(), Message, wxString::Format( "PreM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1206
1207 mergeHead();
1208
1209 PNS_DBG( Dbg(), Message, wxString::Format( "PostM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1210 }
1211
1212 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-post-merge" ) );
1213 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-post-merge" ) );
1214 }
1215
1216 m_last_p_end = aP;
1217
1218 PNS_DBGN( Dbg(), EndGroup );
1219}
1220
1221
1223{
1224 routeStep( aP );
1225
1226 if( !m_head.PointCount() )
1227 return false;
1228
1229 return m_head.CPoint( -1 ) == aP;
1230}
1231
1232
1234{
1236 l.Append( m_head.CLine() );
1237
1238 // Only simplify if we have more than two points, because if we have a zero-length seg as the
1239 // only part of the trace, we don't want it to be removed at this stage (will be the case if
1240 // the routing start point violates DRC due to track width in shove/walk mode, for example).
1241 if( l.PointCount() > 2 )
1242 l.Simplify();
1243
1244 LINE tmp( m_head );
1245
1246 tmp.SetShape( l );
1247
1248 PNS_DBG( Dbg(), AddItem, &m_tail, GREEN, 100000, wxT( "tmp-tail" ) );
1249 PNS_DBG( Dbg(), AddItem, &m_head, LIGHTGREEN, 100000, wxT( "tmp-head" ) );
1250
1251 return tmp;
1252}
1253
1254
1256{
1258 return ITEM_SET( &m_currentTrace );
1259}
1260
1261
1263{
1264 // In order to fix issue 12369 get the current line placer first direction
1265 // and copy it to the mouse trail tracer, as the current placer may have
1266 // changed the route.
1268 {
1269 DIRECTION_45 firstDirection( m_currentTrace.CSegment( 0 ) );
1270
1272 }
1273
1275}
1276
1277
1278NODE* LINE_PLACER::CurrentNode( bool aLoopsRemoved ) const
1279{
1280 if( aLoopsRemoved && m_lastNode )
1281 return m_lastNode;
1282
1283 return m_currentNode;
1284}
1285
1286
1288{
1289 if( !aSeg )
1290 return false;
1291
1292 if( !aSeg->OfKind( ITEM::SEGMENT_T ) )
1293 return false;
1294
1295 const JOINT* jt = aNode->FindJoint( aP, aSeg );
1296
1297 if( jt && jt->LinkCount() >= 1 )
1298 return false;
1299
1300 SEGMENT* s_old = static_cast<SEGMENT*>( aSeg );
1301
1302 std::unique_ptr<SEGMENT> s_new[2] = { Clone( *s_old ), Clone( *s_old ) };
1303
1304 s_new[0]->SetEnds( s_old->Seg().A, aP );
1305 s_new[1]->SetEnds( aP, s_old->Seg().B );
1306
1307 aNode->Remove( s_old );
1308 aNode->Add( std::move( s_new[0] ), true );
1309 aNode->Add( std::move( s_new[1] ), true );
1310
1311 return true;
1312}
1313
1314
1315bool LINE_PLACER::SplitAdjacentArcs( NODE* aNode, ITEM* aArc, const VECTOR2I& aP )
1316{
1317 if( !aArc )
1318 return false;
1319
1320 if( !aArc->OfKind( ITEM::ARC_T ) )
1321 return false;
1322
1323 const JOINT* jt = aNode->FindJoint( aP, aArc );
1324
1325 if( jt && jt->LinkCount() >= 1 )
1326 return false;
1327
1328 ARC* a_old = static_cast<ARC*>( aArc );
1329 const SHAPE_ARC& o_arc = a_old->Arc();
1330
1331 std::unique_ptr<ARC> a_new[2] = { Clone( *a_old ), Clone( *a_old ) };
1332
1333 a_new[0]->Arc().ConstructFromStartEndCenter( o_arc.GetP0(), aP, o_arc.GetCenter(),
1334 o_arc.IsClockwise(), o_arc.GetWidth() );
1335
1336 a_new[1]->Arc().ConstructFromStartEndCenter( aP, o_arc.GetP1(), o_arc.GetCenter(),
1337 o_arc.IsClockwise(), o_arc.GetWidth() );
1338
1339 aNode->Remove( a_old );
1340 aNode->Add( std::move( a_new[0] ), true );
1341 aNode->Add( std::move( a_new[1] ), true );
1342
1343 return true;
1344}
1345
1346
1347bool LINE_PLACER::SetLayer( int aLayer )
1348{
1349 if( m_idle )
1350 {
1351 m_currentLayer = aLayer;
1352 return true;
1353 }
1354 else if( m_chainedPlacement )
1355 {
1356 return false;
1357 }
1358 else if( !m_startItem
1359 || ( m_startItem->OfKind( ITEM::VIA_T ) && m_startItem->Layers().Overlaps( aLayer ) )
1360 || ( m_startItem->OfKind( ITEM::SOLID_T ) && m_startItem->Layers().Overlaps( aLayer ) ) )
1361 {
1362 m_currentLayer = aLayer;
1366 m_head.Line().Clear();
1367 m_tail.Line().Clear();
1368 m_head.RemoveVia();
1369 m_tail.RemoveVia();
1372 Move( m_currentEnd, nullptr );
1373 return true;
1374 }
1375
1376 return false;
1377}
1378
1379
1380bool LINE_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
1381{
1382 m_placementCorrect = false;
1383 m_currentStart = VECTOR2I( aP );
1384 m_fixStart = VECTOR2I( aP );
1385 m_currentEnd = VECTOR2I( aP );
1386 m_currentNet = aStartItem ? aStartItem->Net() : Router()->GetInterface()->GetOrphanedNetHandle();
1387 m_startItem = aStartItem;
1388 m_placingVia = false;
1389 m_chainedPlacement = false;
1391 m_endItem = nullptr;
1392
1393 setInitialDirection( Settings().InitialDirection() );
1394
1395 initPlacement();
1396
1397 DIRECTION_45 initialDir = m_initial_direction;
1399
1400 if( aStartItem && aStartItem->Kind() == ITEM::SEGMENT_T )
1401 {
1402 // If we land on a segment endpoint, assume the starting direction is continuing along
1403 // the same direction as the endpoint. If we started in the middle, don't set a
1404 // direction so that the posture solver is not biased.
1405 SEG seg = static_cast<SEGMENT*>( aStartItem )->Seg();
1406
1407 if( aP == seg.A )
1408 lastSegDir = DIRECTION_45( seg.Reversed() );
1409 else if( aP == seg.B )
1410 lastSegDir = DIRECTION_45( seg );
1411 }
1412 else if( aStartItem && aStartItem->Kind() == ITEM::SOLID_T &&
1413 static_cast<SOLID*>( aStartItem )->Parent()->Type() == PCB_PAD_T )
1414 {
1415 double angle = static_cast<SOLID*>( aStartItem )->GetOrientation().AsDegrees();
1416 angle = ( angle + 22.5 ) / 45.0;
1417 initialDir = DIRECTION_45( static_cast<DIRECTION_45::Directions>( int( angle ) ) );
1418 }
1419
1420 PNS_DBG( Dbg(), Message, wxString::Format( "Posture: init %s, last seg %s",
1421 initialDir.Format(), lastSegDir.Format() ) );
1422
1427 m_mouseTrailTracer.SetMouseDisabled( !Settings().GetAutoPosture() );
1428
1429 NODE *n;
1430
1431 if ( Settings().Mode() == PNS::RM_Shove )
1432 n = m_shove->CurrentNode();
1433 else
1434 n = m_currentNode;
1435
1437
1438 return true;
1439}
1440
1441
1443{
1444 m_idle = false;
1445
1446 m_head.Line().Clear();
1447 m_tail.Line().Clear();
1454 m_head.RemoveVia();
1455 m_tail.RemoveVia();
1456
1457 m_last_p_end.reset();
1460
1461 NODE* world = Router()->GetWorld();
1462
1463 world->KillChildren();
1464 NODE* rootNode = world->Branch();
1465
1467
1468 setWorld( rootNode );
1469
1470 wxLogTrace( wxT( "PNS" ), wxT( "world %p, intitial-direction %s layer %d" ),
1471 m_world,
1472 m_direction.Format().c_str(),
1474
1475 m_lastNode = nullptr;
1477
1478 m_shove = std::make_unique<SHOVE>( m_world->Branch(), Router() );
1479}
1480
1481
1482bool LINE_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1483{
1484 LINE current;
1485 int eiDepth = -1;
1486
1487 if( aEndItem && aEndItem->Owner() )
1488 eiDepth = static_cast<const NODE*>( aEndItem->Owner() )->Depth();
1489
1490 if( m_lastNode )
1491 {
1492 delete m_lastNode;
1493 m_lastNode = nullptr;
1494 }
1495
1496 m_endItem = aEndItem;
1497
1498 bool reachesEnd = route( aP );
1499
1500 current = Trace();
1501
1502 if( !current.PointCount() )
1504 else
1505 m_currentEnd = current.CLine().CPoint( -1 );
1506
1507 NODE* latestNode = m_currentNode;
1508 m_lastNode = latestNode->Branch();
1509
1510 if( reachesEnd
1511 && eiDepth >= 0
1512 && aEndItem && latestNode->Depth() >= eiDepth
1513 && current.SegmentCount() )
1514 {
1515 if ( aEndItem->Net() == m_currentNet )
1516 SplitAdjacentSegments( m_lastNode, aEndItem, current.CPoint( -1 ) );
1517
1518 if( Settings().RemoveLoops() )
1519 removeLoops( m_lastNode, current );
1520 }
1521
1524 return true;
1525}
1526
1527
1528bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1529{
1530 bool fixAll = Settings().GetFixAllSegments();
1531 bool realEnd = false;
1532
1533 LINE pl = Trace();
1534
1535 if( Settings().Mode() == RM_MarkObstacles )
1536 {
1537 // Mark Obstacles is sort of a half-manual, half-automated mode in which the
1538 // user has more responsibility and authority.
1539
1540 if( aEndItem )
1541 {
1542 // The user has indicated a connection should be made. If either the trace or
1543 // endItem is net-less, then allow the connection by adopting the net of the other.
1545 {
1546 m_currentNet = aEndItem->Net();
1547 pl.SetNet( m_currentNet );
1548 }
1549 else if( m_router->GetInterface()->GetNetCode( aEndItem->Net() ) <= 0 )
1550 {
1551 aEndItem->SetNet( m_currentNet );
1552 }
1553 }
1554 }
1555
1556 // Collisions still prevent fixing unless "Allow DRC violations" is checked
1557 // Note that collisions can occur even in walk/shove modes if the beginning of the trace
1558 // collides (for example if the starting track width is too high).
1559
1560 if( !Settings().AllowDRCViolations() )
1561 {
1562 NODE* checkNode = ( Settings().Mode() == RM_Shove ) ? m_shove->CurrentNode() : m_world;
1563 std::optional<OBSTACLE> obs = checkNode->CheckColliding( &pl );
1564
1565 if( obs )
1566 {
1567 // TODO: Determine why the shove node sometimes reports collisions against shoved objects.
1568 // For now, to work around this issue, we consider only solids in shove mode.
1569 if( Settings().Mode() != RM_Shove || obs->m_item->OfKind( ITEM::SOLID_T ) )
1570 return false;
1571 }
1572 }
1573
1574 const SHAPE_LINE_CHAIN& l = pl.CLine();
1575
1576 if( !l.SegmentCount() )
1577 {
1578 if( m_lastNode )
1579 {
1580 // Do a final optimization to the stored state
1581 NODE::ITEM_VECTOR removed, added;
1582 m_lastNode->GetUpdatedItems( removed, added );
1583
1584 if( !added.empty() && added.back()->Kind() == ITEM::SEGMENT_T )
1585 simplifyNewLine( m_lastNode, static_cast<SEGMENT*>( added.back() ) );
1586 }
1587
1588 // Nothing to commit if we have an empty line
1589 if( !pl.EndsWithVia() )
1590 return false;
1591
1594 if( m_lastNode )
1595 {
1596 m_lastNode->Add( Clone( pl.Via() ) );
1597 m_shove->AddLockedSpringbackNode( m_lastNode );
1598 }
1599
1600 m_currentNode = nullptr;
1601
1602 m_idle = true;
1603 m_placementCorrect = true;
1604 return true;
1605 }
1606
1607 VECTOR2I p_pre_last = l.CPoint( -1 );
1608 const VECTOR2I p_last = l.CPoint( -1 );
1609
1610 if( l.PointCount() > 2 )
1611 p_pre_last = l.CPoint( -2 );
1612
1613 if( aEndItem && m_currentNet && m_currentNet == aEndItem->Net() )
1614 realEnd = true;
1615
1616 if( aForceFinish )
1617 realEnd = true;
1618
1619 // TODO: Rollback doesn't work properly if fix-all isn't enabled and we are placing arcs,
1620 // so if we are, act as though we are in fix-all mode.
1621 if( !fixAll && l.ArcCount() )
1622 fixAll = true;
1623
1624 // TODO: lastDirSeg will be calculated incorrectly if we end on an arc
1625 SEG lastDirSeg = ( !fixAll && l.SegmentCount() > 1 ) ? l.CSegment( -2 ) : l.CSegment( -1 );
1626 DIRECTION_45 d_last( lastDirSeg );
1627
1628 int lastV;
1629
1630 if( realEnd || m_placingVia || fixAll )
1631 lastV = l.SegmentCount();
1632 else
1633 lastV = std::max( 1, l.SegmentCount() - 1 );
1634
1635 ARC arc;
1636 SEGMENT seg;
1637 LINKED_ITEM* lastItem = nullptr;
1638 int lastArc = -1;
1639
1640 for( int i = 0; i < lastV; i++ )
1641 {
1642 ssize_t arcIndex = l.ArcIndex( i );
1643
1644 if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
1645 {
1646 seg = SEGMENT( pl.CSegment( i ), m_currentNet );
1647 seg.SetWidth( pl.Width() );
1648 seg.SetLayer( m_currentLayer );
1649
1650 std::unique_ptr<SEGMENT> sp = std::make_unique<SEGMENT>( seg );
1651 lastItem = sp.get();
1652
1653 if( !m_lastNode->Add( std::move( sp ) ) )
1654 lastItem = nullptr;
1655 }
1656 else
1657 {
1658 if( arcIndex == lastArc )
1659 continue;
1660
1661 arc = ARC( l.Arc( arcIndex ), m_currentNet );
1662 arc.SetWidth( pl.Width() );
1663 arc.SetLayer( m_currentLayer );
1664
1665 std::unique_ptr<ARC> ap = std::make_unique<ARC>( arc );
1666 lastItem = ap.get();
1667
1668 if( !m_lastNode->Add( std::move( ap ) ) )
1669 lastItem = nullptr;
1670
1671 lastArc = arcIndex;
1672 }
1673 }
1674
1675 if( pl.EndsWithVia() )
1676 m_lastNode->Add( Clone( pl.Via() ) );
1677
1678 if( realEnd && lastItem )
1679 simplifyNewLine( m_lastNode, lastItem );
1680
1681 if( !realEnd )
1682 {
1683 setInitialDirection( d_last );
1684 m_currentStart = ( m_placingVia || fixAll ) ? p_last : p_pre_last;
1685
1687
1689 m_startItem = nullptr;
1690 m_placingVia = false;
1692
1695
1696 m_head.Line().Clear();
1697 m_tail.Line().Clear();
1698 m_head.RemoveVia();
1699 m_tail.RemoveVia();
1702
1703 m_shove->AddLockedSpringbackNode( m_currentNode );
1704
1705 DIRECTION_45 lastSegDir = pl.EndsWithVia() ? DIRECTION_45::UNDEFINED : d_last;
1706
1711
1712 m_placementCorrect = true;
1713 }
1714 else
1715 {
1716 m_shove->AddLockedSpringbackNode( m_lastNode );
1717 m_placementCorrect = true;
1718 m_idle = true;
1719 }
1720
1721 return realEnd;
1722}
1723
1724
1725std::optional<VECTOR2I> LINE_PLACER::UnfixRoute()
1726{
1728 std::optional<VECTOR2I> ret;
1729
1730 if ( !m_fixedTail.PopStage( st ) )
1731 return ret;
1732
1733 if( m_head.Line().PointCount() )
1734 ret = m_head.Line().CPoint( 0 );
1735
1736 m_head.Line().Clear();
1737 m_tail.Line().Clear();
1738 m_startItem = nullptr;
1739 m_p_start = st.pts[0].p;
1741 m_direction = st.pts[0].direction;
1742 m_placingVia = st.pts[0].placingVias;
1743 m_currentNode = st.commit;
1744 m_currentLayer = st.pts[0].layer;
1748 m_head.RemoveVia();
1749 m_tail.RemoveVia();
1750
1754
1755 m_shove->RewindSpringbackTo( m_currentNode );
1756 m_shove->UnlockSpringbackNode( m_currentNode );
1757
1758 if( Settings().Mode() == PNS::RM_Shove )
1759 {
1760 m_currentNode = m_shove->CurrentNode();
1762 }
1763
1765
1766 return ret;
1767}
1768
1769
1771{
1773}
1774
1775
1777{
1778 if( Settings().Mode() == PNS::RM_Shove )
1779 {
1780 m_shove->RewindToLastLockedNode();
1781 m_lastNode = m_shove->CurrentNode();
1783 }
1784
1785 if( m_lastNode )
1787
1788 m_lastNode = nullptr;
1789 m_currentNode = nullptr;
1790 return true;
1791}
1792
1793
1794void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest )
1795{
1796 if( !aLatest.SegmentCount() )
1797 return;
1798
1799 if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) )
1800 return;
1801
1802 std::set<LINKED_ITEM *> toErase;
1803 aLatest.ClearLinks();
1804 aNode->Add( aLatest, true );
1805
1806 for( int s = 0; s < aLatest.LinkCount(); s++ )
1807 {
1808 LINKED_ITEM* seg = aLatest.GetLink(s);
1809 LINE ourLine = aNode->AssembleLine( seg );
1810 JOINT a, b;
1811 std::vector<LINE> lines;
1812
1813 aNode->FindLineEnds( ourLine, a, b );
1814
1815 if( a == b )
1816 aNode->FindLineEnds( aLatest, a, b );
1817
1818 aNode->FindLinesBetweenJoints( a, b, lines );
1819
1820 int removedCount = 0;
1821 int total = 0;
1822
1823 for( LINE& line : lines )
1824 {
1825 total++;
1826
1827 if( !( line.ContainsLink( seg ) ) && line.SegmentCount() )
1828 {
1829 for( LINKED_ITEM* ss : line.Links() )
1830 toErase.insert( ss );
1831
1832 removedCount++;
1833 }
1834 }
1835
1836 PNS_DBG( Dbg(), Message, wxString::Format( "total segs removed: %d/%d", removedCount, total ) );
1837 }
1838
1839 for( LINKED_ITEM* s : toErase )
1840 aNode->Remove( s );
1841
1842 aNode->Remove( aLatest );
1843}
1844
1845
1847{
1848 wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) );
1849
1850 // Before we assemble the final line and run the optimizer, do a separate pass to clean up
1851 // colinear segments that exist on non-line-corner joints, as these will prevent proper assembly
1852 // of the line and won't get cleaned up by the optimizer.
1853 NODE::ITEM_VECTOR removed, added;
1854 aNode->GetUpdatedItems( removed, added );
1855
1856 std::set<ITEM*> cleanup;
1857
1858 auto processJoint =
1859 [&]( const JOINT* aJoint, ITEM* aItem )
1860 {
1861 if( !aJoint || aJoint->IsLineCorner() )
1862 return;
1863
1864 SEG refSeg = static_cast<SEGMENT*>( aItem )->Seg();
1865
1866 NODE::ITEM_VECTOR toRemove;
1867
1868 for( ITEM* neighbor : aJoint->CLinks().CItems() )
1869 {
1870 if( neighbor == aItem
1871 || !neighbor->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
1872 || !neighbor->LayersOverlap( aItem ) )
1873 {
1874 continue;
1875 }
1876
1877 if( static_cast<const SEGMENT*>( neighbor )->Width()
1878 != static_cast<const SEGMENT*>( aItem )->Width() )
1879 {
1880 continue;
1881 }
1882
1883 const SEG& testSeg = static_cast<const SEGMENT*>( neighbor )->Seg();
1884
1885 if( refSeg.Contains( testSeg ) )
1886 {
1887 const JOINT* nA = aNode->FindJoint( neighbor->Anchor( 0 ), neighbor );
1888 const JOINT* nB = aNode->FindJoint( neighbor->Anchor( 1 ), neighbor );
1889
1890 if( ( nA == aJoint && nB->LinkCount() == 1 ) ||
1891 ( nB == aJoint && nA->LinkCount() == 1 ) )
1892 {
1893 cleanup.insert( neighbor );
1894 }
1895 }
1896 }
1897 };
1898
1899 for( ITEM* item : added )
1900 {
1901 if( !item->OfKind( ITEM::SEGMENT_T ) || cleanup.count( item ) )
1902 continue;
1903
1904 const JOINT* jA = aNode->FindJoint( item->Anchor( 0 ), item );
1905 const JOINT* jB = aNode->FindJoint( item->Anchor( 1 ), item );
1906
1907 processJoint( jA, item );
1908 processJoint( jB, item );
1909 }
1910
1911 for( ITEM* seg : cleanup )
1912 aNode->Remove( seg );
1913
1914 // And now we can proceed with assembling the final line and optimizing it.
1915
1916 LINE l_orig = aNode->AssembleLine( aLatest );
1917 LINE l( l_orig );
1918
1919 bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode );
1920
1921 SHAPE_LINE_CHAIN simplified( l.CLine() );
1922
1923 simplified.Simplify();
1924
1925 if( optimized || simplified.PointCount() != l.PointCount() )
1926 {
1927 aNode->Remove( l_orig );
1928 l.SetShape( simplified );
1929 aNode->Add( l );
1930 PNS_DBG( Dbg(), AddItem, &l, RED, 100000, wxT("simplified"));
1931 }
1932}
1933
1934
1936{
1937 m_sizes = aSizes;
1938
1939 if( !m_idle )
1940 {
1941 // If the track width continues from an existing track, we don't want to change the width.
1942 // Disallow changing width after the first segment has been fixed because we don't want to
1943 // go back and rip up tracks or allow DRC errors
1945 || ( !HasPlacedAnything() && ( !m_startItem || m_startItem->Kind() != ITEM::SEGMENT_T ) ) )
1946 {
1950 }
1951
1952 if( m_head.EndsWithVia() )
1953 {
1956 }
1957 }
1958}
1959
1960
1962{
1963 LINE current = Trace();
1964 SHAPE_LINE_CHAIN ratLine;
1965 TOPOLOGY topo( m_lastNode );
1966
1967 if( topo.LeadingRatLine( &current, ratLine ) )
1969}
1970
1971
1972void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
1973{
1974 m_orthoMode = aOrthoMode;
1975}
1976
1977
1978bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, PNS::PNS_MODE aMode, bool aForceNoVia )
1979{
1981 DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
1982
1983 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "buildInitialLine: m_direction %s, guessedDir %s, tail points %d" ),
1984 m_direction.Format(), guessedDir.Format(), m_tail.PointCount() ) );
1985
1987 // Rounded corners don't make sense when routing orthogonally (single track at a time)
1988 if( m_orthoMode )
1990
1991 PNS_DBG( Dbg(), AddPoint, m_p_start, WHITE, 10000, wxT( "pstart [buildInitial]" ) );
1992
1993
1994 if( m_p_start == aP )
1995 {
1996 l.Clear();
1997 }
1998 else
1999 {
2000 if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
2001 {
2002 l = SHAPE_LINE_CHAIN( { m_p_start, aP } );
2003 }
2004 else
2005 {
2006 if( !m_tail.PointCount() )
2007 l = guessedDir.BuildInitialTrace( m_p_start, aP, false, cornerMode );
2008 else
2009 l = m_direction.BuildInitialTrace( m_p_start, aP, false, cornerMode );
2010 }
2011
2012 if( l.SegmentCount() > 1 && m_orthoMode )
2013 {
2014 VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) );
2015
2016 l.Remove( -1, -1 );
2017 l.SetPoint( 1, newLast );
2018 }
2019 }
2020
2021 aHead.SetLayer( m_currentLayer );
2022 aHead.SetShape( l );
2023
2024 PNS_DBG( Dbg(), AddItem, &aHead, CYAN, 10000, wxT( "initial-trace" ) );
2025
2026
2027 if( !m_placingVia || aForceNoVia )
2028 return true;
2029
2030 VIA v( makeVia( aP ) );
2031 v.SetNet( aHead.Net() );
2032
2033 if( aMode == RM_MarkObstacles )
2034 {
2035 aHead.AppendVia( v );
2036 return true;
2037 }
2038
2039 const int collMask = ( aMode == RM_Walkaround ) ? ITEM::ANY_T : ITEM::SOLID_T;
2040 const int iterLimit = Settings().ViaForcePropIterationLimit();
2041
2042 for( int attempt = 0; attempt < 2; attempt++)
2043 {
2044 VECTOR2I lead = aP - m_p_start;
2045 VECTOR2I force;
2046
2047 if( attempt == 1 && m_last_p_end.has_value() )
2048 lead = aP - m_last_p_end.value();
2049
2050 if( v.PushoutForce( m_currentNode, lead, force, collMask, iterLimit ) )
2051 {
2052 SHAPE_LINE_CHAIN line = guessedDir.BuildInitialTrace( m_p_start, aP + force, false, cornerMode );
2053 aHead = LINE( aHead, line );
2054
2055 v.SetPos( v.Pos() + force );
2056
2057 aHead.AppendVia( v );
2058
2059 PNS_DBG( Dbg(), AddPoint, v.Pos(), GREEN, 1000000, "via-force-coll-2" );
2060
2061 return true;
2062 }
2063 }
2064
2065 return false; // via placement unsuccessful
2066}
2067
2068
2069void LINE_PLACER::GetModifiedNets( std::vector<NET_HANDLE>& aNets ) const
2070{
2071 aNets.push_back( m_currentNet );
2072}
2073
2074
2076{
2078 m_lastNode = nullptr;
2079 return true;
2080}
2081
2082
2083FIXED_TAIL::FIXED_TAIL( int aLineCount )
2084{
2085
2086}
2087
2088
2090{
2091
2092}
2093
2094
2096{
2097 m_stages.clear();
2098}
2099
2100
2101void FIXED_TAIL::AddStage( const VECTOR2I& aStart, int aLayer, bool placingVias,
2102 DIRECTION_45 direction, NODE* aNode )
2103{
2104 STAGE st;
2105 FIX_POINT pt;
2106
2107 pt.p = aStart;
2108 pt.layer = aLayer;
2109 pt.direction = direction;
2110 pt.placingVias = placingVias;
2111
2112 st.pts.push_back(pt);
2113 st.commit = aNode;
2114
2115 m_stages.push_back( st );
2116}
2117
2118
2120{
2121 if( !m_stages.size() )
2122 return false;
2123
2124 aStage = m_stages.back();
2125
2126 if( m_stages.size() > 1 )
2127 m_stages.pop_back();
2128
2129 return true;
2130}
2131
2132
2134{
2135 return m_stages.size();
2136}
2137
2138}
2139
constexpr Vec NearestPoint(const Vec &aPoint) const
Return the point in this rect that is closest to the provided point.
Definition: box2.h:851
Represent route directions & corner angles in a 45-degree metric.
Definition: direction45.h:37
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I &aP0, const VECTOR2I &aP1, bool aStartDiagonal=false, CORNER_MODE aMode=CORNER_MODE::MITERED_45) const
Build a 2-segment line chain between points aP0 and aP1 and following 45-degree routing regime.
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
Definition: direction45.h:181
AngleType
Represent kind of angle formed by vectors heading in two DIRECTION_45s.
Definition: direction45.h:78
Directions
Available directions, there are 8 of them, as on a rectilinear map (north = up) + an extra undefined ...
Definition: direction45.h:49
CORNER_MODE
Corner modes.
Definition: direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition: direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition: direction45.h:70
@ 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
const std::string Format() const
Format the direction in a human readable word.
Definition: direction45.h:129
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
Definition: pns_algo_base.h:73
void SetLogger(LOGGER *aLogger)
Definition: pns_algo_base.h:65
virtual LOGGER * Logger()
ROUTER * Router() const
Return current router settings.
Definition: pns_algo_base.h:54
ROUTER * m_router
Definition: pns_algo_base.h:87
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
DEBUG_DECORATOR * Dbg() const
Definition: pns_algo_base.h:78
SHAPE_ARC & Arc()
Definition: pns_arc.h:115
void SetWidth(int aWidth) override
Definition: pns_arc.h:83
FIXED_TAIL(int aLineCount=1)
int StageCount() const
bool PopStage(STAGE &aStage)
void AddStage(const VECTOR2I &aStart, int aLayer, bool placingVias, DIRECTION_45 direction, NODE *aNode)
std::vector< STAGE > m_stages
const std::vector< ITEM * > & CItems() const
Definition: pns_itemset.h:88
Base class for PNS router board items.
Definition: pns_item.h:98
BOARD_ITEM * Parent() const
Definition: pns_item.h:190
const PNS_LAYER_RANGE & Layers() const
Definition: pns_item.h:200
virtual NET_HANDLE Net() const
Definition: pns_item.h:198
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:171
void SetNet(NET_HANDLE aNet)
Definition: pns_item.h:197
virtual int Layer() const
Definition: pns_item.h:204
void SetLayer(int aLayer)
Definition: pns_item.h:203
@ SEGMENT_T
Definition: pns_item.h:107
bool OfKind(int aKindMask) const
Definition: pns_item.h:179
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
int LinkCount(int aMask=-1) const
Definition: pns_joint.h:318
bool IsLineCorner(bool aAllowLockedSegs=false) const
Checks if a joint connects two segments of the same net, layer, and width.
Definition: pns_joint.h:101
const ITEM_SET & CLinks() const
Definition: pns_joint.h:308
bool mergeHead()
Moves "established" segments from the head to the tail if certain conditions are met.
bool SplitAdjacentArcs(NODE *aNode, ITEM *aArc, const VECTOR2I &aP)
Snaps the point aP to arc aArc.
bool handleSelfIntersections()
Check if the head of the track intersects its tail.
LINE_PLACER(ROUTER *aRouter)
bool SetLayer(int aLayer) override
Set the current routing layer.
NODE * m_lastNode
Postprocessed world state (including marked collisions & removed loops)
bool route(const VECTOR2I &aP)
Re-route the current track to point aP.
void updatePStart(const LINE &tail)
bool AbortPlacement() override
std::unique_ptr< SHOVE > m_shove
The shove engine.
const LINE Trace() const
Return the complete routed line.
bool splitHeadTail(const LINE &aNewLine, const LINE &aOldTail, LINE &aNewHead, LINE &aNewTail)
bool handlePullback()
Deal with pull-back: reduces the tail if head trace is moved backwards wrs to the current tail direct...
void setWorld(NODE *aWorld)
Set the board to route.
void UpdateSizes(const SIZES_SETTINGS &aSizes) override
Perform on-the-fly update of the width, via diameter & drill size from a settings class.
bool reduceTail(const VECTOR2I &aEnd)
Attempt to reduce the number of segments in the tail by trying to replace a certain number of latest ...
void SetOrthoMode(bool aOrthoMode) override
Function SetOrthoMode()
LINE m_tail
routing "tail": part of the track that has been already fixed due to collisions with obstacles
MOUSE_TRAIL_TRACER m_mouseTrailTracer
bool Start(const VECTOR2I &aP, ITEM *aStartItem) override
Start routing a single track at point aP, taking item aStartItem as anchor (unless NULL).
std::optional< VECTOR2I > m_last_p_end
bool optimizeTailHeadTransition()
Try to reduce the corner count of the most recent part of tail/head by merging obtuse/collinear segme...
bool HasPlacedAnything() const override
void routeStep(const VECTOR2I &aP)
Perform a single routing algorithm step, for the end point aP.
NODE * m_currentNode
Current world state.
bool buildInitialLine(const VECTOR2I &aP, LINE &aHead, PNS::PNS_MODE aMode, bool aForceNoVia=false)
bool cursorDistMinimum(const SHAPE_LINE_CHAIN &aL, const VECTOR2I &aCursor, double lengthThreshold, SHAPE_LINE_CHAIN &aOut)
LINE m_head
the volatile part of the track from the previously analyzed point to the current routing destination
void removeLoops(NODE *aNode, LINE &aLatest)
Searches aNode for traces concurrent to aLatest and removes them.
VECTOR2I m_fixStart
start point of the last 'fix'
bool rhMarkObstacles(const VECTOR2I &aP, LINE &aNewHead, LINE &aNewTail)
NODE * m_world
pointer to world to search colliding items
NET_HANDLE m_currentNet
DIRECTION_45 m_initial_direction
routing direction for new traces
NODE * CurrentNode(bool aLoopsRemoved=false) const override
Return the most recent world state.
SIZES_SETTINGS m_sizes
bool SplitAdjacentSegments(NODE *aNode, ITEM *aSeg, const VECTOR2I &aP)
Snaps the point aP to segment aSeg.
bool rhWalkBase(const VECTOR2I &aP, LINE &aWalkLine, int aCollisionMask, PNS::PNS_MODE aMode, bool &aViaOk)
void setInitialDirection(const DIRECTION_45 &aDirection)
Set preferred direction of the very first track segment to be laid.
void updateLeadingRatLine()
Draw the "leading" rats nest line, which connects the end of currently routed track and the nearest y...
bool routeHead(const VECTOR2I &aP, LINE &aNewHead, LINE &aNewTail)
Compute the head trace between the current start point (m_p_start) and point aP, starting with direct...
bool rhWalkOnly(const VECTOR2I &aP, LINE &aNewHead, LINE &aNewTail)
void FlipPosture() override
Toggle the current posture (straight/diagonal) of the trace head.
const VIA makeVia(const VECTOR2I &aP)
bool rhShoveOnly(const VECTOR2I &aP, LINE &aNewHead, LINE &aNewTail)
< Route step shove mode.
bool CommitPlacement() override
VECTOR2I m_p_start
current routing start (end of tail, beginning of head)
void GetModifiedNets(std::vector< NET_HANDLE > &aNets) const override
Function GetModifiedNets.
bool Move(const VECTOR2I &aP, ITEM *aEndItem) override
Move the end of the currently routed trace to the point aP, taking aEndItem as anchor (if not NULL).
void simplifyNewLine(NODE *aNode, LINKED_ITEM *aLatest)
Assemble a line starting from segment or arc aLatest, removes collinear segments and redundant vertic...
DIRECTION_45 m_direction
current routing direction
void initPlacement()
Initialize placement of a new line with given parameters.
FIXED_TAIL m_fixedTail
const ITEM_SET Traces() override
Return the complete routed line, as a single-member ITEM_SET.
bool FixRoute(const VECTOR2I &aP, ITEM *aEndItem, bool aForceFinish) override
Commit the currently routed track to the parent node taking aP as the final end point and aEndItem as...
std::optional< VECTOR2I > UnfixRoute() override
bool ToggleVia(bool aEnabled) override
Enable/disable a via at the end of currently routed trace.
bool clipAndCheckCollisions(const VECTOR2I &aP, const SHAPE_LINE_CHAIN &aL, SHAPE_LINE_CHAIN &aOut, int &thresholdDist)
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition: pns_line.h:62
void SetViaDrill(int aDrill)
Definition: pns_line.h:210
const VECTOR2I & CPoint(int aIdx) const
Definition: pns_line.h:146
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition: pns_line.h:127
const SHAPE_LINE_CHAIN & CLine() const
Definition: pns_line.h:138
void RemoveVia()
Definition: pns_line.cpp:1315
int CountCorners(int aAngles) const
Definition: pns_line.cpp:167
SHAPE_LINE_CHAIN & Line()
Definition: pns_line.h:137
void AppendVia(const VIA &aVia)
Definition: pns_line.cpp:1082
VIA & Via()
Definition: pns_line.h:198
int SegmentCount() const
Definition: pns_line.h:140
void SetViaDiameter(int aDiameter)
Definition: pns_line.h:201
int PointCount() const
Definition: pns_line.h:141
int ShapeCount() const
Return the aIdx-th point of the line.
Definition: pns_line.h:143
void SetWidth(int aWidth)
Return line width.
Definition: pns_line.h:150
void SetBlockingObstacle(ITEM *aObstacle)
Definition: pns_line.h:216
bool EndsWithVia() const
Definition: pns_line.h:190
const SEG CSegment(int aIdx) const
Set line width.
Definition: pns_line.h:147
virtual LINE * Clone() const override
Return a deep copy of the item.
Definition: pns_line.cpp:115
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition: pns_line.h:157
void Clear()
Definition: pns_line.cpp:1307
void SetDefaultDirections(DIRECTION_45 aInitDirection, DIRECTION_45 aLastSegDir)
void SetMouseDisabled(bool aDisabled=true)
Disables the mouse-trail portion of the posture solver; leaving only the manual posture switch and th...
void AddTrailPoint(const VECTOR2I &aP)
DIRECTION_45 GetPosture(const VECTOR2I &aP)
Keep the router "world" - i.e.
Definition: pns_node.h:231
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition: pns_node.cpp:143
int FindLinesBetweenJoints(const JOINT &aA, const JOINT &aB, std::vector< LINE > &aLines)
Find the joints corresponding to the ends of line aLine.
Definition: pns_node.cpp:1140
std::vector< ITEM * > ITEM_VECTOR
Definition: pns_node.h:242
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the pre-set worst case clearance between any pair of items.
Definition: pns_node.cpp:129
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.
Definition: pns_node.cpp:1446
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:410
const JOINT * FindJoint(const VECTOR2I &aPos, int aLayer, NET_HANDLE aNet) const
Search for a joint at a given position, layer and belonging to given net.
Definition: pns_node.cpp:1245
std::optional< OBSTACLE > OPT_OBSTACLE
Definition: pns_node.h:241
int Depth() const
Definition: pns_node.h:281
void FindLineEnds(const LINE &aLine, JOINT &aA, JOINT &aB)
Destroy all child nodes. Applicable only to the root node.
Definition: pns_node.cpp:1133
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition: pns_node.cpp:665
OPT_OBSTACLE NearestObstacle(const LINE *aLine, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS())
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition: pns_node.cpp:286
void KillChildren()
Definition: pns_node.cpp:1526
void Remove(ARC *aArc)
Remove an item from this branch.
Definition: pns_node.cpp:909
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
Definition: pns_node.cpp:1047
Perform various optimizations of the lines being routed, attempting to make the lines shorter and les...
Definition: pns_optimizer.h:95
void SetCollisionMask(int aMask)
void SetEffortLevel(int aEffort)
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
@ SMART_PADS
Reroute pad exits.
@ FANOUT_CLEANUP
Simplify pad-pad and pad-via connections if possible.
@ MERGE_SEGMENTS
Reduce corner cost iteratively.
Definition: pns_optimizer.h:99
@ MERGE_COLINEAR
Merge co-linear segments.
const ITEM_OWNER * Owner() const
Return the owner of this item, or NULL if there's none.
Definition: pns_item.h:72
virtual int GetNetCode(NET_HANDLE aNet) const =0
virtual void DisplayRatline(const SHAPE_LINE_CHAIN &aRatline, NET_HANDLE aNetCode)=0
virtual NET_HANDLE GetOrphanedNetHandle()=0
ROUTER_IFACE * GetInterface() const
Definition: pns_router.h:223
void CommitRouting()
Definition: pns_router.cpp:921
NODE * GetWorld() const
Definition: pns_router.h:169
int ViaForcePropIterationLimit() const
double WalkaroundHugLengthThreshold() const
PNS_MODE Mode() const
Set the routing mode.
DIRECTION_45::CORNER_MODE GetCornerMode() const
const SEG & Seg() const
Definition: pns_segment.h:90
int Width() const override
Definition: pns_segment.h:85
void SetWidth(int aWidth) override
Definition: pns_segment.h:80
VIATYPE ViaType() const
bool TrackWidthIsExplicit() const
bool LeadingRatLine(const LINE *aTrack, SHAPE_LINE_CHAIN &aRatLine)
const VECTOR2I & Pos() const
Definition: pns_via.h:175
bool PushoutForce(NODE *aNode, const VECTOR2I &aDirection, VECTOR2I &aForce, int aCollisionMask=ITEM::ANY_T, int aMaxIterations=10)
Definition: pns_via.cpp:125
void SetPos(const VECTOR2I &aPos)
Definition: pns_via.h:177
void SetIterationLimit(const int aIterLimit)
void SetSolidsOnly(bool aSolidsOnly)
STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
void SetItemMask(int aMask)
void SetAllowedPolicies(std::vector< WALK_POLICY > aPolicies)
Represent a contiguous set of PCB layers.
Definition: pns_layerset.h:32
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
int Length() const
Return the length (this).
Definition: seg.h:333
bool Contains(const SEG &aSeg) const
Definition: seg.h:314
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
SEG Reversed() const
Returns the center point of the line.
Definition: seg.h:363
bool IsClockwise() const
Definition: shape_arc.h:317
int GetWidth() const
Definition: shape_arc.h:210
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:849
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsPtOnArc(size_t aPtIndex) const
const SHAPE_ARC & Arc(size_t aArc) const
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
int ShapeCount() const
Return the number of shapes (line segments or arcs) in this line chain.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
void Simplify(int aMaxError=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
void Clear()
Remove all points from the line chain.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex=-1) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
const std::vector< SHAPE_ARC > & CArcs() const
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.
int SegmentCount() const
Return the number of segments in this line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
size_t ArcCount() const
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
void RemoveShape(int aPointIndex)
Remove the shape at the given index from the line chain.
std::vector< INTERSECTION > INTERSECTIONS
long long int Length() const
Return length of the line chain in Euclidean metric.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
@ LIGHTGREEN
Definition: color4d.h:63
@ WHITE
Definition: color4d.h:48
@ BLUE
Definition: color4d.h:56
@ MAGENTA
Definition: color4d.h:60
@ GREEN
Definition: color4d.h:57
@ CYAN
Definition: color4d.h:58
@ LIGHTCYAN
Definition: color4d.h:64
@ RED
Definition: color4d.h:59
@ B_Cu
Definition: layer_ids.h:65
@ F_Cu
Definition: layer_ids.h:64
Push and Shove diff pair dimensions (gap) settings dialog.
PNS_MODE
< Routing modes
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition: pns_item.h:328
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
#define PNS_DBG(dbg, method,...)
#define PNS_DBGN(dbg, method)
std::vector< FIX_POINT > pts
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
Represent an intersection between two line segments.
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695