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