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