KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_multi_dragger.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 * Author: Tomasz Wlostowski <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "pns_multi_dragger.h"
22
23#include <core/typeinfo.h>
24
25#include "pns_router.h"
26#include "pns_debug_decorator.h"
27#include "pns_walkaround.h"
28#include "pns_shove.h"
29#include "pns_diff_pair.h"
30#include "pns_utils.h"
31
32namespace PNS
33{
34
36{
37 m_world = nullptr;
38 m_lastNode = nullptr;
39}
40
41
45
46
47std::shared_ptr<DIFF_PAIR> MULTI_DRAGGER::tryAssembleDiffPair( ITEM* aStart )
48{
49 auto rr = Router()->GetRuleResolver();
50 TOPOLOGY topo( m_world );
51
52 if( rr->DpCoupledNet( aStart->Net() ) )
53 {
54 DIFF_PAIR dp;
55 if( topo.AssembleDiffPair( aStart, dp ) )
56 {
57 PNS_DBG( Dbg(), Message, wxString::Format( wxT("LC1 %d/%d"), dp.PLine().LinkCount(), dp.NLine().LinkCount() ) );
58
59 return std::make_shared<DIFF_PAIR>( dp );
60 }
61 }
62
63 return nullptr;
64}
65
66
68 DIFF_PAIR& aReconstructedDP, int& aLeaderSegmentN,
69 int& aLeaderSegmentP )
70{
71 auto resolver = Router()->GetRuleResolver();
72 CONSTRAINT gapConstraint;
73 std::optional<MINOPTMAX<int>> gapValue;
74
75
76 if( aOrigDP.PLine().LinkCount() <= 0 )
77 return false;
78
79 if( aOrigDP.NLine().LinkCount() <= 0 )
80 return false;
81
82 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP, aOrigDP.PLine().Links()[0], aOrigDP.NLine().Links()[0],
83 aOrigDP.Layer(), &gapConstraint ) )
84 {
85 gapValue = gapConstraint.m_Value;
86 }
87
89 aOrigDP.CoupledSegmentPairs( csv, true, gapValue );
90
91
92 int n = 0;
93 int leaderIndex = -1;
94
95 PNS_DBG( Dbg(), Message, wxString::Format( wxT("Coupled pairs: %d"), (int) csv.size() ) );
96
97
98 for( auto sp : csv )
99 {
100 if( sp.linkP == aAnchorItem || sp.linkN == aAnchorItem )
101 {
102 leaderIndex = n;
103 }
104 n++;
105 }
106
107 if ( leaderIndex < 0 )
108 return false;
109
110 auto nearestCoupledSegmentPair = [&]( const SEG aRefSeg ) -> int
111 {
112 SEG::ecoord minDist = std::numeric_limits<SEG::ecoord>::max();
113 int nearestIdx = -1;
114
115 for( size_t i = 0; i < csv.size(); i++ )
116 {
117 auto distP = csv[i].coupledP.SquaredDistance( aRefSeg );
118 auto distN = csv[i].coupledN.SquaredDistance( aRefSeg );
119
120 if( distP < minDist )
121 {
122 minDist = distP;
123 nearestIdx = i;
124 }
125 if( distN < minDist )
126 {
127 minDist = distN;
128 nearestIdx = i;
129 }
130 }
131
132 return nearestIdx;
133 };
134
135 if( leaderIndex < 0 )
136 {
137 const auto leadSegment = dyn_cast<PNS::SEGMENT*>( aAnchorItem );
138 if( leadSegment )
139 {
140 leaderIndex = nearestCoupledSegmentPair( leadSegment->Seg() );
141 }
142 }
143
144 auto areParallel = [&]( const PNS::DIFF_PAIR::COUPLED_SEGMENTS& cs,
145 const PNS::DIFF_PAIR::COUPLED_SEGMENTS& ref ) -> bool
146 {
147 constexpr int threshold = DIFF_PAIR::DP_PARALLELITY_THRESHOLD;
148 if( cs.coupledN.ApproxCollinear( ref.coupledN, threshold )
149 && cs.coupledP.ApproxCollinear( ref.coupledP, threshold ) )
150 return true;
151
152 return false;
153 };
154
155
156 if( leaderIndex >= 0 )
157 {
158 int start = leaderIndex, end = leaderIndex;
159
160 for( int i = leaderIndex - 1; i >= 0; i-- )
161 {
162 if( areParallel( csv[i], csv[leaderIndex] ) )
163 start = i;
164 else
165 break;
166 }
167 for( size_t i = leaderIndex + 1; i < csv.size(); i++ )
168 {
169 if( areParallel( csv[i], csv[leaderIndex] ) )
170 end = i;
171 else
172 break;
173 }
174
175
176 auto cs_start = csv[start];
177 auto cs_end = csv[end];
178
179 auto longestN = LongestCoveringSegment( cs_start.parentN, cs_end.parentN );
180 auto longestP = LongestCoveringSegment( cs_start.parentP, cs_end.parentP );
181
182
183 int p_start = aOrigDP.CP().Find( longestP.A );
184 int p_end = aOrigDP.CP().Find( longestP.B );
185 int n_start = aOrigDP.CN().Find( longestN.A );
186 int n_end = aOrigDP.CN().Find( longestN.B );
187
188 SHAPE_LINE_CHAIN shape_p( aOrigDP.CP() );
189 SHAPE_LINE_CHAIN shape_n( aOrigDP.CN() );
190 shape_p.Replace( p_start, p_end, SHAPE_LINE_CHAIN( { longestP.A, longestP.B } ) );
191 shape_n.Replace( n_start, n_end, SHAPE_LINE_CHAIN( { longestN.A, longestN.B } ) );
192
193
194 PNS_DBG( Dbg(), Message,
195 wxString::Format( wxT( "reconstruct p %d/%d n %d/%d SE %d %d"), p_start, p_end, n_start, n_end, start, end ) );
196
197 PNS_DBG( Dbg(), AddShape, &shape_p, GREEN, 10000, "rec-ps" );
198
199
200 aReconstructedDP = aOrigDP;
201 aReconstructedDP.ClearLinks();
202 aReconstructedDP.SetShape( shape_p, shape_n );
203
204 PNS_DBG( Dbg(), AddItem, &aReconstructedDP.PLine(), GREEN, 10000, "rec-ps2" );
205
206
207 aLeaderSegmentN = n_start;
208 aLeaderSegmentP = p_start;
209 return true;
210 }
211
212 return false;
213}
214
215
216// here we initialize everything that's needed for multidrag. this means:
217bool MULTI_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
218{
219 ITEM_SET leaders( aPrimitives );
220
221 m_lastNode = nullptr;
222 m_dragStatus = false;
223 m_dragStartPoint = aP;
224
225 // check if the initial ("leader") primitive set is empty...
226 if( aPrimitives.Empty() )
227 return false;
228
229 m_mdragLines.clear();
230
231 // find all LINEs to be dragged. Indicate the LINE that contains the point (aP)
232 // as the "primary line", the multidrag algo will place all other lines in such way
233 // that the cursor position lies on the primary line.
234 for( ITEM* pitem : aPrimitives.Items() )
235 {
236 LINKED_ITEM* litem = static_cast<LINKED_ITEM*>( pitem );
237 bool redundant = false;
238 for( auto& l : m_mdragLines )
239 {
240 if( l.assembledOrigLine.ContainsLink( litem ) )
241 {
242 l.originalLeaders.push_back( litem );
243 redundant = true;
244 break;
245 }
246 }
247
248 // we can possibly have multiple SEGMENTs in aPrimitives that belong to the same line.
249 // We reject these.
250 if ( redundant )
251 continue;
252
253 if( Settings().GetKeepDPCouplingWhenDragging() )
254 {
255 auto diffPair = tryAssembleDiffPair( pitem );
256
257 if( diffPair )
258 {
259 DIFF_PAIR reconstructed;
260 int leaderIndexN, leaderIndexP;
261
262 bool reconstructOK =
263 reconstructOriginalDpCoupling( *diffPair.get(), pitem, reconstructed, leaderIndexN, leaderIndexP );
264
265 PNS_DBG( Dbg(), Message,
266 wxString::Format( wxT( "DP assembled OK, leaders: %d/%d reconstructed=%d lc %d %d" ), leaderIndexP,
267 leaderIndexN, reconstructOK ? 1 : 0,
268 diffPair->PLine().LinkCount(), diffPair->NLine().LinkCount()) );
269
270
271
272 if( reconstructOK )
273 {
274 MDRAG_LINE l_p;
275 l_p.assembledDiffPair = diffPair;
276 l_p.assembledOrigLine = diffPair->PLine();
277 l_p.originalLine = reconstructed.PLine();
278 if( diffPair->PLine().ContainsLink( litem ) )
279 {
280 l_p.originalLeaders.push_back( litem );
281 }
282 //leaders.Add( diffPair->PLine().GetLink( leaderIndexP) );
283 l_p.leaderSegIndex = leaderIndexP;
284 l_p.isDraggable = true;
285 l_p.mdragIndex = static_cast<int>( m_mdragLines.size() );
286 PNS_DBG( Dbg(), AddShape, l_p.originalLine.CSegment( leaderIndexP ), GREEN, 10000, "leader-p" );
287 m_mdragLines.push_back( ( l_p ) );
288 MDRAG_LINE l_n;
289 l_n.assembledDiffPair = diffPair;
290 l_n.assembledOrigLine = diffPair->NLine();
291 l_n.leaderSegIndex = leaderIndexN;
292 l_n.originalLine = reconstructed.NLine();
293 if( diffPair->NLine().ContainsLink( litem ) )
294 {
295 l_n.originalLeaders.push_back( litem );
296 }
297 //leaders.Add( diffPair->NLine().GetLink( leaderIndexN ) );
298 l_n.isDraggable = true;
299 l_n.mdragIndex = static_cast<int>( m_mdragLines.size() );
300 PNS_DBG( Dbg(), AddShape, l_n.originalLine.CSegment( leaderIndexN ), GREEN, 10000, "leader-n" );
301
302 m_mdragLines.push_back( ( l_n ) );
303 continue;
304 }
305 }
306 }
307
308 MDRAG_LINE l;
309 l.assembledOrigLine = m_world->AssembleLine( litem );
311 l.originalLeaders.push_back( litem );
312 l.isDraggable = true;
313 l.leaderSegIndex = -1;
314 l.mdragIndex = static_cast<int>( m_mdragLines.size() );
315 m_mdragLines.push_back( std::move( l ) );
316 }
317
318 PNS_DBG( Dbg(), Message, wxString::Format( wxT("draglines :%d"), (int) m_mdragLines.size() ) );
319
320 bool anyStrictCornersFound = false;
321 bool anyStrictMidSegsFound = false;
322
323 for( auto& l : m_mdragLines )
324 {
325 const int thr = l.originalLine.Width() / 2;
326
327 const VECTOR2I& origFirst = l.originalLine.CLine().CPoint( 0 );
328 const int distFirst = ( origFirst - aP ).EuclideanNorm();
329
330 const VECTOR2I& origLast = l.originalLine.CLine().CLastPoint();
331 const int distLast = ( origLast - aP ).EuclideanNorm();
332
333 PNS_DBG( Dbg(), AddItem, &l.originalLine, GREEN, 10000, "orig-m" );
334
335 l.cornerDistance = std::min( distFirst, distLast );
336
337 bool takeFirst = false;
338 auto ilast = aPrimitives.FindVertex( origLast );
339 auto ifirst = aPrimitives.FindVertex( origFirst );
340
341 if( ilast && ifirst )
342 takeFirst = distFirst < distLast;
343 else if( ilast )
344 takeFirst = false;
345 else if( ifirst )
346 takeFirst = true;
347
348 if( ifirst || ilast )
349 {
350 if( takeFirst )
351 {
352 l.cornerIsLast = false;
353 l.leaderSegIndex = 0;
354 l.cornerDistance = distFirst;
355 l.isCorner = true;
356
357 if( distFirst <= thr )
358 {
359 l.isStrict = true;
360 l.cornerDistance = 0;
361 }
362 }
363 else
364 {
365 l.cornerIsLast = true;
366 l.leaderSegIndex = l.originalLine.SegmentCount() - 1;
367 l.cornerDistance = distLast;
368 l.isCorner = true;
369
370 if( distLast <= thr )
371 {
372 l.isStrict = true;
373 l.cornerDistance = 0;
374 }
375 }
376 }
377
378
379 for( int lidx = 0; lidx < (int) l.originalLine.SegmentCount(); lidx++ )
380 {
381 const SEG& origSeg = l.originalLine.CSegment( lidx );
382 ITEM* origLink = leaders.FindSegment( origSeg );
383
384 if( l.leaderSegIndex < 0 && !origLink )
385 continue;
386
387 int d = origSeg.Distance( aP );
388
389 if( origLink )
390 {
391 l.isMidSeg = true;
392 l.midSeg = origSeg;
393 }
394
395 if( l.leaderSegIndex < 0 )
396 l.leaderSegIndex = lidx;
397
398 if( lidx == l.leaderSegIndex )
399 {
400 l.midSeg = origSeg;
401 l.leaderSegDistance = d + thr;
402
403 if( d < thr && !l.isStrict )
404 {
405 l.isCorner = false;
406 l.isStrict = true;
407 l.leaderSegDistance = 0;
408 }
409 }
410 }
411
412 if( l.isStrict )
413 {
414 anyStrictCornersFound |= l.isCorner;
415 anyStrictMidSegsFound |= !l.isCorner;
416 }
417 }
418
419 if( anyStrictCornersFound )
421 else if (anyStrictMidSegsFound )
423 else
424 {
425 int minLeadSegDist = std::numeric_limits<int>::max();
426 int minCornerDist = std::numeric_limits<int>::max();
427 MDRAG_LINE *bestSeg = nullptr;
428 MDRAG_LINE *bestCorner = nullptr;
429
430 for( auto& l : m_mdragLines )
431 {
432 if( l.cornerDistance < minCornerDist )
433 {
434 minCornerDist = l.cornerDistance;
435 bestCorner = &l;
436 }
437 if( l.leaderSegDistance < minLeadSegDist )
438 {
439 minLeadSegDist = l.leaderSegDistance;
440 bestSeg = &l;
441 }
442 }
443
444 if( bestCorner && bestSeg )
445 {
446 if( minCornerDist < minLeadSegDist )
447 {
449 bestCorner->isPrimaryLine = true;
450 }
451 else
452 {
454 bestSeg->isPrimaryLine = true;
455 }
456 }
457 else if ( bestCorner )
458 {
460 bestCorner->isPrimaryLine = true;
461 }
462 else if ( bestSeg )
463 {
465 bestSeg->isPrimaryLine = true;
466 }
467 else return false; // can it really happen?
468 }
469
470 if( m_dragMode == DM_CORNER )
471 {
472 for( auto& l : m_mdragLines )
473 {
474 // make sure the corner to drag is the last one
475 if ( !l.cornerIsLast )
476 {
477 l.originalLine.Reverse();
478 l.cornerIsLast = true;
479 }
480 // and if it's connected (non-trivial fanout), disregard it
481
482 const JOINT* jt = m_world->FindJoint( l.originalLine.CLastPoint(), &l.originalLine );
483
484 if( !jt )
485 {
487 break;
488 }
489
490 if( !jt->IsTrivialEndpoint() )
491 {
492 m_dragMode = DM_SEGMENT; // fallback to segment mode if non-trivial endpoints found
493 }
494 }
495 }
496
497 for( auto& l : m_mdragLines )
498 {
499 if( (anyStrictCornersFound || anyStrictMidSegsFound) && l.isStrict )
500 {
501 l.isPrimaryLine = true;
502 break;
503 }
504 }
505
506 m_origDraggedItems = aPrimitives;
507
508 if( Settings().Mode() == RM_Shove )
509 {
510 m_preShoveNode = m_world->Branch();
511
512 for( auto& l : m_mdragLines )
513 {
514 m_preShoveNode->Remove( l.assembledOrigLine );
515 }
516
517 m_shove.reset( new SHOVE( m_preShoveNode, Router() ) );
518 m_shove->SetLogger( Logger() );
519 m_shove->SetDebugDecorator( Dbg() );
520 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE | SHOVE::SHP_DONT_LOCK_ENDPOINTS );
521 }
522
523 return true;
524}
525
526
530
531
533{
534 return DM_CORNER;
535}
536
537bool clipToOtherLine( NODE* aNode, const LINE& aRef, LINE& aClipped )
538{
539 std::set<OBSTACLE> obstacles;
540 COLLISION_SEARCH_CONTEXT ctx( obstacles );
541
542 constexpr int clipLengthThreshold = 100;
543
544 //DEBUG_DECORATOR* dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
545
546 LINE l( aClipped );
547 SHAPE_LINE_CHAIN tightest;
548
549 bool didClip = false;
550 int curL = l.CLine().Length();
551 int step = curL / 2 - 1;
552
553 while( step > clipLengthThreshold )
554 {
555 SHAPE_LINE_CHAIN sl_tmp( aClipped.CLine() );
556 VECTOR2I pclip = sl_tmp.PointAlong( curL );
557 int idx = sl_tmp.Split( pclip );
558 sl_tmp = sl_tmp.Slice(0, idx);
559
560 l.SetShape( sl_tmp );
561
562 //PNS_DBG( dbg, 3int, pclip, WHITE, 500000, wxT(""));
563
564 if( l.Collide( &aRef, aNode, l.Layer(), &ctx ) )
565 {
566 didClip = true;
567 curL -= step;
568 step /= 2;
569 }
570 else
571 {
572 tightest = std::move( sl_tmp );
573
574 if( didClip )
575 {
576 curL += step;
577 step /= 2;
578 }
579 else
580 {
581 break;
582 }
583 }
584 }
585
586 aClipped.SetShape( tightest );
587
588 return didClip;
589}
590
591
592
593
594const std::vector<NET_HANDLE> MULTI_DRAGGER::CurrentNets() const
595{
596 std::set<NET_HANDLE> uniqueNets;
597 for( auto &l : m_mdragLines )
598 {
599 NET_HANDLE net = l.draggedLine.Net();
600 if( net )
601 uniqueNets.insert( net );
602 }
603
604 return std::vector<NET_HANDLE>( uniqueNets.begin(), uniqueNets.end() );
605}
606
607// this is what ultimately gets called when the user clicks/releases the mouse button
608// during drag.
609bool MULTI_DRAGGER::FixRoute( bool aForceCommit )
610{
611 NODE* node = CurrentNode();
612
613 if( node )
614 {
615 // last drag status is OK?
616 if( !m_dragStatus && !Settings().AllowDRCViolations() )
617 return false;
618
619 // commit the current world state
620 Router()->CommitRouting( node );
621 return true;
622 }
623
624 return false;
625}
626
627bool MULTI_DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
628{
629 WALKAROUND walkaround( aNode, Router() );
630 walkaround.SetSolidsOnly( false );
631 walkaround.SetDebugDecorator( Dbg() );
632 walkaround.SetLogger( Logger() );
633 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
634 walkaround.SetLengthLimit( true, 3.0 );
636
637 aWalk = aOrig;
638
639 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
640
642 {
643 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
644 return true;
645 }
646
647 return false;
648}
649
651{
652 const SEG origLeader = aLine.preDragLine.CSegment( aLine.leaderSegIndex );
653 const DIRECTION_45 origLeaderDir( origLeader );
654
655 for ( int i = 0; i < aLine.draggedLine.SegmentCount(); i++ )
656 {
657 const SEG& curSeg = aLine.draggedLine.CSegment(i);
658 const DIRECTION_45 curDir( curSeg );
659
660 auto ip = curSeg.IntersectLines( m_guide );
661 PNS_DBG(Dbg(), Message, wxString::Format("s %d ip=%d c=%s o=%s", i, ip?1:0, curDir.Format(), origLeaderDir.Format() ));
662 if( ip && curSeg.Contains( *ip ) )
663 {
664 if( curDir == origLeaderDir || curDir == origLeaderDir.Opposite() )
665 return i;
666 }
667 }
668
669 return -1;
670}
671
672void MULTI_DRAGGER::restoreLeaderSegments( std::vector<MDRAG_LINE>& aCompletedLines )
673{
674 m_leaderSegments.clear();
675
676 for( auto& l : aCompletedLines )
677 {
678 if( l.dragOK )
679 {
680 if( m_dragMode == DM_CORNER )
681 {
682 if( l.draggedLine.LinkCount() > 0 )
683 {
684 m_leaderSegments.push_back(
685 static_cast<PNS::ITEM*>( l.draggedLine.GetLink( -1 ) ) );
686 }
687 }
688 else
689 {
690 int newLeaderIdx = findNewLeaderSegment( l );
691 if( newLeaderIdx >= 0 && newLeaderIdx < l.draggedLine.LinkCount() )
692 {
693 m_leaderSegments.push_back(
694 static_cast<PNS::ITEM*>( l.draggedLine.GetLink( newLeaderIdx ) ) );
695 }
696 }
697 }
698 }
699}
700
701bool MULTI_DRAGGER::multidragWalkaround( std::vector<MDRAG_LINE>& aCompletedLines )
702{
703 // fixme: rewrite using shared_ptr...
704 if( m_lastNode )
705 {
706 delete m_lastNode;
707 m_lastNode = nullptr;
708 }
709
710 auto compareDragStartDist = []( const MDRAG_LINE& a, const MDRAG_LINE& b ) -> int
711 {
712 return a.dragDist < b.dragDist;
713 };
714
715 std::sort( aCompletedLines.begin(), aCompletedLines.end(), compareDragStartDist );
716
717
718 NODE* preWalkNode = m_world->Branch();
719
720 for( auto& l : aCompletedLines )
721 {
722 PNS_DBG( Dbg(), AddItem, &l.assembledOrigLine, BLUE, 100000, wxString::Format("prewalk-remove lc=%d", l.originalLine.LinkCount() ) );
723 preWalkNode->Remove( l.assembledOrigLine );
724 }
725
726 struct WALK_STATE
727 {
728 NODE *node;
729 int totalLength = 0;
730 std::vector<LINE> postWalkLines;
731 bool fail = false;
732 };
733
734 WALK_STATE walkState[2];
735
736 for( int attempt = 0; attempt < 2; attempt++ )
737 {
738 WALK_STATE *state = &walkState[ attempt ];
739 state->node = preWalkNode->Branch();
740 state->postWalkLines.resize( aCompletedLines.size() );
741
742 for( int lidx = 0; lidx < (int) aCompletedLines.size(); lidx++ )
743 {
744 MDRAG_LINE& l = aCompletedLines[attempt ? aCompletedLines.size() - 1 - lidx : lidx];
745 LINE walk( l.draggedLine );
746
747 auto result = tryWalkaround( state->node, l.draggedLine, walk );
748
749 PNS_DBG( Dbg(), AddItem, &l.draggedLine, YELLOW, 100000, wxString::Format("dragged lidx=%d attempt=%d dd=%d isPrimary=%d", lidx, attempt, l.dragDist, l.isPrimaryLine?1:0) );
750 PNS_DBG( Dbg(), AddItem, &walk, BLUE, 100000, wxString::Format("walk lidx=%d attempt=%d", lidx, attempt) );
751
752 if( result )
753 {
754 state->node->Add( walk );
755 state->totalLength += walk.CLine().Length() - l.draggedLine.CLine().Length();
756 state->postWalkLines[lidx] = walk;
757 }
758 else
759 {
760 state->fail = true;
761 break;
762 }
763 }
764 }
765
766 std::optional<int> bestAttempt;
767
768 if( !walkState[0].fail && !walkState[1].fail )
769 {
770 if ( walkState[0].totalLength < walkState[1].totalLength )
771 {
772 bestAttempt = 0;
773 }
774 else
775 {
776 bestAttempt = 1;
777 }
778 }
779 else if ( !walkState[0].fail )
780 {
781 bestAttempt = 0;
782 }
783 else if ( !walkState[1].fail )
784 {
785 bestAttempt = 1;
786 }
787
788 if( !bestAttempt )
789 {
790 delete walkState[0].node;
791 delete walkState[1].node;
792 return false;
793 }
794 else
795 {
796 for( int lidx = 0; lidx < (int) aCompletedLines.size(); lidx++ )
797 {
798 aCompletedLines[lidx].draggedLine = walkState[ *bestAttempt ].postWalkLines[ lidx ];
799 m_draggedItems.Add( aCompletedLines[lidx].draggedLine );
800 }
801
802 m_lastNode = walkState[ *bestAttempt ].node;
803 delete walkState[1 - *bestAttempt].node;
804 }
805
806 // trip asan for qa
807 /*for( auto& l : aCompletedLines )
808 for( auto lnk : l.draggedLine.Links() )
809 assert( lnk->Parent() != reinterpret_cast<BOARD_ITEM*>( 0xdeadbeef ) );*/
810
811 restoreLeaderSegments( aCompletedLines );
812
813 return true;
814}
815
816
817bool MULTI_DRAGGER::multidragMarkObstacles( std::vector<MDRAG_LINE>& aCompletedLines )
818{
819
820// fixme: rewrite using shared_ptr...
821 if( m_lastNode )
822 {
823 delete m_lastNode;
824 m_lastNode = nullptr;
825 }
826
827 // m_lastNode contains the temporary (post-modification) state. Think of it as
828 // of an efficient undo buffer. We don't change the PCB directly, but a branch of it
829 // created below. We can then commit its state (applying the modifications to the host board
830 // by calling ROUTING::CommitRouting(m_lastNode) or simply discard it.
831 m_lastNode = m_world->Branch();
832
833
834 for( int l1 = 0; l1 < (int)aCompletedLines.size(); l1++ )
835 {
836 for( int l2 = l1 + 1; l2 < (int)aCompletedLines.size(); l2++ )
837 {
838 const auto& l1l = aCompletedLines[l1].draggedLine;
839 auto l2l = aCompletedLines[l2].draggedLine;
840
841 if( clipToOtherLine( m_lastNode, l1l, l2l ) )
842 aCompletedLines[l2].draggedLine = l2l;
843 }
844 }
845
846 for ( auto&l : aCompletedLines )
847 {
848 m_lastNode->Remove( l.assembledOrigLine );
849 m_lastNode->Add( l.draggedLine );
850 m_draggedItems.Add( l.draggedLine );
851 }
852
853 restoreLeaderSegments( aCompletedLines );
854
855 return true;
856}
857
858bool MULTI_DRAGGER::multidragShove( std::vector<MDRAG_LINE>& aCompletedLines )
859{
860 if( m_lastNode )
861 {
862 delete m_lastNode;
863 m_lastNode = nullptr;
864 }
865
866 if( !m_shove )
867 return false;
868
869 auto compareDragStartDist = []( const MDRAG_LINE& a, const MDRAG_LINE& b ) -> int
870 {
871 return a.dragDist < b.dragDist;
872 };
873
874 std::sort( aCompletedLines.begin(), aCompletedLines.end(), compareDragStartDist );
875
876 auto iface = Router()->GetInterface();
877
878 for( auto& l : m_mdragLines )
879 {
880 PNS_DBG( Dbg(), Message, wxString::Format ( wxT("net %-30s: isCorner %d isStrict %d c-Dist %-10d l-dist %-10d leadIndex %-2d CisLast %d dragDist %-10d"),
881 iface->GetNetName( l.draggedLine.Net() ),
882 (int) l.isCorner?1:0,
883 (int) l.isStrict?1:0,
884 (int) l.cornerDistance,
885 (int) l.leaderSegDistance,
886 (int) l.leaderSegIndex,
887 (int) l.cornerIsLast?1:0,
888 (int) l.dragDist ) );
889 }
890
891
892 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE );
893 m_shove->ClearHeads();
894
895 for( auto& l : aCompletedLines )
896 {
897 PNS_DBG( Dbg(), AddItem, &l.draggedLine, GREEN, 0, "dragged-line" );
898 m_shove->AddHeads( l.draggedLine, SHOVE::SHP_SHOVE | SHOVE::SHP_DONT_OPTIMIZE );
899 }
900
901 auto status = m_shove->Run();
902
903 m_lastNode = m_shove->CurrentNode()->Branch();
904
905 // Re-add any m_mdragLines that were removed from m_preShoveNode during Start() but
906 // are not part of aCompletedLines. Without this, lines that fail the drag angle check
907 // would be silently deleted from the board.
908 std::set<int> completedIndices;
909
910 for( const auto& cl : aCompletedLines )
911 completedIndices.insert( cl.mdragIndex );
912
913 for( const auto& ml : m_mdragLines )
914 {
915 if( completedIndices.find( ml.mdragIndex ) == completedIndices.end() )
916 {
917 LINE preserved( ml.originalLine );
918 preserved.ClearLinks();
919 m_lastNode->Add( preserved );
920 }
921 }
922
923 if( status == SHOVE::SH_OK )
924 {
925 for( int i = 0; i < (int) aCompletedLines.size(); i++ )
926 {
927 MDRAG_LINE&l = aCompletedLines[i];
928
929 if( m_shove->HeadsModified( i ) )
930 l.draggedLine = m_shove->GetModifiedHead( i );
931
932 // this should not be linked (assert in rt-test)
934
935 m_lastNode->Add( l.draggedLine );
936 }
937 }
938 else
939 {
940 return false;
941 }
942
943 restoreLeaderSegments( aCompletedLines );
944
945 return true;
946}
947
948// this is called every time the user moves the mouse while dragging a set of multiple tracks
950{
951 std::optional<LINE> primaryPreDrag, primaryDragged;
952 m_draggedItems.Clear();
953
954 SEG lastPreDrag;
955 DIRECTION_45 primaryDir;
956 VECTOR2I perp;
957
958 DIRECTION_45 primaryLastSegDir;
959 std::vector<MDRAG_LINE> completed;
960
961 auto tryPosture = [&] ( int aVariant ) -> bool
962 {
963 MDRAG_LINE* primaryLine = nullptr;
964
965 for( auto &l : m_mdragLines )
966 {
967 l.dragOK = false;
968 l.preDragLine = l.originalLine;
969 PNS_DBG( Dbg(), AddItem, &l.originalLine, GREEN, 30000, wxString::Format( wxT("original is-prim: %d"), l.isPrimaryLine?1:0) );
970
971 if( l.isPrimaryLine )
972 {
973
974 //PNS_DBG( Dbg(), AddItem, &l.originalLine, BLUE, 300000, wxT("mdrag-prim"));
975
976 // create a copy of the primary line (pre-drag and post-drag).
977 // the pre-drag version is necessary for NODE::Remove() to be able to
978 // find out the segments before modification by the multidrag algorithm
979 primaryDragged = l.originalLine;
980 primaryDragged->ClearLinks();
981 primaryPreDrag = l.originalLine;
982 primaryLine = &l;
983
984 }
985 }
986
987 if( aVariant == 1 && (primaryPreDrag->PointCount() > 2) )
988 {
989 primaryPreDrag->Line().Remove( -1 );
990 primaryDragged->Line().Remove( -1 );
991
992 for( auto&l : m_mdragLines )
993 {
994 l.preDragLine.Line().Remove(-1);
995 }
996 }
997
998 completed.clear();
999
1000 int snapThreshold = Settings().SmoothDraggedSegments() ? primaryDragged->Width() / 4 : 0;
1001
1002 if( m_dragMode == DM_CORNER )
1003 {
1004 // first, drag only the primary line
1005 PNS_DBG( Dbg(), AddPoint, primaryDragged->CLastPoint(), YELLOW, 600000, wxT("mdrag-sec"));
1006
1007 lastPreDrag = primaryPreDrag->CSegment( -1 );
1008 primaryDir = DIRECTION_45( lastPreDrag );
1009
1010 primaryDragged->SetSnapThreshhold( snapThreshold );
1011 primaryDragged->DragCorner( aP, primaryDragged->PointCount() - 1, false );
1012
1013
1014 if( primaryDragged->SegmentCount() > 0 )
1015 {
1016 SEG lastPrimDrag = primaryDragged->CSegment( -1 );
1017
1018 if ( aVariant == 2 )
1019 lastPrimDrag = lastPreDrag;
1020
1021 auto lastSeg = primaryDragged->CSegment( -1 );
1022 if( DIRECTION_45( lastSeg ) != primaryDir )
1023 {
1024 if( lastSeg.Length() < primaryDragged->Width() )
1025 {
1026 lastPrimDrag = lastPreDrag;
1027 }
1028 }
1029
1030 perp = (lastPrimDrag.B - lastPrimDrag.A).Perpendicular();
1031 primaryLastSegDir = DIRECTION_45( lastPrimDrag );
1032
1033
1034 PNS_DBG( Dbg(), AddItem, &(*primaryDragged), LIGHTGRAY, 100000, "prim" );
1035 PNS_DBG( Dbg(), AddShape, SEG(lastPrimDrag.B, lastPrimDrag.B + perp), LIGHTGRAY, 100000, wxString::Format("prim-perp-seg") );
1036 } else {
1037 return false;
1038 }
1039
1040
1041
1042// PNS_DBG( Dbg(), AddShape, &ll, LIGHTBLUE, 200000, "par" );
1043
1044 }
1045 else // DM_SEGMENT
1046 {
1047
1048 lastPreDrag = primaryDragged->CSegment( primaryLine->leaderSegIndex );
1049
1050 SHAPE_LINE_CHAIN ll2( { lastPreDrag.A, lastPreDrag.B } );
1051 PNS_DBG( Dbg(), AddShape, &ll2, LIGHTYELLOW, 300000, "primary-lead" );
1052
1053 primaryDragged->SetSnapThreshhold( snapThreshold );
1054 PNS_DBG( Dbg(), AddItem, &primaryDragged.value(), GREEN, 30000, "primary-orig" );
1055 primaryDragged->DragSegment( aP, primaryLine->leaderSegIndex );
1056 PNS_DBG( Dbg(), AddItem, &primaryDragged.value(), GREEN, 30000, "primary-dragged" );
1057
1058 perp = (primaryLine->midSeg.B - primaryLine->midSeg.A).Perpendicular();
1059 m_guide = SEG( aP, aP + perp );
1060 }
1061
1062
1064
1065
1066 // now drag all other lines
1067 for( auto& l : m_mdragLines )
1068 {
1069 //PNS_DBG( Dbg(), AddPoint, l.originalLine.CPoint( l.cornerIndex ), WHITE, 1000000, wxT("l-end"));
1070 if( l.isDraggable )
1071 {
1072 l.dragOK = false;
1073 PNS_DBG( Dbg(), AddItem, &l.originalLine, GREEN, 100000, wxT("mdrag-noprim"));
1074
1075 // reject nulls
1076 if( l.preDragLine.SegmentCount() >= 1 )
1077 {
1078
1079 //PNS_DBG( Dbg(), AddPoint, l.preDragLine.CPoint( l.cornerIndex ), YELLOW, 600000, wxT("mdrag-sec"));
1080
1081 // check the direction of the last segment of the line against the direction of
1082 // the last segment of the primary line (both before dragging) and perform drag
1083 // only when the directions are the same. The algorithm here is quite trival and
1084 // otherwise would produce really awkward results. There's of course a TON of
1085 // room for improvement here :-)
1086
1087 if( m_dragMode == DM_CORNER )
1088 {
1089 DIRECTION_45 parallelDir( l.preDragLine.CSegment( -1 ) );
1090
1091 auto leadAngle = primaryDir.Angle( parallelDir );
1092
1093 if( leadAngle == DIRECTION_45::ANG_OBTUSE
1094 || leadAngle == DIRECTION_45::ANG_RIGHT
1095 || leadAngle == DIRECTION_45::ANG_STRAIGHT )
1096 {
1097 // compute the distance between the primary line and the last point of
1098 // the currently processed line
1099 int dist = lastPreDrag.LineDistance( l.preDragLine.CLastPoint(), true );
1100
1101 // now project it on the perpendicular line we computed before
1102 auto projected = aP + perp.Resize( dist );
1103
1104
1105 LINE parallelDragged( l.preDragLine );
1106
1107 PNS_DBG( Dbg(), AddPoint, projected, LIGHTGRAY, 100000, "dragged-c" );
1108 PNS_DBG( Dbg(), AddPoint, parallelDragged.CLastPoint(), LIGHTGRAY, 100000, wxString::Format("orig-c cil %d", l.cornerIsLast?1:0) );
1109
1110 parallelDragged.ClearLinks();
1111 //m_lastNode->Remove( parallelDragged );
1112 // drag the non-primary line's end trying to place it at the projected point
1113 parallelDragged.DragCorner( projected, parallelDragged.PointCount() - 1,
1114 false, primaryLastSegDir );
1115
1116 PNS_DBG( Dbg(), AddPoint, projected, LIGHTYELLOW, 600000,
1117 wxT( "l-end" ) );
1118
1119 // DragCorner can collapse a very short secondary line to a single
1120 // point; skip it so the later CSegment(-1) check stays valid
1121 if( parallelDragged.SegmentCount() < 1 )
1122 continue;
1123
1124 l.dragOK = true;
1125
1126 if( !l.isPrimaryLine )
1127 {
1128 l.draggedLine = parallelDragged;
1129 completed.push_back( l );
1130 }
1131 }
1132 }
1133 else if ( m_dragMode == DM_SEGMENT )
1134 {
1135 SEG sdrag = l.midSeg;
1136 DIRECTION_45 refDir( lastPreDrag );
1137 DIRECTION_45 curDir( sdrag );
1138 auto ang = refDir.Angle( curDir );
1139
1140 PNS_DBG( Dbg(), AddShape, lastPreDrag, LIGHTBLUE, 30000, "lpd" );
1141 PNS_DBG( Dbg(), AddShape, sdrag, LIGHTRED, 30000, "sdrag" );
1142
1144 {
1145 int dist = lastPreDrag.LineDistance(
1146 l.preDragLine.CPoint( l.leaderSegIndex ), true );
1147 auto projected = aP + perp.Resize( dist );
1148
1149 SEG sperp( aP, aP + perp.Resize( 10000000 ) );
1150 VECTOR2I startProj = sperp.LineProject( m_dragStartPoint );
1151
1152 SHAPE_LINE_CHAIN ll( { sperp.A, sperp.B } );
1153
1154
1155 PNS_DBG( Dbg(), AddShape, &ll, LIGHTBLUE, 100000, "par" );
1156 SHAPE_LINE_CHAIN ll2( { sdrag.A, sdrag.B } );
1157 PNS_DBG( Dbg(), AddShape, &ll2, LIGHTBLUE, 100000, "sdrag" );
1158 VECTOR2I v = projected - startProj;
1159 l.dragDist = v.EuclideanNorm() * sign( v.Dot( perp ) );
1160 l.dragOK = true;
1161
1162 if( !l.isPrimaryLine )
1163 {
1164 l.draggedLine = l.preDragLine;
1165 l.draggedLine.ClearLinks();
1166 l.draggedLine.SetSnapThreshhold( snapThreshold );
1167 l.draggedLine.DragSegment( projected, l.leaderSegIndex, false );
1168 completed.push_back( l );
1169 PNS_DBG( Dbg(), AddItem, &l.draggedLine, LIGHTBLUE, 100000,
1170 "dragged" );
1171 }
1172
1173
1174 PNS_DBG( Dbg(), AddPoint, startProj, LIGHTBLUE, 400000,
1175 wxT( "startProj" ) );
1176 PNS_DBG( Dbg(), AddPoint, projected, LIGHTRED, 400000,
1177 wxString::Format( "pro dd=%d", l.dragDist ) );
1178 }
1179 }
1180 }
1181 }
1182
1183 if (l.isPrimaryLine)
1184 {
1185 l.draggedLine = *primaryDragged;
1186 l.dragOK = true;
1187 completed.push_back( l );
1188 }
1189 }
1190
1191 if( m_dragMode == DM_SEGMENT )
1192 return true;
1193 else
1194 {
1195 for ( const auto &l: completed )
1196 {
1197 if( !l.dragOK && aVariant < 2 )
1198 return false;
1199
1200 if( l.isPrimaryLine )
1201 continue;
1202
1203 // A degenerate dragged line has no last segment to read a direction from;
1204 // reject this posture so the next variant is attempted
1205 if( l.draggedLine.SegmentCount() < 1 )
1206 return false;
1207
1208 DIRECTION_45 lastDir ( l.draggedLine.CSegment(-1) );
1209
1210 if( lastDir != primaryLastSegDir )
1211 return false;
1212 }
1213 }
1214
1215 return true;
1216 };
1217
1218 bool res = false;
1219
1220 for( int variant = 0; variant < 3; variant++ )
1221 {
1222 res = tryPosture( variant );
1223
1224 if( res )
1225 break;
1226 }
1227
1228 switch( Settings().Mode() )
1229 {
1230 case RM_Walkaround:
1231 m_dragStatus = multidragWalkaround ( completed );
1232 break;
1233
1234 case RM_Shove:
1235 m_dragStatus = multidragShove ( completed );
1236 break;
1237
1238 case RM_MarkObstacles:
1239 m_dragStatus = multidragMarkObstacles( completed );
1240 break;
1241
1242
1243
1244 default:
1245 break;
1246 }
1247
1248 return m_dragStatus;
1249}
1250
1251
1253{
1254 return m_lastNode ? m_lastNode : m_world;
1255}
1256
1257
1259{
1260 return m_draggedItems;
1261}
1262
1263
1265{
1266 // fixme: should we care?
1267 return 0;
1268}
1269
1270
1271} // namespace PNS
Represent route directions & corner angles in a 45-degree metric.
Definition direction45.h:37
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
const std::string Format() const
Format the direction in a human readable word.
DIRECTION_45 Opposite() const
Return a direction opposite (180 degree) to (this).
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
void SetLogger(LOGGER *aLogger)
virtual LOGGER * Logger()
Return the logger object, allowing to dump geometry to a file.
ROUTER * Router() const
Return the instance of our router.
ROUTING_SETTINGS & Settings() const
Return current router settings.
DEBUG_DECORATOR * Dbg() const
Basic class for a differential pair.
const SHAPE_LINE_CHAIN & CN() const
std::vector< COUPLED_SEGMENTS > COUPLED_SEGMENTS_VEC
virtual void ClearLinks() override
Erase the linking information. Used to detach the line from the owning node.
void SetShape(const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN &aN, bool aSwapLanes=false)
static constexpr int DP_PARALLELITY_THRESHOLD
const SHAPE_LINE_CHAIN & CP() const
void CoupledSegmentPairs(COUPLED_SEGMENTS_VEC &aPairs, bool aUseGapConstraint=true, const std::optional< DP_GAP_CONSTRAINT > &aOverrideGapConstraint=std::optional< DP_GAP_CONSTRAINT >()) const
DRAG_ALGO(ROUTER *aRouter)
bool Empty() const
Definition pns_itemset.h:90
ITEM * FindSegment(const SEG &aSeg) const
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
ITEM * FindVertex(const VECTOR2I &aV) const
Base class for PNS router board items.
Definition pns_item.h:98
virtual NET_HANDLE Net() const
Definition pns_item.h:210
virtual int Layer() const
Definition pns_item.h:216
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition pns_item.cpp:305
A 2D point on a given set of layers and belonging to a certain net, that links together a number of b...
Definition pns_joint.h:43
bool IsTrivialEndpoint() const
Definition pns_joint.h:177
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Assign a shape to the line (a polyline/line chain).
Definition pns_line.h:135
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
const VECTOR2I & CLastPoint() const
Definition pns_line.h:155
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:884
int SegmentCount() const
Definition pns_line.h:148
int PointCount() const
Definition pns_line.h:149
const SEG CSegment(int aIdx) const
Definition pns_line.h:156
bool multidragShove(std::vector< MDRAG_LINE > &aCompletedLines)
bool multidragMarkObstacles(std::vector< MDRAG_LINE > &aCompletedLines)
std::vector< PNS::ITEM * > m_leaderSegments
virtual bool Start(const VECTOR2I &aP, ITEM_SET &aPrimitives) override
Function Start()
bool FixRoute(bool aForceCommit) override
Function FixRoute()
bool Drag(const VECTOR2I &aP) override
Function Drag()
int CurrentLayer() const override
Function CurrentLayer()
NODE * CurrentNode() const override
Function CurrentNode()
std::vector< MDRAG_LINE > m_mdragLines
std::shared_ptr< DIFF_PAIR > tryAssembleDiffPair(ITEM *aStart)
bool tryWalkaround(NODE *aNode, LINE &aOrig, LINE &aWalk)
void SetMode(PNS::DRAG_MODE aDragMode) override
int findNewLeaderSegment(const MDRAG_LINE &aLine) const
void restoreLeaderSegments(std::vector< MDRAG_LINE > &aCompletedLines)
bool multidragWalkaround(std::vector< MDRAG_LINE > &aCompletedLines)
const ITEM_SET Traces() override
Function Traces()
bool reconstructOriginalDpCoupling(DIFF_PAIR &aOrigDP, PNS::ITEM *aAnchorItem, DIFF_PAIR &aReconstructedDP, int &aLeaderSegmentN, int &aLeaderSegmentP)
const std::vector< NET_HANDLE > CurrentNets() const override
Function CurrentNets()
MULTI_DRAGGER(ROUTER *aRouter)
PNS::DRAG_MODE Mode() const override
std::unique_ptr< SHOVE > m_shove
Keep the router "world" - i.e.
Definition pns_node.h:244
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:157
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:991
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:261
void CommitRouting()
RULE_RESOLVER * GetRuleResolver() const
Definition pns_router.h:223
bool SmoothDraggedSegments() const
Return true if smoothing segments during dragging is enabled.
The actual Push and Shove algorithm.
Definition pns_shove.h:47
@ SHP_DONT_OPTIMIZE
Definition pns_shove.h:66
@ SHP_DONT_LOCK_ENDPOINTS
Definition pns_shove.h:67
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
void SetIterationLimit(const int aIterLimit)
void SetLengthLimit(bool aEnable, double aLengthExpansionFactor)
void SetSolidsOnly(bool aSolidsOnly)
STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
void SetAllowedPolicies(std::vector< WALK_POLICY > aPolicies)
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:710
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:217
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:759
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
bool Contains(const SEG &aSeg) const
Definition seg.h:321
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:651
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const VECTOR2I PointAlong(int aPathLength) const
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
long long int Length() const
Return length of the line chain in Euclidean metric.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:567
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
@ LIGHTBLUE
Definition color4d.h:58
@ BLUE
Definition color4d.h:52
@ LIGHTGRAY
Definition color4d.h:43
@ LIGHTYELLOW
Definition color4d.h:45
@ GREEN
Definition color4d.h:53
@ YELLOW
Definition color4d.h:63
@ LIGHTRED
Definition color4d.h:61
Push and Shove diff pair dimensions (gap) settings dialog.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
void * NET_HANDLE
Definition pns_item.h:55
DRAG_MODE
Definition pns_router.h:77
@ DM_CORNER
Definition pns_router.h:78
@ DM_SEGMENT
Definition pns_router.h:79
bool clipToOtherLine(NODE *aNode, const LINE &aRef, LINE &aClipped)
const SEG LongestCoveringSegment(const SEG &a, const SEG &b)
#define PNS_DBG(dbg, method,...)
An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between...
Definition pns_node.h:74
MINOPTMAX< int > m_Value
Definition pns_node.h:76
std::vector< PNS::ITEM * > originalLeaders
std::shared_ptr< DIFF_PAIR > assembledDiffPair
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
VECTOR3I res
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
constexpr int sign(T val)
Definition util.h:166
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708