KiCad PCB EDA Suite
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 (C) 2016-2021 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 <core/optional.h>
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"
34 #include "pns_mouse_trail_tracer.h"
35 
36 #include <wx/log.h>
37 
38 namespace 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 = 0;
53  m_currentLayer = 0;
54  m_startItem = nullptr;
55  m_chainedPlacement = false;
56  m_orthoMode = false;
57  m_placementCorrect = false;
58 }
59 
60 
62 {
63 }
64 
65 
66 void LINE_PLACER::setWorld( NODE* aWorld )
67 {
68  m_world = aWorld;
69 }
70 
71 
72 const VIA LINE_PLACER::makeVia( const VECTOR2I& aP )
73 {
76 
77  return VIA( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), -1, m_sizes.ViaType() );
78 }
79 
80 
81 bool LINE_PLACER::ToggleVia( bool aEnabled )
82 {
83  m_placingVia = aEnabled;
84 
85  if( !aEnabled )
86  m_head.RemoveVia();
87 
88  return true;
89 }
90 
91 
93 {
94  m_initial_direction = aDirection;
95 
96  if( m_tail.SegmentCount() == 0 )
97  m_direction = aDirection;
98 }
99 
100 
102 {
104  SHAPE_LINE_CHAIN& head = m_head.Line();
105  SHAPE_LINE_CHAIN& tail = m_tail.Line();
106 
107  // if there is no tail, there is nothing to intersect with
108  if( tail.PointCount() < 2 )
109  return false;
110 
111  if( head.PointCount() < 2 )
112  return false;
113 
114  // completely new head trace? chop off the tail
115  if( tail.CPoint(0) == head.CPoint(0) )
116  {
117  m_p_start = tail.CPoint( 0 );
119  tail.Clear();
120  return true;
121  }
122 
123  tail.Intersect( head, ips );
124 
125  // no intesection points - nothing to reduce
126  if( ips.empty() )
127  return false;
128 
129  int n = INT_MAX;
130  VECTOR2I ipoint;
131 
132  // if there is more than one intersection, find the one that is
133  // closest to the beginning of the tail.
134  for( const SHAPE_LINE_CHAIN::INTERSECTION& i : ips )
135  {
136  if( i.index_our < n )
137  {
138  n = i.index_our;
139  ipoint = i.p;
140  }
141  }
142 
143  // ignore the point where head and tail meet
144  if( ipoint == head.CPoint( 0 ) || ipoint == tail.CPoint( -1 ) )
145  return false;
146 
147  // Intersection point is on the first or the second segment: just start routing
148  // from the beginning
149  if( n < 2 )
150  {
151  m_p_start = tail.CPoint( 0 );
153  tail.Clear();
154  head.Clear();
155 
156  return true;
157  }
158  else
159  {
160  // Clip till the last tail segment before intersection.
161  // Set the direction to the one of this segment.
162  const SEG last = tail.CSegment( n - 1 );
163  m_p_start = last.A;
164  m_direction = DIRECTION_45( last );
165  tail.Remove( n, -1 );
166  return true;
167  }
168 
169  return false;
170 }
171 
172 
174 {
175  SHAPE_LINE_CHAIN& head = m_head.Line();
176  SHAPE_LINE_CHAIN& tail = m_tail.Line();
177 
178  if( head.PointCount() < 2 )
179  return false;
180 
181  int n = tail.PointCount();
182 
183  if( n == 0 )
184  {
185  return false;
186  }
187  else if( n == 1 )
188  {
189  m_p_start = tail.CPoint( 0 );
190  tail.Clear();
191  return true;
192  }
193 
194  DIRECTION_45 first_head, last_tail;
195 
196  wxASSERT( tail.PointCount() >= 2 );
197 
198  if( !head.IsPtOnArc( 0 ) )
199  first_head = DIRECTION_45( head.CSegment( 0 ) );
200  else
201  first_head = DIRECTION_45( head.CArcs()[head.ArcIndex(0)] );
202 
203  int lastSegIdx = tail.PointCount() - 2;
204 
205  if( !tail.IsPtOnArc( lastSegIdx ) )
206  last_tail = DIRECTION_45( tail.CSegment( lastSegIdx ) );
207  else
208  last_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex(lastSegIdx)] );
209 
210  DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
211 
212  // case 1: we have a defined routing direction, and the currently computed
213  // head goes in different one.
214  bool pullback_1 = false; // (m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
215 
216  // case 2: regardless of the current routing direction, if the tail/head
217  // extremities form an acute or right angle, reduce the tail by one segment
218  // (and hope that further iterations) will result with a cleaner trace
219  bool pullback_2 = ( angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE );
220 
221  if( pullback_1 || pullback_2 )
222  {
223  lastSegIdx = tail.PrevShape( -1 );
224 
225  if( !tail.IsPtOnArc( lastSegIdx ) )
226  {
227  const SEG& seg = tail.CSegment( lastSegIdx );
228  m_direction = DIRECTION_45( seg );
229  m_p_start = seg.A;
230  }
231  else
232  {
233  const SHAPE_ARC& arc = tail.CArcs()[tail.ArcIndex( lastSegIdx )];
234  m_direction = DIRECTION_45( arc );
235  m_p_start = arc.GetP0();
236  }
237 
238  wxLogTrace( wxT( "PNS" ), wxT( "Placer: pullback triggered [%d] [%s %s]" ),
239  n, last_tail.Format().c_str(), first_head.Format().c_str() );
240 
241  // erase the last point in the tail, hoping that the next iteration will
242  // result with a head trace that starts with a segment following our
243  // current direction.
244  if( n < 2 )
245  tail.Clear(); // don't leave a single-point tail
246  else
247  tail.RemoveShape( -1 );
248 
249  if( !tail.SegmentCount() )
251 
252  return true;
253  }
254 
255  return false;
256 }
257 
258 
260 {
261  SHAPE_LINE_CHAIN& head = m_head.Line();
262  SHAPE_LINE_CHAIN& tail = m_tail.Line();
263 
264  int n = tail.SegmentCount();
265 
266  if( head.SegmentCount() < 1 )
267  return false;
268 
269  // Don't attempt this for too short tails
270  if( n < 2 )
271  return false;
272 
273  // Start from the segment farthest from the end of the tail
274  // int start_index = std::max(n - 1 - ReductionDepth, 0);
275 
276  DIRECTION_45 new_direction;
277  VECTOR2I new_start;
278  int reduce_index = -1;
279 
280  for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
281  {
282  const SEG s = tail.CSegment( i );
283  DIRECTION_45 dir( s );
284 
285  // calculate a replacement route and check if it matches
286  // the direction of the segment to be replaced
287  SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace( s.A, aEnd );
288 
289  if( replacement.SegmentCount() < 1 )
290  continue;
291 
292  LINE tmp( m_tail, replacement );
293 
295  break;
296 
297  if( DIRECTION_45( replacement.CSegment( 0 ) ) == dir )
298  {
299  new_start = s.A;
300  new_direction = dir;
301  reduce_index = i;
302  }
303  }
304 
305  if( reduce_index >= 0 )
306  {
307  wxLogTrace( wxT( "PNS" ), wxT( "Placer: reducing tail: %d" ), reduce_index );
308  SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace( new_start, aEnd );
309 
310  m_p_start = new_start;
311  m_direction = new_direction;
312  tail.Remove( reduce_index + 1, -1 );
313  head.Clear();
314  return true;
315  }
316 
317  if( !tail.SegmentCount() )
319 
320  return false;
321 }
322 
323 
325 {
326  SHAPE_LINE_CHAIN& head = m_head.Line();
327  SHAPE_LINE_CHAIN& tail = m_tail.Line();
328 
329  const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE
332 
333  head.Simplify();
334  tail.Simplify();
335 
336  int n_head = head.ShapeCount();
337  int n_tail = tail.ShapeCount();
338 
339  if( n_head < 3 )
340  {
341  wxLogTrace( wxT( "PNS" ), wxT( "Merge failed: not enough head segs." ) );
342  return false;
343  }
344 
345  if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
346  {
347  wxLogTrace( wxT( "PNS" ), wxT( "Merge failed: head and tail discontinuous." ) );
348  return false;
349  }
350 
351  if( m_head.CountCorners( ForbiddenAngles ) != 0 )
352  return false;
353 
354  DIRECTION_45 dir_tail, dir_head;
355 
356  if( !head.IsPtOnArc( 0 ) )
357  dir_head = DIRECTION_45( head.CSegment( 0 ) );
358  else
359  dir_head = DIRECTION_45( head.CArcs()[head.ArcIndex( 0 )] );
360 
361  if( n_tail )
362  {
363  wxASSERT( tail.PointCount() >= 2 );
364  int lastSegIdx = tail.PointCount() - 2;
365 
366  if( !tail.IsPtOnArc( lastSegIdx ) )
367  dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
368  else
369  dir_tail = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
370 
371  if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
372  return false;
373  }
374 
375  tail.Append( head );
376 
377  tail.Simplify();
378 
379  SEG last = tail.CSegment( -1 );
380  m_p_start = last.B;
381 
382  int lastSegIdx = tail.PointCount() - 2;
383 
384  if( !tail.IsArcSegment( lastSegIdx ) )
385  m_direction = DIRECTION_45( tail.CSegment( -1 ) );
386  else
387  m_direction = DIRECTION_45( tail.CArcs()[tail.ArcIndex( lastSegIdx )] );
388 
389  head.Remove( 0, -1 );
390 
391  wxLogTrace( wxT( "PNS" ), wxT( "Placer: merge %d, new direction: %s" ), n_head,
392  m_direction.Format().c_str() );
393 
394  head.Simplify();
395  tail.Simplify();
396 
397  return true;
398 }
399 
400 
402 {
403  // Keep distances squared for performance
404  SEG::ecoord min_dist_sq = VECTOR2I::ECOORD_MAX;
405  VECTOR2I closest;
406 
407  for( int i = 0; i < line.SegmentCount(); i++ )
408  {
409  const SEG& s = line.CSegment( i );
410  VECTOR2I a = s.NearestPoint( p );
411  int d_sq = (a - p).SquaredEuclideanNorm();
412 
413  if( d_sq < min_dist_sq )
414  {
415  min_dist_sq = d_sq;
416  closest = a;
417  }
418  }
419 
420  return closest;
421 }
422 
423 
424 static bool cursorDistMinimum( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aCursor, double lengthThreshold, int& theDist, VECTOR2I& aNearest )
425 {
426  std::vector<int> dists;
427  std::vector<VECTOR2I> pts;
428 
429  if( aL.PointCount() == 0 )
430  return false;
431 
432  VECTOR2I lastP = aL.CPoint(-1);
433  int accumulatedDist = 0;
434 
435  dists.reserve( 2 * aL.PointCount() );
436 
437  for( int i = 0; i < aL.SegmentCount(); i++ )
438  {
439  const SEG& s = aL.CSegment( i );
440 
441  dists.push_back( ( aCursor - s.A ).EuclideanNorm() );
442  pts.push_back( s.A );
443  auto pn = s.NearestPoint( aCursor );
444 
445  if( pn != s.A && pn != s.B )
446  {
447  dists.push_back( ( pn - aCursor ).EuclideanNorm() );
448  pts.push_back( pn );
449  }
450 
451  accumulatedDist += s.Length();
452 
453  if ( accumulatedDist > lengthThreshold )
454  {
455  lastP = s.B;
456  break;
457  }
458  }
459 
460  dists.push_back( ( aCursor - lastP ).EuclideanNorm() );
461  pts.push_back( lastP );
462 
463  int minDistLoc = std::numeric_limits<int>::max();
464  int minPLoc = -1;
465  int minDistGlob = std::numeric_limits<int>::max();
466  int minPGlob = -1;
467 
468  for( int i = 0; i < dists.size(); i++ )
469  {
470  int d = dists[i];
471 
472  if( d < minDistGlob )
473  {
474  minDistGlob = d;
475  minPGlob = i;
476  }
477  }
478 
479  if( dists.size() >= 3 )
480  {
481  for( int i = 0; i < dists.size() - 3; i++ )
482  {
483  if( dists[i + 2] > dists[i + 1] && dists[i] > dists[i + 1] )
484  {
485  int d = dists[i + 1];
486  if( d < minDistLoc )
487  {
488  minDistLoc = d;
489  minPLoc = i + 1;
490  }
491  }
492  }
493 
494  if( dists.back() < minDistLoc && minPLoc >= 0 )
495  {
496  minDistLoc = dists.back();
497  minPLoc = dists.size() - 1;
498  }
499  }
500  else
501  {
502  // Too few points: just use the global
503  minDistLoc = minDistGlob;
504  minPLoc = minPGlob;
505  }
506 
507 // fixme: I didn't make my mind yet if local or global minimum feels better. I'm leaving both
508 // in the code, enabling the global one by default
509  minPLoc = -1;
510 
511  if( minPLoc < 0 )
512  {
513  theDist = minDistGlob;
514  aNearest = pts[minPGlob];
515  return true;
516  }
517  else
518  {
519  theDist = minDistLoc;
520  aNearest = pts[minPLoc];
521  return true;
522  }
523 }
524 
525 
526 bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead )
527 {
528  LINE initTrack( m_head );
529  LINE walkFull( m_head );
530 
531  initTrack.RemoveVia();
532  walkFull.RemoveVia();
533 
534  int effort = 0;
535  bool viaOk = false;
536 
537  VECTOR2I walkP = aP;
538 
539  WALKAROUND walkaround( m_currentNode, Router() );
540 
541  walkaround.SetSolidsOnly( false );
542  walkaround.SetDebugDecorator( Dbg() );
543  walkaround.SetLogger( Logger() );
544  walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
545 
546  char name[50];
547  int round = 0;
548 
549  do {
550  snprintf( name, sizeof( name ), "walk-round-%d", round );
551  PNS_DBG( Dbg(), BeginGroup, name );
552 
553  viaOk = buildInitialLine( walkP, initTrack, round == 0 );
554 
555  double initialLength = initTrack.CLine().Length();
556  double hugThresholdLength = initialLength * Settings().WalkaroundHugLengthThreshold();
557 
558  WALKAROUND::RESULT wr = walkaround.Route( initTrack );
559 
560  SHAPE_LINE_CHAIN l_cw = wr.lineCw.CLine();
561  SHAPE_LINE_CHAIN l_ccw = wr.lineCcw.CLine();
562 
564  {
565  int len_cw = wr.statusCw == WALKAROUND::DONE ? l_cw.Length() : INT_MAX;
566  int len_ccw = wr.statusCcw == WALKAROUND::DONE ? l_ccw.Length() : INT_MAX;
567 
568  PNS_DBG( Dbg(), AddLine, wr.lineCw.CLine(), CYAN, 10000, "wf-result-cw" );
569  PNS_DBG( Dbg(), AddLine, wr.lineCcw.CLine(), BLUE, 20000, "wf-result-ccw" );
570 
571  int bestLength = len_cw < len_ccw ? len_cw : len_ccw;
572 
573  if( bestLength > hugThresholdLength )
574  {
577  }
578 
579  SHAPE_LINE_CHAIN& bestLine = len_cw < len_ccw ? l_cw : l_ccw;
580  walkFull.SetShape( bestLine );
581  }
582 
584  {
585  bool valid_cw = false, valid_ccw = false;
586  VECTOR2I p_cw, p_ccw;
587  int dist_ccw = 0, dist_cw = 0;
588 
590  {
591  valid_ccw = cursorDistMinimum( l_ccw, aP, hugThresholdLength, dist_ccw, p_ccw );
592 
593  if( valid_ccw )
594  {
595  int idx_ccw = l_ccw.Split( p_ccw );
596  l_ccw = l_ccw.Slice( 0, idx_ccw );
597  PNS_DBG( Dbg(), AddPoint, p_ccw, BLUE, 500000, "hug-target-ccw" );
598  PNS_DBG( Dbg(), AddLine, l_ccw, MAGENTA, 200000, "wh-result-ccw" );
599  }
600  }
601 
603  {
604  valid_cw = cursorDistMinimum( l_cw, aP, hugThresholdLength, dist_cw, p_cw );
605 
606  if( valid_cw )
607  {
608  int idx_cw = l_cw.Split( p_cw );
609  l_cw = l_cw.Slice( 0, idx_cw );
610  PNS_DBG( Dbg(), AddPoint, p_cw, YELLOW, 500000, "hug-target-cw" );
611  PNS_DBG( Dbg(), AddLine, l_cw, BLUE, 200000, "wh-result-cw" );
612  }
613  }
614 
615  if( dist_cw < dist_ccw && valid_cw )
616  {
617  walkFull.SetShape( l_cw );
618  walkP = p_cw;
619  }
620  else if ( valid_ccw )
621  {
622  walkFull.SetShape( l_ccw );
623  walkP = p_ccw;
624  }
625  else
626  {
627  PNS_DBGN( Dbg(), EndGroup );
628  return false;
629  }
630  }
631  else if ( wr.statusCcw == WALKAROUND::STUCK || wr.statusCw == WALKAROUND::STUCK )
632  {
633  PNS_DBGN( Dbg(), EndGroup );
634  return false;
635  }
636 
637  PNS_DBGN( Dbg(), EndGroup );
638 
639  round++;
640  } while( round < 2 && m_placingVia );
641 
642  PNS_DBG( Dbg(), AddLine, walkFull.CLine(), GREEN, 200000, "walk-full" );
643 
644  switch( Settings().OptimizerEffort() )
645  {
646  case OE_LOW:
647  effort = 0;
648  break;
649 
650  case OE_MEDIUM:
651  case OE_FULL:
652  effort = OPTIMIZER::MERGE_SEGMENTS;
653  break;
654  }
655 
657 
658  // Smart Pads is incompatible with 90-degree mode for now
659  if( Settings().SmartPads()
660  && ( cornerMode == DIRECTION_45::MITERED_45 || cornerMode == DIRECTION_45::ROUNDED_45 )
662  {
663  effort |= OPTIMIZER::SMART_PADS;
664  }
665 
666  if( m_placingVia && viaOk )
667  {
668  walkFull.AppendVia( makeVia( walkFull.CPoint( -1 ) ) );
669  }
670 
671  OPTIMIZER::Optimize( &walkFull, effort, m_currentNode );
672 
673  if( m_currentNode->CheckColliding( &walkFull ) )
674  {
675  return false;
676  }
677 
678  aNewHead = walkFull;
679 
680  return true;
681 }
682 
683 
684 bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead )
685 {
686  buildInitialLine( aP, m_head );
687  m_head.SetBlockingObstacle( nullptr );
688 
689  // Note: Something like the below could be used to implement a "stop at first obstacle" mode,
690  // but we don't have one right now and there isn't a lot of demand for one. If we do end up
691  // doing that, put it in a new routing mode as "highlight collisions" mode should not have
692  // collision handling other than highlighting.
693 #if 0
694  if( !Settings().AllowDRCViolations() )
695  {
697 
698  if( obs && obs->m_distFirst != INT_MAX )
699  {
700  buildInitialLine( obs->m_ipFirst, m_head );
701  m_head.SetBlockingObstacle( obs->m_item );
702  }
703  }
704 #endif
705 
706  aNewHead = m_head;
707 
708  return true;
709 }
710 
711 
712 bool LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, LINE& aNewHead )
713 {
714  LINE initTrack( m_head );
715  LINE walkSolids, l2;
716 
717  bool viaOk = buildInitialLine( aP, initTrack );
718 
719  m_currentNode = m_shove->CurrentNode();
720 
721  m_shove->SetLogger( Logger() );
722  m_shove->SetDebugDecorator( Dbg() );
723 
724  OPTIMIZER optimizer( m_currentNode );
725 
726  WALKAROUND walkaround( m_currentNode, Router() );
727 
728  walkaround.SetSolidsOnly( true );
729  walkaround.SetIterationLimit( 10 );
730  walkaround.SetDebugDecorator( Dbg() );
731  walkaround.SetLogger( Logger() );
732  WALKAROUND::WALKAROUND_STATUS stat_solids = walkaround.Route( initTrack, walkSolids );
733 
735  optimizer.SetCollisionMask( ITEM::SOLID_T );
736  optimizer.Optimize( &walkSolids );
737 
738  if( stat_solids == WALKAROUND::DONE )
739  l2 = walkSolids;
740  else
741  l2 = initTrack.ClipToNearestObstacle( m_shove->CurrentNode() );
742 
743  LINE l( m_tail );
744  l.Line().Append( l2.CLine() );
745  l.Line().Simplify();
746 
747  if( l.PointCount() == 0 || l2.PointCount() == 0 )
748  {
749  aNewHead = m_head;
750  return false;
751  }
752 
753  if( m_placingVia && viaOk )
754  {
755  VIA v1( makeVia( l.CPoint( -1 ) ) );
756  VIA v2( makeVia( l2.CPoint( -1 ) ) );
757 
758  l.AppendVia( v1 );
759  l2.AppendVia( v2 );
760  }
761 
762  l.Line().Simplify();
763 
764  // in certain, uncommon cases there may be loops in the head+tail, In such case, we don't
765  // shove to avoid screwing up the database.
766  if( l.HasLoops() )
767  {
768  aNewHead = m_head;
769  return false;
770  }
771 
772  if( m_endItem )
773  {
774  // Make sure the springback algorithm won't erase the NODE that owns m_endItem.
775  m_shove->SetSpringbackDoNotTouchNode( m_endItem->Owner() );
776  }
777 
778  SHOVE::SHOVE_STATUS status = m_shove->ShoveLines( l );
779 
780  m_currentNode = m_shove->CurrentNode();
781 
782  if( status == SHOVE::SH_OK || status == SHOVE::SH_HEAD_MODIFIED )
783  {
784  if( status == SHOVE::SH_HEAD_MODIFIED )
785  l2 = m_shove->NewHead();
786 
787  optimizer.SetWorld( m_currentNode );
788 
789  int effortLevel = OPTIMIZER::MERGE_OBTUSE;
790 
791  if( Settings().SmartPads() && !m_mouseTrailTracer.IsManuallyForced() )
792  effortLevel = OPTIMIZER::SMART_PADS;
793 
794  optimizer.SetEffortLevel( effortLevel );
795 
796  optimizer.SetCollisionMask( ITEM::ANY_T );
797  optimizer.Optimize( &l2 );
798 
799  aNewHead = l2;
800 
801  return true;
802  }
803  else
804  {
805  walkaround.SetWorld( m_currentNode );
806  walkaround.SetSolidsOnly( false );
807  walkaround.SetIterationLimit( 10 );
808  walkaround.SetApproachCursor( true, aP );
809  walkaround.Route( initTrack, l2 );
810  aNewHead = l2.ClipToNearestObstacle( m_shove->CurrentNode() );
811 
812  return false;
813  }
814 
815  return false;
816 }
817 
818 
819 bool LINE_PLACER::routeHead( const VECTOR2I& aP, LINE& aNewHead )
820 {
821  switch( Settings().Mode() )
822  {
823  case RM_MarkObstacles:
824  return rhMarkObstacles( aP, aNewHead );
825  case RM_Walkaround:
826  return rhWalkOnly( aP, aNewHead );
827  case RM_Shove:
828  return rhShoveOnly( aP, aNewHead );
829  default:
830  break;
831  }
832 
833  return false;
834 }
835 
836 
838 {
839  LINE linetmp = Trace();
840 
841  PNS_DBG( Dbg(), Message, "optimize HT" );
842 
843  // NOTE: FANOUT_CLEANUP can override posture setting at the moment
846  {
847  if( linetmp.SegmentCount() < 1 )
848  return false;
849 
850  m_head = linetmp;
851  m_p_start = linetmp.CLine().CPoint( 0 );
852  m_direction = DIRECTION_45( linetmp.CSegment( 0 ) );
853  m_tail.Line().Clear();
854 
855  PNS_DBG( Dbg(), Message, wxString::Format( "Placer: optimize fanout-cleanup" ) );
856 
857 
858  return true;
859  }
860 
861  SHAPE_LINE_CHAIN& head = m_head.Line();
862  SHAPE_LINE_CHAIN& tail = m_tail.Line();
863 
864  int tailLookbackSegments = 3;
865 
866  //if(m_currentMode() == RM_Walkaround)
867  // tailLookbackSegments = 10000;
868 
869  int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
870 
871  if( tail.ShapeCount() < 3 )
872  return false;
873 
874  // assemble TailLookbackSegments tail segments with the current head
875  SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
876 
877  int end = std::min(2, head.PointCount() - 1 );
878 
879  opt_line.Append( head.Slice( 0, end ) );
880 
881  LINE new_head( m_tail, opt_line );
882 
883  // and see if it could be made simpler by merging obtuse/collnear segments.
884  // If so, replace the (threshold) last tail points and the head with
885  // the optimized line
886 
887 
888  PNS_DBG( Dbg(), AddLine, new_head.CLine(), LIGHTCYAN, 10000, "ht-newline" );
889 
891  {
892  LINE tmp( m_tail, opt_line );
893 
894  PNS_DBG( Dbg(), Message, wxString::Format( "Placer: optimize tail-head [%d]", threshold ) );
895 
896  head.Clear();
897  tail.Replace( -threshold, -1, new_head.CLine() );
898  tail.Simplify();
899 
900  m_p_start = new_head.CLine().CPoint( -1 );
901  m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
902 
903  return true;
904  }
905 
906  return false;
907 }
908 
909 
911 {
912  bool fail = false;
913  bool go_back = false;
914 
915  int i, n_iter = 1;
916 
917  LINE new_head;
918 
919  wxLogTrace( wxT( "PNS" ), wxT( "routeStep: direction: %s head: %d, tail: %d shapes" ),
920  m_direction.Format().c_str(),
921  m_head.ShapeCount(),
922  m_tail.ShapeCount() );
923 
924  PNS_DBG( Dbg(), BeginGroup, "route-step" );
925 
926  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 10000, "tail-init" );
927  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 10000, "head-init" );
928 
929  for( i = 0; i < n_iter; i++ )
930  {
931  if( !go_back && Settings().FollowMouse() )
932  reduceTail( aP );
933 
934  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 10000, "tail-after-reduce" );
935  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 10000, "head-after-reduce" );
936 
937  go_back = false;
938 
939  if( !routeHead( aP, new_head ) )
940  fail = true;
941 
942  if( !new_head.Is45Degree() &&
944  fail = true;
945 
946  if( fail )
947  break;
948 
949  m_head = new_head;
950 
951  PNS_DBG( Dbg(), AddLine, m_head.CLine(), LIGHTGREEN, 100000, "head-new" );
952 
954  {
955  n_iter++;
956  go_back = true;
957  }
958 
959  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 10000, "tail-after-si" );
960  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 10000, "head-after-si" );
961 
962  if( !go_back && handlePullback() )
963  {
964  n_iter++;
965  go_back = true;
966  }
967 
968  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 100000, "tail-after-pb" );
969  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 100000, "head-after-pb" );
970  }
971 
972  if( fail )
973  {
974  if( m_last_head.PointCount() > 0 )
975  {
977  }
978  else
979  {
980  m_head.RemoveVia();
981  m_head.Clear();
982  }
983  }
984  else
985  {
987  }
988 
989  if( !fail && Settings().FollowMouse() )
990  {
991  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 10000, "tail-pre-merge" );
992  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 10000, "head-pre-merge" );
993 
995  {
996  mergeHead();
997  }
998 
999  PNS_DBG( Dbg(), AddLine, m_tail.CLine(), WHITE, 100000, "tail-post-merge" );
1000  PNS_DBG( Dbg(), AddLine, m_head.CLine(), GREEN, 100000, "head-post-merge" );
1001  }
1002 
1003  PNS_DBGN( Dbg(), EndGroup );
1004 }
1005 
1006 
1007 bool LINE_PLACER::route( const VECTOR2I& aP )
1008 {
1009  routeStep( aP );
1010 
1011  if (!m_head.PointCount() )
1012  return false;
1013 
1014  return m_head.CPoint(-1) == aP;
1015 }
1016 
1017 
1019 {
1020  LINE tmp( m_head );
1021 
1022  tmp.SetShape( m_tail.CLine() );
1023  tmp.Line().Append( m_head.CLine() );
1024  tmp.Line().Simplify();
1025  return tmp;
1026 }
1027 
1028 
1030 {
1031  m_currentTrace = Trace();
1032  return ITEM_SET( &m_currentTrace );
1033 }
1034 
1035 
1037 {
1039 }
1040 
1041 
1042 NODE* LINE_PLACER::CurrentNode( bool aLoopsRemoved ) const
1043 {
1044  if( aLoopsRemoved && m_lastNode )
1045  return m_lastNode;
1046 
1047  return m_currentNode;
1048 }
1049 
1050 
1051 bool LINE_PLACER::SplitAdjacentSegments( NODE* aNode, ITEM* aSeg, const VECTOR2I& aP )
1052 {
1053  if( !aSeg )
1054  return false;
1055 
1056  if( !aSeg->OfKind( ITEM::SEGMENT_T ) )
1057  return false;
1058 
1059  JOINT* jt = aNode->FindJoint( aP, aSeg );
1060 
1061  if( jt && jt->LinkCount() >= 1 )
1062  return false;
1063 
1064  SEGMENT* s_old = static_cast<SEGMENT*>( aSeg );
1065 
1066  std::unique_ptr<SEGMENT> s_new[2] = { Clone( *s_old ), Clone( *s_old ) };
1067 
1068  s_new[0]->SetEnds( s_old->Seg().A, aP );
1069  s_new[1]->SetEnds( aP, s_old->Seg().B );
1070 
1071  aNode->Remove( s_old );
1072  aNode->Add( std::move( s_new[0] ), true );
1073  aNode->Add( std::move( s_new[1] ), true );
1074 
1075  return true;
1076 }
1077 
1078 
1079 bool LINE_PLACER::SetLayer( int aLayer )
1080 {
1081  if( m_idle )
1082  {
1083  m_currentLayer = aLayer;
1084  return true;
1085  }
1086  else if( m_chainedPlacement )
1087  {
1088  return false;
1089  }
1090  else if( !m_startItem
1091  || ( m_startItem->OfKind( ITEM::VIA_T ) && m_startItem->Layers().Overlaps( aLayer ) )
1092  || ( m_startItem->OfKind( ITEM::SOLID_T ) && m_startItem->Layers().Overlaps( aLayer ) ) )
1093  {
1094  m_currentLayer = aLayer;
1098  m_head.Line().Clear();
1099  m_tail.Line().Clear();
1100  m_last_head.Line().Clear();
1101  m_head.RemoveVia();
1102  m_tail.RemoveVia();
1106  Move( m_currentEnd, nullptr );
1107  return true;
1108  }
1109 
1110  return false;
1111 }
1112 
1113 
1114 bool LINE_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
1115 {
1116  m_placementCorrect = false;
1117  m_currentStart = VECTOR2I( aP );
1118  m_currentEnd = VECTOR2I( aP );
1119  m_currentNet = std::max( 0, aStartItem ? aStartItem->Net() : 0 );
1120  m_startItem = aStartItem;
1121  m_placingVia = false;
1122  m_chainedPlacement = false;
1123  m_fixedTail.Clear();
1124  m_endItem = nullptr;
1125 
1126  setInitialDirection( Settings().InitialDirection() );
1127 
1128  initPlacement();
1129 
1130  DIRECTION_45 initialDir = m_initial_direction;
1132 
1133  if( aStartItem && aStartItem->Kind() == ITEM::SEGMENT_T )
1134  {
1135  // If we land on a segment endpoint, assume the starting direction is continuing along
1136  // the same direction as the endpoint. If we started in the middle, don't set a
1137  // direction so that the posture solver is not biased.
1138  SEG seg = static_cast<SEGMENT*>( aStartItem )->Seg();
1139 
1140  if( aP == seg.A )
1141  lastSegDir = DIRECTION_45( seg.Reversed() );
1142  else if( aP == seg.B )
1143  lastSegDir = DIRECTION_45( seg );
1144  }
1145  else if( aStartItem && aStartItem->Kind() == ITEM::SOLID_T &&
1146  static_cast<SOLID*>( aStartItem )->Parent()->Type() == PCB_PAD_T )
1147  {
1148  double angle = static_cast<SOLID*>( aStartItem )->GetOrientation() / 10.0;
1149  angle = ( angle + 22.5 ) / 45.0;
1150  initialDir = DIRECTION_45( static_cast<DIRECTION_45::Directions>( int( angle ) ) );
1151  }
1152 
1153  wxLogTrace( wxT( "PNS" ), wxT( "Posture: init %s, last seg %s" ),
1154  initialDir.Format(), lastSegDir.Format() );
1155 
1160  m_mouseTrailTracer.SetMouseDisabled( !Settings().GetAutoPosture() );
1161 
1162  NODE *n;
1163 
1164  if ( Settings().Mode() == PNS::RM_Shove )
1165  n = m_shove->CurrentNode();
1166  else
1167  n = m_currentNode;
1168 
1170 
1171  return true;
1172 }
1173 
1174 
1176 {
1177  m_idle = false;
1178 
1179  m_head.Line().Clear();
1180  m_tail.Line().Clear();
1187  m_head.RemoveVia();
1188  m_tail.RemoveVia();
1189 
1192 
1193  NODE* world = Router()->GetWorld();
1194 
1195  world->KillChildren();
1196  NODE* rootNode = world->Branch();
1197 
1199 
1200  setWorld( rootNode );
1201 
1202  wxLogTrace( wxT( "PNS" ), wxT( "world %p, intitial-direction %s layer %d" ),
1203  m_world,
1204  m_direction.Format().c_str(),
1205  m_currentLayer );
1206 
1207  m_lastNode = nullptr;
1209 
1210  m_shove = std::make_unique<SHOVE>( m_world->Branch(), Router() );
1211 }
1212 
1213 
1214 bool LINE_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1215 {
1216  LINE current;
1217  VECTOR2I p = aP;
1218  int eiDepth = -1;
1219 
1220  if( aEndItem && aEndItem->Owner() )
1221  eiDepth = static_cast<NODE*>( aEndItem->Owner() )->Depth();
1222 
1223  if( m_lastNode )
1224  {
1225  delete m_lastNode;
1226  m_lastNode = nullptr;
1227  }
1228 
1229  m_endItem = aEndItem;
1230 
1231  bool reachesEnd = route( p );
1232 
1233  current = Trace();
1234 
1235  if( !current.PointCount() )
1237  else
1238  m_currentEnd = current.CLine().CPoint( -1 );
1239 
1240  NODE* latestNode = m_currentNode;
1241  m_lastNode = latestNode->Branch();
1242 
1243  if( reachesEnd
1244  && eiDepth >= 0
1245  && aEndItem && latestNode->Depth() >= eiDepth
1246  && current.SegmentCount() )
1247  {
1248  SplitAdjacentSegments( m_lastNode, aEndItem, current.CPoint( -1 ) );
1249 
1250  if( Settings().RemoveLoops() )
1251  removeLoops( m_lastNode, current );
1252  }
1253 
1256  return true;
1257 }
1258 
1259 
1260 bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1261 {
1262  bool fixAll = Settings().GetFixAllSegments();
1263  bool realEnd = false;
1264 
1265  LINE pl = Trace();
1266 
1267  if( Settings().Mode() == RM_MarkObstacles )
1268  {
1269  // Mark Obstacles is sort of a half-manual, half-automated mode in which the
1270  // user has more responsibility and authority.
1271 
1272  if( aEndItem )
1273  {
1274  // The user has indicated a connection should be made. If either the trace or
1275  // endItem is net-less, then allow the connection by adopting the net of the other.
1276  if( m_currentNet <= 0 )
1277  {
1278  m_currentNet = aEndItem->Net();
1279  pl.SetNet( m_currentNet );
1280  }
1281  else if (aEndItem->Net() <= 0 )
1282  {
1283  aEndItem->SetNet( m_currentNet );
1284  }
1285  }
1286 
1287  // Collisions still prevent fixing unless "Allow DRC violations" is checked
1288  if( !Settings().AllowDRCViolations() && m_world->CheckColliding( &pl ) )
1289  return false;
1290  }
1291 
1292  const SHAPE_LINE_CHAIN& l = pl.CLine();
1293 
1294  if( !l.SegmentCount() )
1295  {
1296  if( m_lastNode )
1297  {
1298  // Do a final optimization to the stored state
1299  NODE::ITEM_VECTOR removed, added;
1300  m_lastNode->GetUpdatedItems( removed, added );
1301 
1302  if( !added.empty() && added.back()->Kind() == ITEM::SEGMENT_T )
1303  simplifyNewLine( m_lastNode, static_cast<SEGMENT*>( added.back() ) );
1304  }
1305 
1306  // Nothing to commit if we have an empty line
1307  if( !pl.EndsWithVia() )
1308  return false;
1309 
1312  if( m_lastNode )
1313  {
1314  m_lastNode->Add( Clone( pl.Via() ) );
1315  m_shove->AddLockedSpringbackNode( m_lastNode );
1316  }
1317 
1318  m_currentNode = nullptr;
1319 
1320  m_idle = true;
1321  m_placementCorrect = true;
1322  return true;
1323  }
1324 
1325  VECTOR2I p_pre_last = l.CPoint( -1 );
1326  const VECTOR2I p_last = l.CPoint( -1 );
1327 
1328  if( l.PointCount() > 2 )
1329  p_pre_last = l.CPoint( -2 );
1330 
1331  if( aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->Net() )
1332  realEnd = true;
1333 
1334  if( aForceFinish )
1335  realEnd = true;
1336 
1337  // TODO: Rollback doesn't work properly if fix-all isn't enabled and we are placing arcs,
1338  // so if we are, act as though we are in fix-all mode.
1339  if( !fixAll && l.ArcCount() )
1340  fixAll = true;
1341 
1342  // TODO: lastDirSeg will be calculated incorrectly if we end on an arc
1343  SEG lastDirSeg = ( !fixAll && l.SegmentCount() > 1 ) ? l.CSegment( -2 ) : l.CSegment( -1 );
1344  DIRECTION_45 d_last( lastDirSeg );
1345 
1346  int lastV;
1347 
1348  if( realEnd || m_placingVia || fixAll )
1349  lastV = l.SegmentCount();
1350  else
1351  lastV = std::max( 1, l.SegmentCount() - 1 );
1352 
1353  ARC arc;
1354  SEGMENT seg;
1355  LINKED_ITEM* lastItem = nullptr;
1356  int lastArc = -1;
1357 
1358  for( int i = 0; i < lastV; i++ )
1359  {
1360  ssize_t arcIndex = l.ArcIndex( i );
1361 
1362  if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
1363  {
1364  seg = SEGMENT( pl.CSegment( i ), m_currentNet );
1365  seg.SetWidth( pl.Width() );
1366  seg.SetLayer( m_currentLayer );
1367 
1368  std::unique_ptr<SEGMENT> sp = std::make_unique<SEGMENT>( seg );
1369  lastItem = sp.get();
1370 
1371  if( !m_lastNode->Add( std::move( sp ) ) )
1372  lastItem = nullptr;
1373  }
1374  else
1375  {
1376  if( arcIndex == lastArc )
1377  continue;
1378 
1379  arc = ARC( l.Arc( arcIndex ), m_currentNet );
1380  arc.SetWidth( pl.Width() );
1381  arc.SetLayer( m_currentLayer );
1382 
1383  std::unique_ptr<ARC> ap = std::make_unique<ARC>( arc );
1384  lastItem = ap.get();
1385 
1386  if( !m_lastNode->Add( std::move( ap ) ) )
1387  lastItem = nullptr;
1388 
1389  lastArc = arcIndex;
1390  }
1391  }
1392 
1393  if( pl.EndsWithVia() )
1394  m_lastNode->Add( Clone( pl.Via() ) );
1395 
1396  if( realEnd && lastItem )
1397  simplifyNewLine( m_lastNode, lastItem );
1398 
1399  if( !realEnd )
1400  {
1401  setInitialDirection( d_last );
1402  m_currentStart = ( m_placingVia || fixAll ) ? p_last : p_pre_last;
1403 
1404  VECTOR2I ps;
1405  if( m_tail.SegmentCount() )
1406  ps = m_tail.CPoint( 0 );
1407  else
1408  ps = m_p_start;
1409 
1411 
1412  m_startItem = nullptr;
1413  m_placingVia = false;
1415 
1418 
1419  m_head.Line().Clear();
1420  m_tail.Line().Clear();
1421  m_head.RemoveVia();
1422  m_tail.RemoveVia();
1425 
1426  m_shove->AddLockedSpringbackNode( m_currentNode );
1427 
1428  DIRECTION_45 lastSegDir = pl.EndsWithVia() ? DIRECTION_45::UNDEFINED : d_last;
1429 
1434 
1435  m_placementCorrect = true;
1436  }
1437  else
1438  {
1439  m_shove->AddLockedSpringbackNode( m_lastNode );
1440  m_placementCorrect = true;
1441  m_idle = true;
1442  }
1443 
1444  return realEnd;
1445 }
1446 
1447 
1449 {
1450  FIXED_TAIL::STAGE st;
1451 
1452  if ( !m_fixedTail.PopStage( st ) )
1453  return false;
1454 
1455  m_head.Line().Clear();
1456  m_tail.Line().Clear();
1457  m_startItem = nullptr;
1458  m_p_start = st.pts[0].p;
1459  m_direction = st.pts[0].direction;
1460  m_placingVia = st.pts[0].placingVias;
1461  m_currentNode = st.commit;
1462  m_currentLayer = st.pts[0].layer;
1465  m_head.RemoveVia();
1466  m_tail.RemoveVia();
1467 
1471 
1472  m_shove->RewindSpringbackTo( m_currentNode );
1473  m_shove->UnlockSpringbackNode( m_currentNode );
1474 
1475  if( Settings().Mode() == PNS::RM_Shove )
1476  {
1477  m_currentNode = m_shove->CurrentNode();
1479  }
1480 
1482 
1483  return true;
1484 }
1485 
1486 
1488 {
1489  return m_placementCorrect || m_fixedTail.StageCount() > 1;
1490 }
1491 
1492 
1494 {
1495  if( Settings().Mode() == PNS::RM_Shove )
1496  {
1497  m_shove->RewindToLastLockedNode();
1498  m_lastNode = m_shove->CurrentNode();
1500  }
1501 
1502  if( m_lastNode )
1504 
1505  m_lastNode = nullptr;
1506  m_currentNode = nullptr;
1507  return true;
1508 }
1509 
1510 
1511 void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest )
1512 {
1513  if( !aLatest.SegmentCount() )
1514  return;
1515 
1516  if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) )
1517  return;
1518 
1519  std::set<LINKED_ITEM *> toErase;
1520  aNode->Add( aLatest, true );
1521 
1522  for( int s = 0; s < aLatest.LinkCount(); s++ )
1523  {
1524  LINKED_ITEM* seg = aLatest.GetLink(s);
1525  LINE ourLine = aNode->AssembleLine( seg );
1526  JOINT a, b;
1527  std::vector<LINE> lines;
1528 
1529  aNode->FindLineEnds( ourLine, a, b );
1530 
1531  if( a == b )
1532  aNode->FindLineEnds( aLatest, a, b );
1533 
1534  aNode->FindLinesBetweenJoints( a, b, lines );
1535 
1536  int removedCount = 0;
1537  int total = 0;
1538 
1539  for( LINE& line : lines )
1540  {
1541  total++;
1542 
1543  if( !( line.ContainsLink( seg ) ) && line.SegmentCount() )
1544  {
1545  for( LINKED_ITEM* ss : line.Links() )
1546  toErase.insert( ss );
1547 
1548  removedCount++;
1549  }
1550  }
1551 
1552  wxLogTrace( wxT( "PNS" ), wxT( "total segs removed: %d/%d" ), removedCount, total );
1553  }
1554 
1555  for( LINKED_ITEM* s : toErase )
1556  aNode->Remove( s );
1557 
1558  aNode->Remove( aLatest );
1559 }
1560 
1561 
1563 {
1564  wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) );
1565 
1566  // Before we assemble the final line and run the optimizer, do a separate pass to clean up
1567  // colinear segments that exist on non-line-corner joints, as these will prevent proper assembly
1568  // of the line and won't get cleaned up by the optimizer.
1569  NODE::ITEM_VECTOR removed, added;
1570  aNode->GetUpdatedItems( removed, added );
1571 
1572  std::set<ITEM*> cleanup;
1573 
1574  auto processJoint =
1575  [&]( JOINT* aJoint, ITEM* aItem )
1576  {
1577  if( !aJoint || aJoint->IsLineCorner() )
1578  return;
1579 
1580  SEG refSeg = static_cast<SEGMENT*>( aItem )->Seg();
1581 
1582  NODE::ITEM_VECTOR toRemove;
1583 
1584  for( ITEM* neighbor : aJoint->Links() )
1585  {
1586  if( neighbor == aItem
1587  || !neighbor->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
1588  || !neighbor->LayersOverlap( aItem ) )
1589  {
1590  continue;
1591  }
1592 
1593  if( static_cast<SEGMENT*>( neighbor )->Width()
1594  != static_cast<SEGMENT*>( aItem )->Width() )
1595  {
1596  continue;
1597  }
1598 
1599  const SEG& testSeg = static_cast<SEGMENT*>( neighbor )->Seg();
1600 
1601  if( refSeg.Contains( testSeg ) )
1602  {
1603  JOINT* nA = aNode->FindJoint( neighbor->Anchor( 0 ), neighbor );
1604  JOINT* nB = aNode->FindJoint( neighbor->Anchor( 1 ), neighbor );
1605 
1606  if( ( nA == aJoint && nB->LinkCount() == 1 ) ||
1607  ( nB == aJoint && nA->LinkCount() == 1 ) )
1608  {
1609  cleanup.insert( neighbor );
1610  }
1611  }
1612  }
1613  };
1614 
1615  for( ITEM* item : added )
1616  {
1617  if( !item->OfKind( ITEM::SEGMENT_T ) || cleanup.count( item ) )
1618  continue;
1619 
1620  JOINT* jA = aNode->FindJoint( item->Anchor( 0 ), item );
1621  JOINT* jB = aNode->FindJoint( item->Anchor( 1 ), item );
1622 
1623  processJoint( jA, item );
1624  processJoint( jB, item );
1625  }
1626 
1627  for( ITEM* seg : cleanup )
1628  aNode->Remove( seg );
1629 
1630  // And now we can proceed with assembling the final line and optimizing it.
1631 
1632  LINE l = aNode->AssembleLine( aLatest );
1633 
1634  bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode );
1635 
1636  SHAPE_LINE_CHAIN simplified( l.CLine() );
1637 
1638  simplified.Simplify();
1639 
1640  if( optimized || simplified.PointCount() != l.PointCount() )
1641  {
1642  aNode->Remove( l );
1643  l.SetShape( simplified );
1644  aNode->Add( l );
1645  }
1646 }
1647 
1648 
1650 {
1651  m_sizes = aSizes;
1652 
1653  if( !m_idle )
1654  {
1655  // If the track width continues from an existing track, we don't want to change the width.
1656  // Disallow changing width after the first segment has been fixed because we don't want to
1657  // go back and rip up tracks or allow DRC errors
1659  && ( !m_startItem || m_startItem->Kind() != ITEM::SEGMENT_T ) ) )
1660  {
1664  }
1665 
1666  if( m_head.EndsWithVia() )
1667  {
1670  }
1671  }
1672 }
1673 
1674 
1676 {
1677  LINE current = Trace();
1678  SHAPE_LINE_CHAIN ratLine;
1679  TOPOLOGY topo( m_lastNode );
1680 
1681  if( topo.LeadingRatLine( &current, ratLine ) )
1682  m_router->GetInterface()->DisplayRatline( ratLine, 5 );
1683 }
1684 
1685 
1686 void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
1687 {
1688  m_orthoMode = aOrthoMode;
1689 }
1690 
1691 
1692 bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aForceNoVia )
1693 {
1694  SHAPE_LINE_CHAIN l;
1695  DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
1696 
1697  wxLogTrace( wxT( "PNS" ),
1698  wxT( "buildInitialLine: m_direction %s, guessedDir %s, tail points %d" ),
1699  m_direction.Format(), guessedDir.Format(), m_tail.PointCount() );
1700 
1702  // Rounded corners don't make sense when routing orthogonally (single track at a time)
1703  if( m_orthoMode )
1704  cornerMode = DIRECTION_45::CORNER_MODE::MITERED_45;
1705 
1706  if( m_p_start == aP )
1707  {
1708  l.Clear();
1709  }
1710  else
1711  {
1712  if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
1713  {
1714  l = SHAPE_LINE_CHAIN( { m_p_start, aP } );
1715  }
1716  else
1717  {
1718  if( !m_tail.PointCount() )
1719  l = guessedDir.BuildInitialTrace( m_p_start, aP, false, cornerMode );
1720  else
1721  l = m_direction.BuildInitialTrace( m_p_start, aP, false, cornerMode );
1722  }
1723 
1724  if( l.SegmentCount() > 1 && m_orthoMode )
1725  {
1726  VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) );
1727 
1728  l.Remove( -1, -1 );
1729  l.SetPoint( 1, newLast );
1730  }
1731  }
1732 
1733  aHead.SetLayer( m_currentLayer );
1734  aHead.SetShape( l );
1735 
1736  if( !m_placingVia || aForceNoVia )
1737  return true;
1738 
1739  VIA v( makeVia( aP ) );
1740  v.SetNet( aHead.Net() );
1741 
1742  if( Settings().Mode() == RM_MarkObstacles )
1743  {
1744  aHead.AppendVia( v );
1745  return true;
1746  }
1747 
1748  VECTOR2I force;
1749  VECTOR2I lead = aP - m_p_start;
1750 
1751  bool solidsOnly = ( Settings().Mode() != RM_Walkaround );
1752 
1753  if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
1754  {
1755  SHAPE_LINE_CHAIN line =
1756  guessedDir.BuildInitialTrace( m_p_start, aP + force, false, cornerMode );
1757  aHead = LINE( aHead, line );
1758 
1759  v.SetPos( v.Pos() + force );
1760  return true;
1761  }
1762 
1763  return false; // via placement unsuccessful
1764 }
1765 
1766 
1767 void LINE_PLACER::GetModifiedNets( std::vector<int>& aNets ) const
1768 {
1769  aNets.push_back( m_currentNet );
1770 }
1771 
1772 
1774 {
1775  m_world->KillChildren();
1776  return true;
1777 }
1778 
1779 
1780 FIXED_TAIL::FIXED_TAIL( int aLineCount )
1781 {
1782 
1783 }
1784 
1785 
1787 {
1788 
1789 }
1790 
1791 
1793 {
1794  m_stages.clear();
1795 }
1796 
1797 
1798 void FIXED_TAIL::AddStage( const VECTOR2I& aStart, int aLayer, bool placingVias,
1799  DIRECTION_45 direction, NODE* aNode )
1800 {
1801  STAGE st;
1802  FIX_POINT pt;
1803 
1804  pt.p = aStart;
1805  pt.layer = aLayer;
1806  pt.direction = direction;
1807  pt.placingVias = placingVias;
1808 
1809  st.pts.push_back(pt);
1810  st.commit = aNode;
1811 
1812  m_stages.push_back( st );
1813 }
1814 
1815 
1817 {
1818  if( !m_stages.size() )
1819  return false;
1820 
1821  aStage = m_stages.back();
1822 
1823  if( m_stages.size() > 1 )
1824  m_stages.pop_back();
1825 
1826  return true;
1827 }
1828 
1829 
1831 {
1832  return m_stages.size();
1833 }
1834 
1835 }
1836 
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
int Length() const
Return the length (this).
Definition: seg.h:350
Represent an intersection between two line segments.
const SHAPE_LINE_CHAIN & CLine() const
Definition: pns_line.h:137
int PrevShape(int aPointIndex) const
#define PNS_DBGN(dbg, method)
Base class for PNS router board items.
Definition: pns_item.h:55
ROUTER * Router() const
Return current router settings.
Definition: pns_algo_base.h:54
const SHAPE_ARC & Arc(size_t aArc) const
long long int Length() const
Return length of the line chain in Euclidean metric.
int Split(const VECTOR2I &aP)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
void SetViaDiameter(int aDiameter)
Definition: pns_line.h:198
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
const ITEM_SET Traces() override
Return the complete routed line, as a single-member ITEM_SET.
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).
LINE m_last_head
Most recent successful (non-colliding) head.
VECTOR2I v2(1, 0)
Test suite for KiCad math code.
std::vector< INTERSECTION > INTERSECTIONS
Keep the router "world" - i.e.
Definition: pns_node.h:148
NODE * m_lastNode
Postprocessed world state (including marked collisions & removed loops)
void RemoveShape(int aPointIndex)
Remove the shape at the given index from the line chain.
DIRECTION_45::CORNER_MODE GetCornerMode() const
void routeStep(const VECTOR2I &aP)
Perform a single routing algorithm step, for the end point aP.
int SegmentCount() const
Definition: pns_line.h:139
SHAPE_LINE_CHAIN & Simplify(bool aRemoveColinear=true)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
std::vector< STAGE > m_stages
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
NODE * m_world
pointer to world to search colliding items
void SetLayer(int aLayer)
Definition: pns_item.h:157
const SEG CSegment(int aIdx) const
Set line width.
Definition: pns_line.h:146
const std::string Format() const
Format the direction in a human readable word.
Definition: direction45.h:129
bool Is45Degree() const
Print out all linked segments.
Definition: pns_line.cpp:537
void CommitRouting()
Definition: pns_router.cpp:710
void removeLoops(NODE *aNode, LINE &aLatest)
Searches aNode for traces concurrent to aLatest and removes them.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex=-1) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
Simplify pad-pad and pad-via connections if possible.
WALKAROUND_STATUS statusCw
void simplifyNewLine(NODE *aNode, LINKED_ITEM *aLatest)
Assemble a line starting from segment or arc aLatest, removes collinear segments and redundant vertic...
VECTOR2I::extended_type ecoord
Definition: seg.h:43
virtual LOGGER * Logger()
void FindLineEnds(const LINE &aLine, JOINT &aA, JOINT &aB)
Destroy all child nodes. Applicable only to the root node.
Definition: pns_node.cpp:1042
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.
LINE m_head
the volatile part of the track from the previously analyzed point to the current routing destination
bool Overlaps(const LAYER_RANGE &aOther) const
Definition: pns_layerset.h:67
void RemoveVia()
Definition: pns_line.h:194
bool rhWalkOnly(const VECTOR2I &aP, LINE &aNewHead)
Route step shove mode.
SIZES_SETTINGS m_sizes
std::vector< FIX_POINT > pts
int FindLinesBetweenJoints(const JOINT &aA, const JOINT &aB, std::vector< LINE > &aLines)
Find the joints corresponding to the ends of line aLine.
Definition: pns_node.cpp:1049
bool PopStage(STAGE &aStage)
WALKAROUND_STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
FIXED_TAIL(int aLineCount=1)
const LINE Trace() const
Return the complete routed line.
class PAD, a pad in a footprint
Definition: typeinfo.h:89
void AppendVia(const VIA &aVia)
Definition: pns_line.cpp:1046
bool IsLineCorner(bool aAllowLockedSegs=false) const
Checks if a joint connects two segments of the same net, layer, and width.
Definition: pns_joint.h:103
virtual void DisplayRatline(const SHAPE_LINE_CHAIN &aRatline, int aColor=-1)=0
size_t ArcCount() const
const SEG & Seg() const
Definition: pns_segment.h:84
bool reduceTail(const VECTOR2I &aEnd)
Attempt to reduce the number of segments in the tail by trying to replace a certain number of latest ...
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition: pns_line.h:60
int Depth() const
Definition: pns_node.h:194
const std::vector< SHAPE_ARC > & CArcs() const
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...
VECTOR2< int > VECTOR2I
Definition: vector2d.h:622
int PointCount() const
Return the number of points (vertices) in this line chain.
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition: pns_node.cpp:138
const VECTOR2I & Pos() const
Definition: pns_via.h:99
int PointCount() const
Definition: pns_line.h:140
bool RemoveLoops() const
Enable/disable loop (redundant track) removal.
bool EndsWithVia() const
Definition: pns_line.h:191
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Remove(ARC *aArc)
Remove an item from this branch.
Definition: pns_node.cpp:850
void SetWidth(int aWidth)
Return line width.
Definition: pns_line.h:149
void SetNet(int aNet)
Definition: pns_item.h:151
const VECTOR2I & CPoint(int aIdx) const
Definition: pns_line.h:145
VIATYPE ViaType() const
bool handlePullback()
Deal with pull-back: reduces the tail if head trace is moved backwards wrs to the current tail direct...
void SetCollisionMask(int aMask)
bool PushoutForce(NODE *aNode, const VECTOR2I &aDirection, VECTOR2I &aForce, bool aSolidsOnly=true, int aMaxIterations=10)
Definition: pns_via.cpp:32
LINE_PLACER(ROUTER *aRouter)
bool FollowMouse() const
Return true if smoothing segments during dragging is enabled.
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition: pns_line.h:126
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
ITEM_SET & Links()
Definition: pns_joint.h:243
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
Definition: direction45.h:181
Definition: color4d.h:67
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
void SetApproachCursor(bool aEnabled, const VECTOR2I &aPos)
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:42
void UpdateSizes(const SIZES_SETTINGS &aSizes) override
Perform on-the-fly update of the width, via diameter & drill size from a settings class.
static constexpr extended_type ECOORD_MAX
Definition: vector2d.h:79
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:301
ROUTER * m_router
Definition: pns_algo_base.h:87
static bool cursorDistMinimum(const SHAPE_LINE_CHAIN &aL, const VECTOR2I &aCursor, double lengthThreshold, int &theDist, VECTOR2I &aNearest)
DIRECTION_45 m_initial_direction
routing direction for new traces
void KillChildren()
Definition: pns_node.cpp:1424
bool IsArcSegment(size_t aSegment) const
void SetWorld(NODE *aNode)
bool LeadingRatLine(const LINE *aTrack, SHAPE_LINE_CHAIN &aRatLine)
LINE m_tail
routing "tail": part of the track that has been already fixed due to collisions with obstacles
void setInitialDirection(const DIRECTION_45 &aDirection)
Set preferred direction of the very first track segment to be laid.
void SetWidth(int aWidth) override
Definition: pns_segment.h:74
int Net() const
Definition: pns_item.h:152
Represent route directions & corner angles in a 45-degree metric.
Definition: direction45.h:36
bool SplitAdjacentSegments(NODE *aNode, ITEM *aSeg, const VECTOR2I &aP)
Check if point aP lies on segment aSeg.
PNS_OPTIMIZATION_EFFORT OptimizerEffort() const
Set the optimizer effort. Bigger means cleaner traces, but slower routing.
bool TrackWidthIsExplicit() const
std::unique_ptr< SHOVE > m_shove
The shove engine.
DIRECTION_45 m_direction
current routing direction
Definition: color4d.h:57
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
void initPlacement()
Initialize placement of a new line with given parameters.
CORNER_MODE
Corner modes.
Definition: direction45.h:66
#define PNS_DBG(dbg, method,...)
DEBUG_DECORATOR * Dbg() const
Definition: pns_algo_base.h:78
void AddTrailPoint(const VECTOR2I &aP)
const LINE ClipToNearestObstacle(NODE *aNode) const
Clip the line to a given range of vertices.
Definition: pns_line.cpp:566
void updateLeadingRatLine()
Draw the "leading" rats nest line, which connects the end of currently routed track and the nearest y...
PNS_MODE Mode() const
Set the routing mode.
void SetWidth(int aWidth) override
Definition: pns_arc.h:82
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:260
bool routeHead(const VECTOR2I &aP, LINE &aNewHead)
Compute the head trace between the current start point (m_p_start) and point aP, starting with direct...
Definition: color4d.h:58
void SetSolidsOnly(bool aSolidsOnly)
int ShapeCount() const
Return the number of shapes (line segments or arcs) in this line chain.
bool HasPlacedAnything() const override
MOUSE_TRAIL_TRACER m_mouseTrailTracer
bool handleSelfIntersections()
Check if the head of the track intersects its tail.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
VECTOR2I closestProjectedPoint(const SHAPE_LINE_CHAIN &line, const VECTOR2I &p)
bool route(const VECTOR2I &aP)
Re-route the current track to point aP.
Reduce corner cost iteratively.
Definition: pns_optimizer.h:99
H/V/45 with mitered corners (default)
Definition: direction45.h:68
void GetModifiedNets(std::vector< int > &aNets) const override
Function GetModifiedNets.
FIXED_TAIL m_fixedTail
int SegmentCount() const
Return the number of segments in this line chain.
bool CommitPlacement() override
H/V/45 with filleted corners.
Definition: direction45.h:69
bool IsPtOnArc(size_t aPtIndex) const
bool rhMarkObstacles(const VECTOR2I &aP, LINE &aNewHead)
void AddStage(const VECTOR2I &aStart, int aLayer, bool placingVias, DIRECTION_45 direction, NODE *aNode)
JOINT * FindJoint(const VECTOR2I &aPos, int aLayer, int aNet)
Search for a joint at a given position, layer and belonging to given net.
Definition: pns_node.cpp:1154
bool rhShoveOnly(const VECTOR2I &aP, LINE &aNewHead)
Route step mark obstacles mode.
SHAPE_LINE_CHAIN & Line()
Definition: pns_line.h:136
void SetPos(const VECTOR2I &aPos)
Definition: pns_via.h:101
SEG Reversed() const
Returns the center point of the line.
Definition: seg.h:380
Definition: color4d.h:48
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Definition: seg.h:40
void SetViaDrill(int aDrill)
Definition: pns_line.h:199
NODE * m_currentNode
Current world state.
int ShapeCount() const
Return the aIdx-th point of the line.
Definition: pns_line.h:142
Definition: color4d.h:56
AngleType
Represent kind of angle formed by vectors heading in two DIRECTION_45s.
Definition: direction45.h:77
void setWorld(NODE *aWorld)
Set the board to route.
bool optimizeTailHeadTransition()
Try to reduce the corner count of the most recent part of tail/head by merging obtuse/collinear segme...
NODE * CurrentNode(bool aLoopsRemoved=false) const override
Return the most recent world state.
Ignore collisions, mark obstacles.
bool ToggleVia(bool aEnabled) override
Enable/disable a via at the end of currently routed trace.
const char * name
Definition: DXF_plotter.cpp:56
Reduce corner cost by merging obtuse segments.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
void FlipPosture() override
Toggle the current posture (straight/diagonal) of the trace head.
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition: pns_item.h:271
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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:463
Definition: layer_ids.h:71
WALKAROUND_STATUS statusCcw
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
bool HasLoops() const
Definition: pns_line.cpp:1137
void SetDefaultDirections(DIRECTION_45 aInitDirection, DIRECTION_45 aLastSegDir)
VECTOR2I A
Definition: seg.h:48
bool OfKind(int aKindMask) const
Return true if the item's type matches the mask aKindMask.
Definition: pns_item.h:138
double WalkaroundHugLengthThreshold() const
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition: pns_line.h:156
bool SetLayer(int aLayer) override
Set the current routing layer.
Only walk around.
DIRECTION_45 GetPosture(const VECTOR2I &aP)
void GetUpdatedItems(ITEM_VECTOR &aRemoved, ITEM_VECTOR &aAdded)
Return the list of items removed and added in this branch with respect to the root branch.
Definition: pns_node.cpp:1357
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
Definition: pns_algo_base.h:73
PnsKind Kind() const
Return the type (kind) of the item.
Definition: pns_item.h:130
Perform various optimizations of the lines being routed, attempting to make the lines shorter and les...
Definition: pns_optimizer.h:94
void Clear()
Remove all points from the line chain.
OPT< OBSTACLE > OPT_OBSTACLE
Definition: pns_node.h:151
VECTOR2I m_p_start
current routing start (end of tail, beginning of head)
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
Definition: pns_node.cpp:960
void SetWorld(NODE *aNode)
Merge co-linear segments.
bool mergeHead()
Moves "established" segments from the head to the tail if certain conditions are met.
OPT_OBSTACLE NearestObstacle(const LINE *aLine, int aKindMask=ITEM::ANY_T, const std::set< ITEM * > *aRestrictedSet=nullptr, bool aUseClearanceEpsilon=true)
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition: pns_node.cpp:297
void SetMouseDisabled(bool aDisabled=true)
Disables the mouse-trail portion of the posture solver; leaving only the manual posture switch and th...
int LinkCount(int aMask=-1) const
Definition: pns_joint.h:248
void SetEffortLevel(int aEffort)
void Clear()
Definition: pns_line.cpp:1257
void SetOrthoMode(bool aOrthoMode) override
Function SetOrthoMode()
bool Start(const VECTOR2I &aP, ITEM *aStartItem) override
Start routing a single track at point aP, taking item aStartItem as anchor (unless NULL).
NODE * Owner() const
Return the owner of this item, or NULL if there's none.
Definition: pns_item.h:171
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
bool AbortPlacement() override
const VIA & Via() const
Definition: pns_line.h:196
const VIA makeVia(const VECTOR2I &aP)
int StageCount() const
Push and Shove diff pair dimensions (gap) settings dialog.
void SetLogger(LOGGER *aLogger)
Definition: pns_algo_base.h:65
NODE * GetWorld() const
Definition: pns_router.h:154
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition: pns_node.cpp:651
ROUTER_IFACE * GetInterface() const
Definition: pns_router.h:208
void SetIterationLimit(const int aIterLimit)
bool UnfixRoute() override
Represent a contiguous set of PCB layers.
Definition: pns_layerset.h:31
const LAYER_RANGE & Layers() const
Definition: pns_item.h:154
Reroute pad exits.
std::vector< ITEM * > ITEM_VECTOR
Definition: pns_node.h:152
bool buildInitialLine(const VECTOR2I &aP, LINE &aHead, bool aForceNoVia=false)
int CountCorners(int aAngles) const
Definition: pns_line.cpp:135
void SetBlockingObstacle(ITEM *aObstacle)
Definition: pns_line.h:205
bool Contains(const SEG &aSeg) const
Definition: seg.h:331
VECTOR2I B
Definition: seg.h:49