KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_line_placer.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <optional>
23#include <memory>
24
25#include <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
952 LINE newHead( walkSolids );
953
954 if( walkSolids.EndsWithVia() )
955 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), RED, 1000000, wxString::Format( "SVIA [%d]", viaOk?1:0 ) );
956
957 if( m_placingVia && viaOk )
958 {
959 newHead.AppendVia( makeVia( newHead.CLastPoint() ) );
960 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-new-via" );
961
962 }
963
964 m_shove->ClearHeads();
965 m_shove->AddHeads( newHead, SHOVE::SHP_SHOVE );
966 bool shoveOk = m_shove->Run() == SHOVE::SH_OK;
967
968 m_currentNode = m_shove->CurrentNode();
969
970 int effort = 0;
971
972 switch( Settings().OptimizerEffort() )
973 {
974 case OE_LOW:
975 effort = 0;
976 break;
977
978 case OE_MEDIUM:
979 case OE_FULL:
981 break;
982 }
983
985
986 // Smart Pads is incompatible with 90-degree mode for now
987 if( Settings().SmartPads()
988 && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
989 && !m_mouseTrailTracer.IsManuallyForced() )
990 {
991 effort |= OPTIMIZER::SMART_PADS;
992 }
993
994 if( shoveOk )
995 {
996 if( m_shove->HeadsModified() )
997 newHead = m_shove->GetModifiedHead( 0 );
998
999 if( newHead.EndsWithVia() )
1000 {
1001 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-preopt" );
1002 PNS_DBG( Dbg(), AddPoint, newHead.Via().Pos(), GREEN, 1000000, "shove-via-postopt" );
1003 }
1004
1005 if( ! splitHeadTail( newHead, m_tail, aNewHead, aNewTail ) )
1006 return false;
1007
1008 if( newHead.EndsWithVia() )
1009 aNewHead.AppendVia( newHead.Via() );
1010
1011 OPTIMIZER::Optimize( &aNewHead, effort, m_currentNode );
1012 PNS_DBG( Dbg(), AddItem, &aNewHead, GREEN, 1000000, "head-sh-postopt" );
1013
1014 return true;
1015 }
1016 else
1017 {
1018 return rhWalkOnly( aP, aNewHead, aNewTail );
1019 }
1020
1021 return false;
1022}
1023
1024
1025bool LINE_PLACER::routeHead( const VECTOR2I& aP, LINE& aNewHead, LINE& aNewTail )
1026{
1027 switch( Settings().Mode() )
1028 {
1029 case RM_MarkObstacles:
1030 return rhMarkObstacles( aP, aNewHead, aNewTail );
1031 case RM_Walkaround:
1032 return rhWalkOnly( aP, aNewHead, aNewTail );
1033 case RM_Shove:
1034 return rhShoveOnly( aP, aNewHead, aNewTail );
1035 default:
1036 break;
1037 }
1038
1039 return false;
1040}
1041
1042
1044{
1045 LINE linetmp = Trace();
1046
1047 PNS_DBG( Dbg(), Message, "optimize HT" );
1048
1049 // NOTE: FANOUT_CLEANUP can override posture setting at the moment
1050 if( !m_mouseTrailTracer.IsManuallyForced() &&
1052 {
1053 if( linetmp.SegmentCount() < 1 )
1054 return false;
1055
1056 m_head = linetmp;
1057 m_direction = DIRECTION_45( linetmp.CSegment( 0 ) );
1058 m_tail.Line().Clear();
1059
1060 return true;
1061 }
1062
1063 SHAPE_LINE_CHAIN& head = m_head.Line();
1064 SHAPE_LINE_CHAIN& tail = m_tail.Line();
1065
1066 int tailLookbackSegments = 3;
1067
1068 //if(m_currentMode() == RM_Walkaround)
1069 // tailLookbackSegments = 10000;
1070
1071 int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
1072
1073 if( tail.ShapeCount() < 3 )
1074 return false;
1075
1076 // assemble TailLookbackSegments tail segments with the current head
1077 SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
1078
1079 int end = std::min(2, head.PointCount() - 1 );
1080
1081 opt_line.Append( head.Slice( 0, end ) );
1082
1083 LINE new_head( m_tail, opt_line );
1084
1085 // and see if it could be made simpler by merging obtuse/collnear segments.
1086 // If so, replace the (threshold) last tail points and the head with
1087 // the optimized line
1088
1089 PNS_DBG( Dbg(), AddItem, &new_head, LIGHTCYAN, 10000, wxT( "ht-newline" ) );
1090
1092 {
1093 LINE tmp( m_tail, opt_line );
1094
1095 head.Clear();
1096 tail.Replace( -threshold, -1, new_head.CLine() );
1097 tail.Simplify();
1098
1099 m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
1100
1101 return true;
1102 }
1103
1104 return false;
1105}
1106
1108{
1109 if( tail.CLine().PointCount() )
1110 m_p_start = tail.CLine().CLastPoint();
1111 else
1113}
1114
1116{
1117 bool fail = false;
1118 bool go_back = false;
1119
1120 int i, n_iter = 1;
1121
1122
1123 PNS_DBG( Dbg(), Message, wxString::Format( "routeStep: direction: %s head: %d, tail: %d shapes" ,
1124 m_direction.Format(),
1125 m_head.ShapeCount(),
1126 m_tail.ShapeCount() ) );
1127
1128 PNS_DBG( Dbg(), BeginGroup, wxT( "route-step" ), 0 );
1129
1130 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-init" ) );
1131 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-init" ) );
1132
1133 for( i = 0; i < n_iter; i++ )
1134 {
1135 LINE prevTail( m_tail );
1136 LINE prevHead( m_head );
1137 LINE newHead, newTail;
1138
1139 if( !go_back && Settings().FollowMouse() )
1140 reduceTail( aP );
1141
1142 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-reduce" ) );
1143 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-reduce" ) );
1144
1145 go_back = false;
1146
1148
1149 if( !routeHead( aP, newHead, newTail ) )
1150 {
1151 m_tail = std::move( prevTail );
1152 m_head = std::move( prevHead );
1153
1154 // If we fail to walk out of the initial point (no tail), instead of returning an empty
1155 // line, return a zero-length line so that the user gets some feedback that routing is
1156 // happening. This will get pruned later.
1157 if( m_tail.PointCount() == 0 )
1158 {
1159 m_tail.Line().Append( m_p_start );
1160 m_tail.Line().Append( m_p_start, true );
1161 }
1162
1163 fail = true;
1164 }
1165
1167
1168 PNS_DBG( Dbg(), AddItem, &newHead, LIGHTGREEN, 100000, wxString::Format( "new_head [fail: %d]", fail?1:0 ) );
1169
1170 if( fail )
1171 break;
1172
1173 PNS_DBG( Dbg(), Message, wxString::Format( "N VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1174
1175 m_head = std::move( newHead );
1176 m_tail = std::move( newTail );
1177
1179 {
1180 n_iter++;
1181 go_back = true;
1182 }
1183
1184 PNS_DBG( Dbg(), Message, wxString::Format( "SI VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1185
1186 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-after-si" ) );
1187 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-after-si" ) );
1188
1189 if( !go_back && handlePullback() )
1190 {
1191 n_iter++;
1192 m_head.Clear();
1193 go_back = true;
1194 }
1195
1196 PNS_DBG( Dbg(), Message, wxString::Format( "PB VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1197
1198 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-after-pb" ) );
1199 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-after-pb" ) );
1200 }
1201
1202
1203 if( !fail && Settings().FollowMouse() )
1204 {
1205 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 10000, wxT( "tail-pre-merge" ) );
1206 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 10000, wxT( "head-pre-merge" ) );
1207
1209 {
1210 PNS_DBG( Dbg(), Message, wxString::Format( "PreM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1211
1212 mergeHead();
1213
1214 PNS_DBG( Dbg(), Message, wxString::Format( "PostM VIA H %d T %d\n", m_head.EndsWithVia() ? 1 : 0, m_tail.EndsWithVia() ? 1 : 0 ) );
1215 }
1216
1217 PNS_DBG( Dbg(), AddItem, &m_tail, WHITE, 100000, wxT( "tail-post-merge" ) );
1218 PNS_DBG( Dbg(), AddItem, &m_head, GREEN, 100000, wxT( "head-post-merge" ) );
1219 }
1220
1221 m_last_p_end = aP;
1222
1223 PNS_DBGN( Dbg(), EndGroup );
1224}
1225
1226
1228{
1229 routeStep( aP );
1230
1231 if( !m_head.PointCount() )
1232 return false;
1233
1234 return m_head.CLastPoint() == aP;
1235}
1236
1237
1239{
1240 SHAPE_LINE_CHAIN l( m_tail.CLine() );
1241 l.Append( m_head.CLine() );
1242
1243 // Only simplify if we have more than two points, because if we have a zero-length seg as the
1244 // only part of the trace, we don't want it to be removed at this stage (will be the case if
1245 // the routing start point violates DRC due to track width in shove/walk mode, for example).
1246 if( l.PointCount() > 2 )
1247 l.Simplify();
1248
1249 LINE tmp( m_head );
1250
1251 tmp.SetShape( l );
1252
1253 PNS_DBG( Dbg(), AddItem, &m_tail, GREEN, 100000, wxT( "tmp-tail" ) );
1254 PNS_DBG( Dbg(), AddItem, &m_head, LIGHTGREEN, 100000, wxT( "tmp-head" ) );
1255
1256 return tmp;
1257}
1258
1259
1261{
1263 return ITEM_SET( &m_currentTrace );
1264}
1265
1266
1268{
1269 // In order to fix issue 12369 get the current line placer first direction
1270 // and copy it to the mouse trail tracer, as the current placer may have
1271 // changed the route.
1272 if( m_mouseTrailTracer.IsManuallyForced() == false && m_currentTrace.SegmentCount() > 0 )
1273 {
1274 DIRECTION_45 firstDirection( m_currentTrace.CSegment( 0 ) );
1275
1276 m_mouseTrailTracer.SetDefaultDirections( firstDirection, DIRECTION_45::UNDEFINED );
1277 }
1278
1279 m_mouseTrailTracer.FlipPosture();
1280}
1281
1282
1283NODE* LINE_PLACER::CurrentNode( bool aLoopsRemoved ) const
1284{
1285 if( aLoopsRemoved && m_lastNode )
1286 return m_lastNode;
1287
1288 return m_currentNode;
1289}
1290
1291
1293{
1294 if( !aSeg )
1295 return false;
1296
1297 if( !aSeg->OfKind( ITEM::SEGMENT_T ) )
1298 return false;
1299
1300 const JOINT* jt = aNode->FindJoint( aP, aSeg );
1301
1302 if( jt && jt->LinkCount() >= 1 )
1303 return false;
1304
1305 SEGMENT* s_old = static_cast<SEGMENT*>( aSeg );
1306
1307 std::unique_ptr<SEGMENT> s_new[2] = { Clone( *s_old ), Clone( *s_old ) };
1308
1309 s_new[0]->SetEnds( s_old->Seg().A, aP );
1310 s_new[1]->SetEnds( aP, s_old->Seg().B );
1311
1312 aNode->Remove( s_old );
1313 aNode->Add( std::move( s_new[0] ), true );
1314 aNode->Add( std::move( s_new[1] ), true );
1315
1316 return true;
1317}
1318
1319
1320bool LINE_PLACER::SplitAdjacentArcs( NODE* aNode, ITEM* aArc, const VECTOR2I& aP )
1321{
1322 if( !aArc )
1323 return false;
1324
1325 if( !aArc->OfKind( ITEM::ARC_T ) )
1326 return false;
1327
1328 const JOINT* jt = aNode->FindJoint( aP, aArc );
1329
1330 if( jt && jt->LinkCount() >= 1 )
1331 return false;
1332
1333 ARC* a_old = static_cast<ARC*>( aArc );
1334 const SHAPE_ARC& o_arc = a_old->Arc();
1335
1336 std::unique_ptr<ARC> a_new[2] = { Clone( *a_old ), Clone( *a_old ) };
1337
1338 a_new[0]->Arc().ConstructFromStartEndCenter( o_arc.GetP0(), aP, o_arc.GetCenter(),
1339 o_arc.IsClockwise(), o_arc.GetWidth() );
1340
1341 a_new[1]->Arc().ConstructFromStartEndCenter( aP, o_arc.GetP1(), o_arc.GetCenter(),
1342 o_arc.IsClockwise(), o_arc.GetWidth() );
1343
1344 aNode->Remove( a_old );
1345 aNode->Add( std::move( a_new[0] ), true );
1346 aNode->Add( std::move( a_new[1] ), true );
1347
1348 return true;
1349}
1350
1351
1352bool LINE_PLACER::SetLayer( int aLayer )
1353{
1354 if( m_idle )
1355 {
1356 m_currentLayer = aLayer;
1357 return true;
1358 }
1359 else if( m_chainedPlacement )
1360 {
1361 return false;
1362 }
1363 else if( !m_startItem
1364 || ( m_startItem->OfKind( ITEM::VIA_T ) && m_startItem->Layers().Overlaps( aLayer ) )
1365 || ( m_startItem->OfKind( ITEM::SOLID_T ) && m_startItem->Layers().Overlaps( aLayer ) ) )
1366 {
1367 m_currentLayer = aLayer;
1370 m_mouseTrailTracer.Clear();
1371 m_head.Line().Clear();
1372 m_tail.Line().Clear();
1373 m_head.RemoveVia();
1374 m_tail.RemoveVia();
1375 m_head.SetLayer( m_currentLayer );
1376 m_tail.SetLayer( m_currentLayer );
1377 Move( m_currentEnd, nullptr );
1378 return true;
1379 }
1380
1381 return false;
1382}
1383
1384
1385bool LINE_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
1386{
1387 m_placementCorrect = false;
1388 m_currentStart = VECTOR2I( aP );
1389 m_fixStart = VECTOR2I( aP );
1390 m_currentEnd = VECTOR2I( aP );
1391 m_currentNet = aStartItem ? aStartItem->Net() : Router()->GetInterface()->GetOrphanedNetHandle();
1392 m_startItem = aStartItem;
1393 m_placingVia = false;
1394 m_chainedPlacement = false;
1395 m_fixedTail.Clear();
1396 m_endItem = nullptr;
1397
1398 setInitialDirection( Settings().InitialDirection() );
1399
1400 initPlacement();
1401
1402 DIRECTION_45 initialDir = m_initial_direction;
1404
1405 if( aStartItem && aStartItem->Kind() == ITEM::SEGMENT_T )
1406 {
1407 // If we land on a segment endpoint, assume the starting direction is continuing along
1408 // the same direction as the endpoint. If we started in the middle, don't set a
1409 // direction so that the posture solver is not biased.
1410 SEG seg = static_cast<SEGMENT*>( aStartItem )->Seg();
1411
1412 if( aP == seg.A )
1413 lastSegDir = DIRECTION_45( seg.Reversed() );
1414 else if( aP == seg.B )
1415 lastSegDir = DIRECTION_45( seg );
1416 }
1417 else if( aStartItem && aStartItem->Kind() == ITEM::SOLID_T &&
1418 static_cast<SOLID*>( aStartItem )->Parent()->Type() == PCB_PAD_T )
1419 {
1420 double angle = static_cast<SOLID*>( aStartItem )->GetOrientation().AsDegrees();
1421 angle = ( angle + 22.5 ) / 45.0;
1422 initialDir = DIRECTION_45( static_cast<DIRECTION_45::Directions>( int( angle ) ) );
1423 }
1424
1425 PNS_DBG( Dbg(), Message, wxString::Format( "Posture: init %s, last seg %s",
1426 initialDir.Format(), lastSegDir.Format() ) );
1427
1428 m_mouseTrailTracer.Clear();
1429 m_mouseTrailTracer.AddTrailPoint( aP );
1430 m_mouseTrailTracer.SetTolerance( m_head.Width() );
1432 m_mouseTrailTracer.SetMouseDisabled( !Settings().GetAutoPosture() );
1433
1434 NODE *n;
1435
1436 if ( Settings().Mode() == PNS::RM_Shove )
1437 n = m_shove->CurrentNode();
1438 else
1439 n = m_currentNode;
1440
1442
1443 return true;
1444}
1445
1446
1448{
1449 m_idle = false;
1450
1451 m_head.Line().Clear();
1452 m_tail.Line().Clear();
1453 m_head.SetNet( m_currentNet );
1454 m_tail.SetNet( m_currentNet );
1455 m_head.SetLayer( m_currentLayer );
1456 m_tail.SetLayer( m_currentLayer );
1457 m_head.SetWidth( m_sizes.TrackWidth() );
1458 m_tail.SetWidth( m_sizes.TrackWidth() );
1459 m_head.RemoveVia();
1460 m_tail.RemoveVia();
1461
1462 m_last_p_end.reset();
1465
1466 NODE* world = Router()->GetWorld();
1467
1468 world->KillChildren();
1469 NODE* rootNode = world->Branch();
1470
1472
1473 setWorld( rootNode );
1474
1475 wxLogTrace( wxT( "PNS" ), wxT( "world %p, intitial-direction %s layer %d" ),
1476 m_world,
1477 m_direction.Format().c_str(),
1479
1480 m_lastNode = nullptr;
1482
1483 m_shove = std::make_unique<SHOVE>( m_world->Branch(), Router() );
1484}
1485
1486
1487bool LINE_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1488{
1489 LINE current;
1490 int eiDepth = -1;
1491
1492 if( aEndItem && aEndItem->Owner() )
1493 eiDepth = static_cast<const NODE*>( aEndItem->Owner() )->Depth();
1494
1495 if( m_lastNode )
1496 {
1497 delete m_lastNode;
1498 m_lastNode = nullptr;
1499 }
1500
1501 m_endItem = aEndItem;
1502
1503 bool reachesEnd = route( aP );
1504
1505 current = Trace();
1506
1507 VECTOR2I splitPoint = current.PointCount() ? current.CLine().CLastPoint() : m_p_start;
1508
1509 if( reachesEnd && aEndItem && current.SegmentCount() && aEndItem->OfKind( ITEM::SEGMENT_T ) )
1510 {
1511 const SEG lastSeg = current.CLine().CSegment( current.SegmentCount() - 1 );
1512 const SEG targetSeg = static_cast<SEGMENT*>( aEndItem )->Seg();
1513
1514 if( lastSeg.Collinear( targetSeg ) && targetSeg.Overlaps( lastSeg ) )
1515 {
1516 splitPoint = targetSeg.NearestPoint( lastSeg.A );
1517 current.Line().SetPoint( current.PointCount() - 1, splitPoint );
1518 m_head.Line().SetPoint( m_head.PointCount() - 1, splitPoint );
1519 }
1520 }
1521
1522 if( !current.PointCount() )
1524 else
1525 m_currentEnd = splitPoint;
1526
1527 NODE* latestNode = m_currentNode;
1528 m_lastNode = latestNode->Branch();
1529
1530 if( reachesEnd
1531 && eiDepth >= 0
1532 && aEndItem && latestNode->Depth() >= eiDepth
1533 && current.SegmentCount() )
1534 {
1535 if ( aEndItem->Net() == m_currentNet )
1536 SplitAdjacentSegments( m_lastNode, aEndItem, splitPoint );
1537
1538 if( Settings().RemoveLoops() )
1539 removeLoops( m_lastNode, current );
1540 }
1541
1543 m_mouseTrailTracer.AddTrailPoint( aP );
1544 return true;
1545}
1546
1547
1548bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1549{
1550 bool fixAll = Settings().GetFixAllSegments();
1551 bool realEnd = false;
1552
1553 LINE pl = Trace();
1554
1555 if( Settings().Mode() == RM_MarkObstacles )
1556 {
1557 // Mark Obstacles is sort of a half-manual, half-automated mode in which the
1558 // user has more responsibility and authority.
1559
1560 if( aEndItem )
1561 {
1562 // The user has indicated a connection should be made. If either the trace or
1563 // endItem is net-less, then allow the connection by adopting the net of the other.
1564 if( m_router->GetInterface()->GetNetCode( m_currentNet ) <= 0 )
1565 {
1566 m_currentNet = aEndItem->Net();
1567 pl.SetNet( m_currentNet );
1568 }
1569 else if( m_router->GetInterface()->GetNetCode( aEndItem->Net() ) <= 0 )
1570 {
1571 aEndItem->SetNet( m_currentNet );
1572 }
1573 }
1574 }
1575
1576 // Collisions still prevent fixing unless "Allow DRC violations" is checked
1577 // Note that collisions can occur even in walk/shove modes if the beginning of the trace
1578 // collides (for example if the starting track width is too high).
1579
1580 if( !Settings().AllowDRCViolations() )
1581 {
1582 NODE* checkNode = ( Settings().Mode() == RM_Shove ) ? m_shove->CurrentNode() : m_world;
1583 std::optional<OBSTACLE> obs = checkNode->CheckColliding( &pl );
1584
1585 if( obs )
1586 {
1587 // TODO: Determine why the shove node sometimes reports collisions against shoved objects.
1588 // For now, to work around this issue, we consider only solids in shove mode.
1589 if( Settings().Mode() != RM_Shove || obs->m_item->OfKind( ITEM::SOLID_T ) )
1590 return false;
1591 }
1592 }
1593
1594 const SHAPE_LINE_CHAIN& l = pl.CLine();
1595
1596 if( !l.SegmentCount() )
1597 {
1598 if( m_lastNode )
1599 {
1600 // Do a final optimization to the stored state
1601 NODE::ITEM_VECTOR removed, added;
1602 m_lastNode->GetUpdatedItems( removed, added );
1603
1604 if( !added.empty() && added.back()->Kind() == ITEM::SEGMENT_T )
1605 simplifyNewLine( m_lastNode, static_cast<SEGMENT*>( added.back() ) );
1606 }
1607
1608 // Nothing to commit if we have an empty line
1609 if( !pl.EndsWithVia() )
1610 return false;
1611
1614 if( m_lastNode )
1615 {
1616 auto newVia = Clone( pl.Via() );
1617 newVia->ResetUid();
1618 m_lastNode->Add( std::move( newVia ) );
1619 m_shove->AddLockedSpringbackNode( m_lastNode );
1620 }
1621
1622 m_currentNode = nullptr;
1623
1624 m_idle = true;
1625 m_placementCorrect = true;
1626 return true;
1627 }
1628
1629 VECTOR2I p_pre_last = l.CLastPoint();
1630 const VECTOR2I p_last = l.CLastPoint();
1631
1632 if( l.PointCount() > 2 )
1633 p_pre_last = l.CPoints()[ l.PointCount() - 2 ];
1634
1635 if( aEndItem && m_currentNet && m_currentNet == aEndItem->Net() )
1636 realEnd = true;
1637
1638 if( aForceFinish )
1639 realEnd = true;
1640
1641 // TODO: Rollback doesn't work properly if fix-all isn't enabled and we are placing arcs,
1642 // so if we are, act as though we are in fix-all mode.
1643 if( !fixAll && l.ArcCount() )
1644 fixAll = true;
1645
1646 // TODO: lastDirSeg will be calculated incorrectly if we end on an arc
1647 SEG lastDirSeg = ( !fixAll && l.SegmentCount() > 1 ) ? l.CSegment( -2 ) : l.CSegment( -1 );
1648 DIRECTION_45 d_last( lastDirSeg );
1649
1650 int lastV;
1651
1652 if( realEnd || m_placingVia || fixAll )
1653 lastV = l.SegmentCount();
1654 else
1655 lastV = std::max( 1, l.SegmentCount() - 1 );
1656
1657 ARC arc;
1658 SEGMENT seg;
1659 LINKED_ITEM* lastItem = nullptr;
1660 int lastArc = -1;
1661
1662 for( int i = 0; i < lastV; i++ )
1663 {
1664 ssize_t arcIndex = l.ArcIndex( i );
1665
1666 if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
1667 {
1668 seg = SEGMENT( pl.CSegment( i ), m_currentNet );
1669 seg.SetWidth( pl.Width() );
1670 seg.SetLayer( m_currentLayer );
1671
1672 std::unique_ptr<SEGMENT> sp = std::make_unique<SEGMENT>( seg );
1673 lastItem = sp.get();
1674
1675 if( !m_lastNode->Add( std::move( sp ) ) )
1676 lastItem = nullptr;
1677 }
1678 else
1679 {
1680 if( arcIndex == lastArc )
1681 continue;
1682
1683 arc = ARC( l.Arc( arcIndex ), m_currentNet );
1684 arc.SetWidth( pl.Width() );
1685 arc.SetLayer( m_currentLayer );
1686
1687 std::unique_ptr<ARC> ap = std::make_unique<ARC>( arc );
1688 lastItem = ap.get();
1689
1690 if( !m_lastNode->Add( std::move( ap ) ) )
1691 lastItem = nullptr;
1692
1693 lastArc = arcIndex;
1694 }
1695 }
1696
1697 if( pl.EndsWithVia() )
1698 {
1699 auto newVia = Clone( pl.Via() );
1700 newVia->ResetUid();
1701 m_lastNode->Add( std::move( newVia ) );
1702 }
1703
1704
1705 if( lastItem )
1706 simplifyNewLine( m_lastNode, lastItem );
1707
1708 if( !realEnd )
1709 {
1710 setInitialDirection( d_last );
1711 m_currentStart = ( m_placingVia || fixAll ) ? p_last : p_pre_last;
1712
1714
1716 m_startItem = nullptr;
1717 m_placingVia = false;
1719
1722
1723 m_head.Line().Clear();
1724 m_tail.Line().Clear();
1725 m_head.RemoveVia();
1726 m_tail.RemoveVia();
1728 m_lastNode = m_lastNode->Branch();
1729
1730 m_shove->AddLockedSpringbackNode( m_currentNode );
1731
1732 DIRECTION_45 lastSegDir = pl.EndsWithVia() ? DIRECTION_45::UNDEFINED : d_last;
1733
1734 m_mouseTrailTracer.Clear();
1735 m_mouseTrailTracer.SetTolerance( m_head.Width() );
1736 m_mouseTrailTracer.AddTrailPoint( m_currentStart );
1737 m_mouseTrailTracer.SetDefaultDirections( lastSegDir, DIRECTION_45::UNDEFINED );
1738
1739 m_placementCorrect = true;
1740 }
1741 else
1742 {
1743 m_shove->AddLockedSpringbackNode( m_lastNode );
1744 m_placementCorrect = true;
1745 m_idle = true;
1746 }
1747
1748 return realEnd;
1749}
1750
1751
1752std::optional<VECTOR2I> LINE_PLACER::UnfixRoute()
1753{
1755 std::optional<VECTOR2I> ret;
1756
1757 if ( !m_fixedTail.PopStage( st ) )
1758 return ret;
1759
1760 if( m_head.Line().PointCount() )
1761 ret = m_head.Line().CPoint( 0 );
1762
1763 m_head.Line().Clear();
1764 m_tail.Line().Clear();
1765 m_startItem = nullptr;
1766 m_p_start = st.pts[0].p;
1768 m_direction = st.pts[0].direction;
1769 m_placingVia = st.pts[0].placingVias;
1770 m_currentNode = st.commit;
1771 m_currentLayer = st.pts[0].layer;
1773 m_head.SetLayer( m_currentLayer );
1774 m_tail.SetLayer( m_currentLayer );
1775 m_head.RemoveVia();
1776 m_tail.RemoveVia();
1777
1778 m_mouseTrailTracer.Clear();
1779 m_mouseTrailTracer.SetDefaultDirections( m_initial_direction, m_direction );
1780 m_mouseTrailTracer.AddTrailPoint( m_p_start );
1781
1782 m_shove->RewindSpringbackTo( m_currentNode );
1783 m_shove->UnlockSpringbackNode( m_currentNode );
1784
1785 if( Settings().Mode() == PNS::RM_Shove )
1786 {
1787 m_currentNode = m_shove->CurrentNode();
1788 m_currentNode->KillChildren();
1789 }
1790
1791 m_lastNode = m_currentNode->Branch();
1792
1793 return ret;
1794}
1795
1796
1798{
1799 return m_placementCorrect || m_fixedTail.StageCount() > 1;
1800}
1801
1802
1804{
1805 if( Settings().Mode() == PNS::RM_Shove )
1806 {
1807 m_shove->RewindToLastLockedNode();
1808 m_lastNode = m_shove->CurrentNode();
1809 m_lastNode->KillChildren();
1810 }
1811
1812 if( m_lastNode )
1814
1815 m_lastNode = nullptr;
1816 m_currentNode = nullptr;
1817 return true;
1818}
1819
1820
1821void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest )
1822{
1823 if( !aLatest.SegmentCount() )
1824 return;
1825
1826 if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CLastPoint() )
1827 return;
1828
1829 std::set<LINKED_ITEM *> toErase;
1830 aLatest.ClearLinks();
1831 aNode->Add( aLatest, true );
1832
1833 for( int s = 0; s < aLatest.LinkCount(); s++ )
1834 {
1835 LINKED_ITEM* seg = aLatest.GetLink(s);
1836 LINE ourLine = aNode->AssembleLine( seg );
1837 JOINT a, b;
1838 std::vector<LINE> lines;
1839
1840 aNode->FindLineEnds( ourLine, a, b );
1841
1842 if( a == b )
1843 aNode->FindLineEnds( aLatest, a, b );
1844
1845 aNode->FindLinesBetweenJoints( a, b, lines );
1846
1847 int removedCount = 0;
1848 int total = 0;
1849
1850 for( LINE& line : lines )
1851 {
1852 total++;
1853
1854 if( !( line.ContainsLink( seg ) ) && line.SegmentCount() )
1855 {
1856 // Don't remove locked tracks
1857 bool hasLockedSegment = false;
1858 for( LINKED_ITEM* ss : line.Links() )
1859 {
1860 if( ss->IsLocked() )
1861 {
1862 hasLockedSegment = true;
1863 break;
1864 }
1865 }
1866
1867 if( !hasLockedSegment )
1868 {
1869 for( LINKED_ITEM* ss : line.Links() )
1870 toErase.insert( ss );
1871
1872 removedCount++;
1873 }
1874 }
1875 }
1876
1877 PNS_DBG( Dbg(), Message, wxString::Format( "total segs removed: %d/%d", removedCount, total ) );
1878 }
1879
1880 for( LINKED_ITEM* s : toErase )
1881 aNode->Remove( s );
1882
1883 aNode->Remove( aLatest );
1884}
1885
1886
1888{
1889 wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) );
1890
1891 // Before we assemble the final line and run the optimizer, do a separate pass to clean up
1892 // colinear segments that exist on non-line-corner joints, as these will prevent proper assembly
1893 // of the line and won't get cleaned up by the optimizer.
1894 NODE::ITEM_VECTOR removed, added;
1895 aNode->GetUpdatedItems( removed, added );
1896
1897 std::set<ITEM*> cleanup;
1898
1899 auto processJoint =
1900 [&]( const JOINT* aJoint, ITEM* aItem )
1901 {
1902 if( !aJoint || aJoint->IsLineCorner() )
1903 return;
1904
1905 SEG refSeg = static_cast<SEGMENT*>( aItem )->Seg();
1906
1907 NODE::ITEM_VECTOR toRemove;
1908
1909 for( ITEM* neighbor : aJoint->CLinks().CItems() )
1910 {
1911 if( neighbor == aItem
1912 || !neighbor->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
1913 || !neighbor->LayersOverlap( aItem ) )
1914 {
1915 continue;
1916 }
1917
1918 if( static_cast<const SEGMENT*>( neighbor )->Width()
1919 != static_cast<const SEGMENT*>( aItem )->Width() )
1920 {
1921 continue;
1922 }
1923
1924 const SEG& testSeg = static_cast<const SEGMENT*>( neighbor )->Seg();
1925
1926 if( refSeg.Contains( testSeg ) )
1927 {
1928 const JOINT* nA = aNode->FindJoint( neighbor->Anchor( 0 ), neighbor );
1929 const JOINT* nB = aNode->FindJoint( neighbor->Anchor( 1 ), neighbor );
1930
1931 if( ( nA == aJoint && nB->LinkCount() == 1 ) ||
1932 ( nB == aJoint && nA->LinkCount() == 1 ) )
1933 {
1934 cleanup.insert( neighbor );
1935 }
1936 }
1937 else if( testSeg.Contains( refSeg ) )
1938 {
1939 const JOINT* aA = aNode->FindJoint( aItem->Anchor( 0 ), aItem );
1940 const JOINT* aB = aNode->FindJoint( aItem->Anchor( 1 ), aItem );
1941
1942 if( ( aA == aJoint && aB->LinkCount() == 1 ) ||
1943 ( aB == aJoint && aA->LinkCount() == 1 ) )
1944 {
1945 cleanup.insert( aItem );
1946 return;
1947 }
1948 }
1949 }
1950 };
1951
1952 for( ITEM* item : added )
1953 {
1954 if( !item->OfKind( ITEM::SEGMENT_T ) || cleanup.count( item ) )
1955 continue;
1956
1957 const JOINT* jA = aNode->FindJoint( item->Anchor( 0 ), item );
1958 const JOINT* jB = aNode->FindJoint( item->Anchor( 1 ), item );
1959
1960 processJoint( jA, item );
1961 processJoint( jB, item );
1962 }
1963
1964 for( ITEM* seg : cleanup )
1965 aNode->Remove( seg );
1966
1967 // And now we can proceed with assembling the final line and optimizing it.
1968
1969 LINE l_orig = aNode->AssembleLine( aLatest, nullptr, false, false, false );
1970 LINE l( l_orig );
1971
1972 bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode );
1973
1974 SHAPE_LINE_CHAIN simplified( l.CLine() );
1975
1976 simplified.Simplify();
1977
1978 if( optimized || simplified.PointCount() != l.PointCount() )
1979 {
1980 aNode->Remove( l_orig );
1981 l.SetShape( simplified );
1982 aNode->Add( l );
1983 PNS_DBG( Dbg(), AddItem, &l, RED, 100000, wxT("simplified"));
1984 }
1985}
1986
1987
1989{
1990 m_sizes = aSizes;
1991
1992 if( !m_idle )
1993 {
1994 // If the track width continues from an existing track, we don't want to change the width.
1995 // Disallow changing width after the first segment has been fixed because we don't want to
1996 // go back and rip up tracks or allow DRC errors
1997 if( m_sizes.TrackWidthIsExplicit()
1998 || ( !HasPlacedAnything() && ( !m_startItem || m_startItem->Kind() != ITEM::SEGMENT_T ) ) )
1999 {
2000 m_head.SetWidth( m_sizes.TrackWidth() );
2001 m_tail.SetWidth( m_sizes.TrackWidth() );
2002 m_currentTrace.SetWidth( m_sizes.TrackWidth() );
2003 }
2004
2005 if( m_head.EndsWithVia() )
2006 {
2007 m_head.SetViaDiameter( m_sizes.ViaDiameter() );
2008 m_head.SetViaDrill( m_sizes.ViaDrill() );
2009 }
2010 }
2011}
2012
2013
2015{
2016 LINE current = Trace();
2017 SHAPE_LINE_CHAIN ratLine;
2018 TOPOLOGY topo( m_lastNode );
2019
2020 if( topo.LeadingRatLine( &current, ratLine ) )
2021 m_router->GetInterface()->DisplayRatline( ratLine, m_currentNet );
2022}
2023
2024
2025void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
2026{
2027 m_orthoMode = aOrthoMode;
2028}
2029
2030
2031bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, PNS::PNS_MODE aMode, bool aForceNoVia )
2032{
2034 DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
2035
2036 PNS_DBG( Dbg(), Message, wxString::Format( wxT( "buildInitialLine: m_direction %s, guessedDir %s, tail points %d" ),
2037 m_direction.Format(), guessedDir.Format(), m_tail.PointCount() ) );
2038
2040 // Rounded corners don't make sense when routing orthogonally (single track at a time)
2041 if( m_orthoMode )
2043
2044 PNS_DBG( Dbg(), AddPoint, m_p_start, WHITE, 10000, wxT( "pstart [buildInitial]" ) );
2045
2046
2047 if( m_p_start == aP )
2048 {
2049 l.Clear();
2050 }
2051 else
2052 {
2053 if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
2054 {
2055 l = SHAPE_LINE_CHAIN( { m_p_start, aP } );
2056 }
2057 else
2058 {
2059 if( !m_tail.PointCount() )
2060 l = guessedDir.BuildInitialTrace( m_p_start, aP, false, cornerMode );
2061 else
2062 l = m_direction.BuildInitialTrace( m_p_start, aP, false, cornerMode );
2063 }
2064
2065 if( l.SegmentCount() > 1 && m_orthoMode )
2066 {
2067 VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CLastPoint() );
2068
2069 l.Remove( -1, -1 );
2070 l.SetPoint( 1, newLast );
2071 }
2072 }
2073
2074 aHead.SetLayer( m_currentLayer );
2075 aHead.SetShape( l );
2076
2077 PNS_DBG( Dbg(), AddItem, &aHead, CYAN, 10000, wxT( "initial-trace" ) );
2078
2079
2080 if( !m_placingVia || aForceNoVia )
2081 return true;
2082
2083 VIA v( makeVia( aP ) );
2084 v.SetNet( aHead.Net() );
2085
2086 if( aMode == RM_MarkObstacles )
2087 {
2088 aHead.AppendVia( v );
2089 return true;
2090 }
2091
2092 const int collMask = ( aMode == RM_Walkaround ) ? ITEM::ANY_T : ITEM::SOLID_T;
2093 const int iterLimit = Settings().ViaForcePropIterationLimit();
2094
2095 for( int attempt = 0; attempt < 2; attempt++)
2096 {
2097 VECTOR2I lead = aP - m_p_start;
2098 VECTOR2I force;
2099
2100 if( attempt == 1 && m_last_p_end.has_value() )
2101 lead = aP - m_last_p_end.value();
2102
2103 if( v.PushoutForce( m_currentNode, lead, force, collMask, iterLimit ) )
2104 {
2105 SHAPE_LINE_CHAIN line = guessedDir.BuildInitialTrace( m_p_start, aP + force, false, cornerMode );
2106 aHead = LINE( aHead, line );
2107
2108 v.SetPos( v.Pos() + force );
2109
2110 aHead.AppendVia( v );
2111
2112 PNS_DBG( Dbg(), AddPoint, v.Pos(), GREEN, 1000000, "via-force-coll-2" );
2113
2114 return true;
2115 }
2116 }
2117
2118 return false; // via placement unsuccessful
2119}
2120
2121
2122void LINE_PLACER::GetModifiedNets( std::vector<NET_HANDLE>& aNets ) const
2123{
2124 aNets.push_back( m_currentNet );
2125}
2126
2127
2129{
2130 m_world->KillChildren();
2131 m_lastNode = nullptr;
2132 return true;
2133}
2134
2135
2136FIXED_TAIL::FIXED_TAIL( int aLineCount )
2137{
2138
2139}
2140
2141
2143{
2144
2145}
2146
2147
2149{
2150 m_stages.clear();
2151}
2152
2153
2154void FIXED_TAIL::AddStage( const VECTOR2I& aStart, int aLayer, bool placingVias,
2155 DIRECTION_45 direction, NODE* aNode )
2156{
2157 STAGE st;
2158 FIX_POINT pt;
2159
2160 pt.p = aStart;
2161 pt.layer = aLayer;
2162 pt.direction = direction;
2163 pt.placingVias = placingVias;
2164
2165 st.pts.push_back(pt);
2166 st.commit = aNode;
2167
2168 m_stages.push_back( std::move( st ) );
2169}
2170
2171
2173{
2174 if( !m_stages.size() )
2175 return false;
2176
2177 aStage = m_stages.back();
2178
2179 if( m_stages.size() > 1 )
2180 m_stages.pop_back();
2181
2182 return true;
2183}
2184
2185
2187{
2188 return m_stages.size();
2189}
2190
2191}
constexpr Vec NearestPoint(const Vec &aPoint) const
Return the point in this rect that is closest to the provided point.
Definition box2.h:851
Represent route directions & corner angles in a 45-degree metric.
Definition direction45.h:37
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I &aP0, const VECTOR2I &aP1, bool aStartDiagonal=false, CORNER_MODE aMode=CORNER_MODE::MITERED_45) const
Build a 2-segment line chain between points aP0 and aP1 and following 45-degree routing regime.
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
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:111
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:88
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:240
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:157
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:251
void GetUpdatedItems(ITEM_VECTOR &aRemoved, ITEM_VECTOR &aAdded)
Return the list of items removed and added in this branch with respect to the root branch.
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition pns_node.cpp:494
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:250
int Depth() const
Definition pns_node.h:290
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:749
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:993
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:236
void CommitRouting()
NODE * GetWorld() const
Definition pns_router.h:182
double WalkaroundHugLengthThreshold() const
PNS_MODE Mode() const
Set the routing mode.
DIRECTION_45::CORNER_MODE GetCornerMode() const
const SEG & Seg() const
Definition pns_segment.h:93
void SetWidth(int aWidth) override
Definition pns_segment.h:83
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:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:633
int Length() const
Return the length (this).
Definition seg.h:343
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
Definition seg.h:286
bool Overlaps(const SEG &aSeg) const
Definition seg.h:301
bool Contains(const SEG &aSeg) const
Definition seg.h:324
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:685
SEG Reversed() const
Returns the center point of the line.
Definition seg.h:373
bool IsClockwise() const
Definition shape_arc.h:323
int GetWidth() const override
Definition shape_arc.h:215
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
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:63
@ WHITE
Definition color4d.h:48
@ BLUE
Definition color4d.h:56
@ MAGENTA
Definition color4d.h:60
@ GREEN
Definition color4d.h:57
@ CYAN
Definition color4d.h:58
@ LIGHTCYAN
Definition color4d.h:64
@ RED
Definition color4d.h:59
@ SEGMENT
Definition eda_shape.h:45
@ B_Cu
Definition layer_ids.h:65
@ F_Cu
Definition layer_ids.h:64
Push and Shove diff pair dimensions (gap) settings dialog.
PNS_MODE
< Routing modes
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition pns_item.h: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:87
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695