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