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( "PNS", "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( "PNS", "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( "PNS", "Merge failed: not enough head segs." );
342  return false;
343  }
344 
345  if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
346  {
347  wxLogTrace( "PNS", "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( "PNS", "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( "PNS", "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( "PNS", "Posture: init %s, last seg %s", initialDir.Format(), lastSegDir.Format() );
1154 
1159  m_mouseTrailTracer.SetMouseDisabled( !Settings().GetAutoPosture() );
1160 
1161  NODE *n;
1162 
1163  if ( Settings().Mode() == PNS::RM_Shove )
1164  n = m_shove->CurrentNode();
1165  else
1166  n = m_currentNode;
1167 
1169 
1170  return true;
1171 }
1172 
1173 
1175 {
1176  m_idle = false;
1177 
1178  m_head.Line().Clear();
1179  m_tail.Line().Clear();
1186  m_head.RemoveVia();
1187  m_tail.RemoveVia();
1188 
1191 
1192  NODE* world = Router()->GetWorld();
1193 
1194  world->KillChildren();
1195  NODE* rootNode = world->Branch();
1196 
1198 
1199  setWorld( rootNode );
1200 
1201  wxLogTrace( "PNS", "world %p, intitial-direction %s layer %d",
1202  m_world,
1203  m_direction.Format().c_str(),
1204  m_currentLayer );
1205 
1206  m_lastNode = nullptr;
1208 
1209  m_shove = std::make_unique<SHOVE>( m_world->Branch(), Router() );
1210 }
1211 
1212 
1213 bool LINE_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1214 {
1215  LINE current;
1216  VECTOR2I p = aP;
1217  int eiDepth = -1;
1218 
1219  if( aEndItem && aEndItem->Owner() )
1220  eiDepth = static_cast<NODE*>( aEndItem->Owner() )->Depth();
1221 
1222  if( m_lastNode )
1223  {
1224  delete m_lastNode;
1225  m_lastNode = nullptr;
1226  }
1227 
1228  m_endItem = aEndItem;
1229 
1230  bool reachesEnd = route( p );
1231 
1232  current = Trace();
1233 
1234  if( !current.PointCount() )
1236  else
1237  m_currentEnd = current.CLine().CPoint( -1 );
1238 
1239  NODE* latestNode = m_currentNode;
1240  m_lastNode = latestNode->Branch();
1241 
1242  if( reachesEnd
1243  && eiDepth >= 0
1244  && aEndItem && latestNode->Depth() >= eiDepth
1245  && current.SegmentCount() )
1246  {
1247  SplitAdjacentSegments( m_lastNode, aEndItem, current.CPoint( -1 ) );
1248 
1249  if( Settings().RemoveLoops() )
1250  removeLoops( m_lastNode, current );
1251  }
1252 
1255  return true;
1256 }
1257 
1258 
1259 bool LINE_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1260 {
1261  bool fixAll = Settings().GetFixAllSegments();
1262  bool realEnd = false;
1263 
1264  LINE pl = Trace();
1265 
1266  if( Settings().Mode() == RM_MarkObstacles )
1267  {
1268  // Mark Obstacles is sort of a half-manual, half-automated mode in which the
1269  // user has more responsibility and authority.
1270 
1271  if( aEndItem )
1272  {
1273  // The user has indicated a connection should be made. If either the trace or
1274  // endItem is net-less, then allow the connection by adopting the net of the other.
1275  if( m_currentNet <= 0 )
1276  {
1277  m_currentNet = aEndItem->Net();
1278  pl.SetNet( m_currentNet );
1279  }
1280  else if (aEndItem->Net() <= 0 )
1281  {
1282  aEndItem->SetNet( m_currentNet );
1283  }
1284  }
1285 
1286  // Collisions still prevent fixing unless "Allow DRC violations" is checked
1287  if( !Settings().AllowDRCViolations() && m_world->CheckColliding( &pl ) )
1288  return false;
1289  }
1290 
1291  const SHAPE_LINE_CHAIN& l = pl.CLine();
1292 
1293  if( !l.SegmentCount() )
1294  {
1295  if( m_lastNode )
1296  {
1297  // Do a final optimization to the stored state
1298  NODE::ITEM_VECTOR removed, added;
1299  m_lastNode->GetUpdatedItems( removed, added );
1300 
1301  if( !added.empty() && added.back()->Kind() == ITEM::SEGMENT_T )
1302  simplifyNewLine( m_lastNode, static_cast<SEGMENT*>( added.back() ) );
1303  }
1304 
1305  // Nothing to commit if we have an empty line
1306  if( !pl.EndsWithVia() )
1307  return false;
1308 
1311  if( m_lastNode )
1312  {
1313  m_lastNode->Add( Clone( pl.Via() ) );
1314  m_shove->AddLockedSpringbackNode( m_lastNode );
1315  }
1316 
1317  m_currentNode = nullptr;
1318 
1319  m_idle = true;
1320  m_placementCorrect = true;
1321  return true;
1322  }
1323 
1324  VECTOR2I p_pre_last = l.CPoint( -1 );
1325  const VECTOR2I p_last = l.CPoint( -1 );
1326 
1327  if( l.PointCount() > 2 )
1328  p_pre_last = l.CPoint( -2 );
1329 
1330  if( aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->Net() )
1331  realEnd = true;
1332 
1333  if( aForceFinish )
1334  realEnd = true;
1335 
1336  // TODO: Rollback doesn't work properly if fix-all isn't enabled and we are placing arcs,
1337  // so if we are, act as though we are in fix-all mode.
1338  if( !fixAll && l.ArcCount() )
1339  fixAll = true;
1340 
1341  // TODO: lastDirSeg will be calculated incorrectly if we end on an arc
1342  SEG lastDirSeg = ( !fixAll && l.SegmentCount() > 1 ) ? l.CSegment( -2 ) : l.CSegment( -1 );
1343  DIRECTION_45 d_last( lastDirSeg );
1344 
1345  int lastV;
1346 
1347  if( realEnd || m_placingVia || fixAll )
1348  lastV = l.SegmentCount();
1349  else
1350  lastV = std::max( 1, l.SegmentCount() - 1 );
1351 
1352  ARC arc;
1353  SEGMENT seg;
1354  LINKED_ITEM* lastItem = nullptr;
1355  int lastArc = -1;
1356 
1357  for( int i = 0; i < lastV; i++ )
1358  {
1359  ssize_t arcIndex = l.ArcIndex( i );
1360 
1361  if( arcIndex < 0 || ( lastArc >= 0 && i == lastV - 1 && !l.IsPtOnArc( lastV ) ) )
1362  {
1363  seg = SEGMENT( pl.CSegment( i ), m_currentNet );
1364  seg.SetWidth( pl.Width() );
1365  seg.SetLayer( m_currentLayer );
1366 
1367  std::unique_ptr<SEGMENT> sp = std::make_unique<SEGMENT>( seg );
1368  lastItem = sp.get();
1369 
1370  if( !m_lastNode->Add( std::move( sp ) ) )
1371  lastItem = nullptr;
1372  }
1373  else
1374  {
1375  if( arcIndex == lastArc )
1376  continue;
1377 
1378  arc = ARC( l.Arc( arcIndex ), m_currentNet );
1379  arc.SetWidth( pl.Width() );
1380  arc.SetLayer( m_currentLayer );
1381 
1382  std::unique_ptr<ARC> ap = std::make_unique<ARC>( arc );
1383  lastItem = ap.get();
1384 
1385  if( !m_lastNode->Add( std::move( ap ) ) )
1386  lastItem = nullptr;
1387 
1388  lastArc = arcIndex;
1389  }
1390  }
1391 
1392  if( pl.EndsWithVia() )
1393  m_lastNode->Add( Clone( pl.Via() ) );
1394 
1395  if( realEnd && lastItem )
1396  simplifyNewLine( m_lastNode, lastItem );
1397 
1398  if( !realEnd )
1399  {
1400  setInitialDirection( d_last );
1401  m_currentStart = ( m_placingVia || fixAll ) ? p_last : p_pre_last;
1402 
1403  VECTOR2I ps;
1404  if( m_tail.SegmentCount() )
1405  ps = m_tail.CPoint( 0 );
1406  else
1407  ps = m_p_start;
1408 
1410 
1411  m_startItem = nullptr;
1412  m_placingVia = false;
1414 
1417 
1418  m_head.Line().Clear();
1419  m_tail.Line().Clear();
1420  m_head.RemoveVia();
1421  m_tail.RemoveVia();
1424 
1425  m_shove->AddLockedSpringbackNode( m_currentNode );
1426 
1427  DIRECTION_45 lastSegDir = pl.EndsWithVia() ? DIRECTION_45::UNDEFINED : d_last;
1428 
1433 
1434  m_placementCorrect = true;
1435  }
1436  else
1437  {
1438  m_shove->AddLockedSpringbackNode( m_lastNode );
1439  m_placementCorrect = true;
1440  m_idle = true;
1441  }
1442 
1443  return realEnd;
1444 }
1445 
1446 
1448 {
1449  FIXED_TAIL::STAGE st;
1450 
1451  if ( !m_fixedTail.PopStage( st ) )
1452  return false;
1453 
1454  m_head.Line().Clear();
1455  m_tail.Line().Clear();
1456  m_startItem = nullptr;
1457  m_p_start = st.pts[0].p;
1458  m_direction = st.pts[0].direction;
1459  m_placingVia = st.pts[0].placingVias;
1460  m_currentNode = st.commit;
1461  m_currentLayer = st.pts[0].layer;
1464  m_head.RemoveVia();
1465  m_tail.RemoveVia();
1466 
1470 
1471  m_shove->RewindSpringbackTo( m_currentNode );
1472  m_shove->UnlockSpringbackNode( m_currentNode );
1473 
1474  if( Settings().Mode() == PNS::RM_Shove )
1475  {
1476  m_currentNode = m_shove->CurrentNode();
1478  }
1479 
1481 
1482  return true;
1483 }
1484 
1485 
1487 {
1488  return m_placementCorrect || m_fixedTail.StageCount() > 1;
1489 }
1490 
1491 
1493 {
1494  if( Settings().Mode() == PNS::RM_Shove )
1495  {
1496  m_shove->RewindToLastLockedNode();
1497  m_lastNode = m_shove->CurrentNode();
1499  }
1500 
1501  if( m_lastNode )
1503 
1504  m_lastNode = nullptr;
1505  m_currentNode = nullptr;
1506  return true;
1507 }
1508 
1509 
1510 void LINE_PLACER::removeLoops( NODE* aNode, LINE& aLatest )
1511 {
1512  if( !aLatest.SegmentCount() )
1513  return;
1514 
1515  if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) )
1516  return;
1517 
1518  std::set<LINKED_ITEM *> toErase;
1519  aNode->Add( aLatest, true );
1520 
1521  for( int s = 0; s < aLatest.LinkCount(); s++ )
1522  {
1523  LINKED_ITEM* seg = aLatest.GetLink(s);
1524  LINE ourLine = aNode->AssembleLine( seg );
1525  JOINT a, b;
1526  std::vector<LINE> lines;
1527 
1528  aNode->FindLineEnds( ourLine, a, b );
1529 
1530  if( a == b )
1531  aNode->FindLineEnds( aLatest, a, b );
1532 
1533  aNode->FindLinesBetweenJoints( a, b, lines );
1534 
1535  int removedCount = 0;
1536  int total = 0;
1537 
1538  for( LINE& line : lines )
1539  {
1540  total++;
1541 
1542  if( !( line.ContainsLink( seg ) ) && line.SegmentCount() )
1543  {
1544  for( LINKED_ITEM* ss : line.Links() )
1545  toErase.insert( ss );
1546 
1547  removedCount++;
1548  }
1549  }
1550 
1551  wxLogTrace( "PNS", "total segs removed: %d/%d", removedCount, total );
1552  }
1553 
1554  for( LINKED_ITEM* s : toErase )
1555  aNode->Remove( s );
1556 
1557  aNode->Remove( aLatest );
1558 }
1559 
1560 
1562 {
1563  wxASSERT( aLatest->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) );
1564 
1565  // Before we assemble the final line and run the optimizer, do a separate pass to clean up
1566  // colinear segments that exist on non-line-corner joints, as these will prevent proper assembly
1567  // of the line and won't get cleaned up by the optimizer.
1568  NODE::ITEM_VECTOR removed, added;
1569  aNode->GetUpdatedItems( removed, added );
1570 
1571  std::set<ITEM*> cleanup;
1572 
1573  auto processJoint =
1574  [&]( JOINT* aJoint, ITEM* aItem )
1575  {
1576  if( !aJoint || aJoint->IsLineCorner() )
1577  return;
1578 
1579  SEG refSeg = static_cast<SEGMENT*>( aItem )->Seg();
1580 
1581  NODE::ITEM_VECTOR toRemove;
1582 
1583  for( ITEM* neighbor : aJoint->Links() )
1584  {
1585  if( neighbor == aItem
1586  || !neighbor->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
1587  || !neighbor->LayersOverlap( aItem ) )
1588  {
1589  continue;
1590  }
1591 
1592  if( static_cast<SEGMENT*>( neighbor )->Width()
1593  != static_cast<SEGMENT*>( aItem )->Width() )
1594  {
1595  continue;
1596  }
1597 
1598  const SEG& testSeg = static_cast<SEGMENT*>( neighbor )->Seg();
1599 
1600  if( refSeg.Contains( testSeg ) )
1601  {
1602  JOINT* nA = aNode->FindJoint( neighbor->Anchor( 0 ), neighbor );
1603  JOINT* nB = aNode->FindJoint( neighbor->Anchor( 1 ), neighbor );
1604 
1605  if( ( nA == aJoint && nB->LinkCount() == 1 ) ||
1606  ( nB == aJoint && nA->LinkCount() == 1 ) )
1607  {
1608  cleanup.insert( neighbor );
1609  }
1610  }
1611  }
1612  };
1613 
1614  for( ITEM* item : added )
1615  {
1616  if( !item->OfKind( ITEM::SEGMENT_T ) || cleanup.count( item ) )
1617  continue;
1618 
1619  JOINT* jA = aNode->FindJoint( item->Anchor( 0 ), item );
1620  JOINT* jB = aNode->FindJoint( item->Anchor( 1 ), item );
1621 
1622  processJoint( jA, item );
1623  processJoint( jB, item );
1624  }
1625 
1626  for( ITEM* seg : cleanup )
1627  aNode->Remove( seg );
1628 
1629  // And now we can proceed with assembling the final line and optimizing it.
1630 
1631  LINE l = aNode->AssembleLine( aLatest );
1632 
1633  bool optimized = OPTIMIZER::Optimize( &l, OPTIMIZER::MERGE_COLINEAR, aNode );
1634 
1635  SHAPE_LINE_CHAIN simplified( l.CLine() );
1636 
1637  simplified.Simplify();
1638 
1639  if( optimized || simplified.PointCount() != l.PointCount() )
1640  {
1641  aNode->Remove( l );
1642  l.SetShape( simplified );
1643  aNode->Add( l );
1644  }
1645 }
1646 
1647 
1649 {
1650  m_sizes = aSizes;
1651 
1652  if( !m_idle )
1653  {
1654  // If the track width continues from an existing track, we don't want to change the width.
1655  // Disallow changing width after the first segment has been fixed because we don't want to
1656  // go back and rip up tracks or allow DRC errors
1658  && ( !m_startItem || m_startItem->Kind() != ITEM::SEGMENT_T ) ) )
1659  {
1663  }
1664 
1665  if( m_head.EndsWithVia() )
1666  {
1669  }
1670  }
1671 }
1672 
1673 
1675 {
1676  LINE current = Trace();
1677  SHAPE_LINE_CHAIN ratLine;
1678  TOPOLOGY topo( m_lastNode );
1679 
1680  if( topo.LeadingRatLine( &current, ratLine ) )
1681  m_router->GetInterface()->DisplayRatline( ratLine, 5 );
1682 }
1683 
1684 
1685 void LINE_PLACER::SetOrthoMode( bool aOrthoMode )
1686 {
1687  m_orthoMode = aOrthoMode;
1688 }
1689 
1690 
1691 bool LINE_PLACER::buildInitialLine( const VECTOR2I& aP, LINE& aHead, bool aForceNoVia )
1692 {
1693  SHAPE_LINE_CHAIN l;
1694  DIRECTION_45 guessedDir = m_mouseTrailTracer.GetPosture( aP );
1695 
1696  wxLogTrace( "PNS", "buildInitialLine: m_direction %s, guessedDir %s, tail points %d",
1697  m_direction.Format(), guessedDir.Format(), m_tail.PointCount() );
1698 
1700  // Rounded corners don't make sense when routing orthogonally (single track at a time)
1701  if( m_orthoMode )
1702  cornerMode = DIRECTION_45::CORNER_MODE::MITERED_45;
1703 
1704  if( m_p_start == aP )
1705  {
1706  l.Clear();
1707  }
1708  else
1709  {
1710  if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
1711  {
1712  l = SHAPE_LINE_CHAIN( { m_p_start, aP } );
1713  }
1714  else
1715  {
1716  if( !m_tail.PointCount() )
1717  l = guessedDir.BuildInitialTrace( m_p_start, aP, false, cornerMode );
1718  else
1719  l = m_direction.BuildInitialTrace( m_p_start, aP, false, cornerMode );
1720  }
1721 
1722  if( l.SegmentCount() > 1 && m_orthoMode )
1723  {
1724  VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) );
1725 
1726  l.Remove( -1, -1 );
1727  l.SetPoint( 1, newLast );
1728  }
1729  }
1730 
1731  aHead.SetLayer( m_currentLayer );
1732  aHead.SetShape( l );
1733 
1734  if( !m_placingVia || aForceNoVia )
1735  return true;
1736 
1737  VIA v( makeVia( aP ) );
1738  v.SetNet( aHead.Net() );
1739 
1740  if( Settings().Mode() == RM_MarkObstacles )
1741  {
1742  aHead.AppendVia( v );
1743  return true;
1744  }
1745 
1746  VECTOR2I force;
1747  VECTOR2I lead = aP - m_p_start;
1748 
1749  bool solidsOnly = ( Settings().Mode() != RM_Walkaround );
1750 
1751  if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
1752  {
1753  SHAPE_LINE_CHAIN line =
1754  guessedDir.BuildInitialTrace( m_p_start, aP + force, false, cornerMode );
1755  aHead = LINE( aHead, line );
1756 
1757  v.SetPos( v.Pos() + force );
1758  return true;
1759  }
1760 
1761  return false; // via placement unsuccessful
1762 }
1763 
1764 
1765 void LINE_PLACER::GetModifiedNets( std::vector<int>& aNets ) const
1766 {
1767  aNets.push_back( m_currentNet );
1768 }
1769 
1770 
1772 {
1773  m_world->KillChildren();
1774  return true;
1775 }
1776 
1777 
1778 FIXED_TAIL::FIXED_TAIL( int aLineCount )
1779 {
1780 
1781 }
1782 
1783 
1785 {
1786 
1787 }
1788 
1789 
1791 {
1792  m_stages.clear();
1793 }
1794 
1795 
1796 void FIXED_TAIL::AddStage( const VECTOR2I& aStart, int aLayer, bool placingVias,
1797  DIRECTION_45 direction, NODE* aNode )
1798 {
1799  STAGE st;
1800  FIX_POINT pt;
1801 
1802  pt.p = aStart;
1803  pt.layer = aLayer;
1804  pt.direction = direction;
1805  pt.placingVias = placingVias;
1806 
1807  st.pts.push_back(pt);
1808  st.commit = aNode;
1809 
1810  m_stages.push_back( st );
1811 }
1812 
1813 
1815 {
1816  if( !m_stages.size() )
1817  return false;
1818 
1819  aStage = m_stages.back();
1820 
1821  if( m_stages.size() > 1 )
1822  m_stages.pop_back();
1823 
1824  return true;
1825 }
1826 
1827 
1829 {
1830  return m_stages.size();
1831 }
1832 
1833 }
1834 
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:146
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:1029
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:1036
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:191
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:837
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:228
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:268
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:1406
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:227
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)
OPT_OBSTACLE NearestObstacle(const LINE *aLine, int aKindMask=ITEM::ANY_T, const std::set< ITEM * > *aRestrictedSet=nullptr)
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
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:1141
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:451
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:1339
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:149
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:947
void SetWorld(NODE *aNode)
Merge co-linear segments.
bool mergeHead()
Moves "established" segments from the head to the tail if certain conditions are met.
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:233
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:639
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:150
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