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 along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "pns_multi_dragger.h"
22#include "pns_router.h"
23#include "pns_debug_decorator.h"
24#include "pns_walkaround.h"
25#include "pns_shove.h"
26
27namespace PNS
28{
29
31{
32 m_world = nullptr;
33 m_lastNode = nullptr;
34}
35
36
40
41// here we initialize everything that's needed for multidrag. this means:
42bool MULTI_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
43{
44 m_lastNode = nullptr;
45 m_dragStatus = false;
47
48 // check if the initial ("leader") primitive set is empty...
49 if( aPrimitives.Empty() )
50 return false;
51
52 m_mdragLines.clear();
53
54 // find all LINEs to be dragged. Indicate the LINE that contains the point (aP)
55 // as the "primary line", the multidrag algo will place all other lines in such way
56 // that the cursor position lies on the primary line.
57 for( ITEM* pitem : aPrimitives.Items() )
58 {
59 LINKED_ITEM* litem = static_cast<LINKED_ITEM*>( pitem );
60 bool redundant = false;
61 for( auto& l : m_mdragLines )
62 {
63 if( l.originalLine.ContainsLink( litem ) )
64 {
65 l.originalLeaders.push_back( litem );
66 redundant = true;
67 break;
68 }
69 }
70
71 // we can possibly have multiple SEGMENTs in aPrimitives that belong to the same line.
72 // We reject these.
73 if( !redundant )
74 {
75 MDRAG_LINE l;
76 l.originalLine = m_world->AssembleLine( litem );
77 l.originalLeaders.push_back( litem );
78 l.isDraggable = true;
79 m_mdragLines.push_back( std::move( l ) );
80 }
81 }
82
83 int n = 0;
84
85 bool anyStrictCornersFound = false;
86 bool anyStrictMidSegsFound = false;
87
88 for( auto& l : m_mdragLines )
89 {
90 const int thr = l.originalLine.Width() / 2;
91
92 const VECTOR2I& origFirst = l.originalLine.CLine().CPoint( 0 );
93 const int distFirst = ( origFirst - aP ).EuclideanNorm();
94
95 const VECTOR2I& origLast = l.originalLine.CLine().CLastPoint();
96 const int distLast = ( origLast - aP ).EuclideanNorm();
97
98 l.cornerDistance = std::min( distFirst, distLast );
99
100 bool takeFirst = false;
101 auto ilast = aPrimitives.FindVertex( origLast );
102 auto ifirst = aPrimitives.FindVertex( origFirst );
103
104 if( ilast && ifirst )
105 takeFirst = distFirst < distLast;
106 else if( ilast )
107 takeFirst = false;
108 else if( ifirst )
109 takeFirst = true;
110
111 if( ifirst || ilast )
112 {
113 if( takeFirst )
114 {
115 l.cornerIsLast = false;
116 l.leaderSegIndex = 0;
117 l.cornerDistance = distFirst;
118 l.isCorner = true;
119
120 if( distFirst <= thr )
121 {
122 l.isStrict = true;
123 l.cornerDistance = 0;
124 }
125 }
126 else
127 {
128 l.cornerIsLast = true;
129 l.leaderSegIndex = l.originalLine.SegmentCount() - 1;
130 l.cornerDistance = distLast;
131 l.isCorner = true;
132
133 if( distLast <= thr )
134 {
135 l.isStrict = true;
136 l.cornerDistance = 0;
137 }
138 }
139 }
140
141 const auto& links = l.originalLine.Links();
142
143 for( int lidx = 0; lidx < (int) links.size(); lidx++ )
144 {
145 if( auto lseg = dyn_cast<SEGMENT*>( links[lidx] ) )
146 {
147
148 if( !aPrimitives.Contains( lseg ) )
149 continue;
150
151 int d = lseg->Seg().Distance( aP );
152
153 l.midSeg = lseg->Seg();
154 l.isMidSeg = true;
155 l.leaderSegIndex = lidx;
156 l.leaderSegDistance = d + thr;
157
158 if( d < thr && !l.isStrict )
159 {
160 l.isCorner = false;
161 l.isStrict = true;
162 l.leaderSegDistance = 0;
163 }
164 }
165 }
166
167 if( l.isStrict )
168 {
169 anyStrictCornersFound |= l.isCorner;
170 anyStrictMidSegsFound |= !l.isCorner;
171 }
172 }
173
174 if( anyStrictCornersFound )
176 else if (anyStrictMidSegsFound )
178 else
179 {
180 int minLeadSegDist = std::numeric_limits<int>::max();
181 int minCornerDist = std::numeric_limits<int>::max();
182 MDRAG_LINE *bestSeg = nullptr;
183 MDRAG_LINE *bestCorner = nullptr;
184
185 for( auto& l : m_mdragLines )
186 {
187 if( l.cornerDistance < minCornerDist )
188 {
189 minCornerDist = l.cornerDistance;
190 bestCorner = &l;
191 }
192 if( l.leaderSegDistance < minLeadSegDist )
193 {
194 minLeadSegDist = l.leaderSegDistance;
195 bestSeg = &l;
196 }
197 }
198
199 if( bestCorner && bestSeg )
200 {
201 if( minCornerDist < minLeadSegDist )
202 {
204 bestCorner->isPrimaryLine = true;
205 }
206 else
207 {
209 bestSeg->isPrimaryLine = true;
210 }
211 }
212 else if ( bestCorner )
213 {
215 bestCorner->isPrimaryLine = true;
216 }
217 else if ( bestSeg )
218 {
220 bestSeg->isPrimaryLine = true;
221 }
222 else return false; // can it really happen?
223 }
224
225 if( m_dragMode == DM_CORNER )
226 {
227 for( auto& l : m_mdragLines )
228 {
229 // make sure the corner to drag is the last one
230 if ( !l.cornerIsLast )
231 {
232 l.originalLine.Reverse();
233 l.cornerIsLast = true;
234 }
235 // and if it's connected (non-trivial fanout), disregard it
236
237 const JOINT* jt = m_world->FindJoint( l.originalLine.CLastPoint(), &l.originalLine );
238
239 assert (jt != nullptr);
240
241 if( !jt->IsTrivialEndpoint() )
242 {
243 m_dragMode = DM_SEGMENT; // fallback to segment mode if non-trivial endpoints found
244 }
245 }
246 }
247
248 for( auto& l : m_mdragLines )
249 {
250 if( (anyStrictCornersFound || anyStrictMidSegsFound) && l.isStrict )
251 {
252 l.isPrimaryLine = true;
253 break;
254 }
255 }
256
257 m_origDraggedItems = aPrimitives;
258
259 if( Settings().Mode() == RM_Shove )
260 {
261 m_preShoveNode = m_world->Branch();
262
263 for( auto& l : m_mdragLines )
264 {
265 m_preShoveNode->Remove( l.originalLine );
266 }
267
268 m_shove.reset( new SHOVE( m_preShoveNode, Router() ) );
269 m_shove->SetLogger( Logger() );
270 m_shove->SetDebugDecorator( Dbg() );
271 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE | SHOVE::SHP_DONT_LOCK_ENDPOINTS );
272 }
273
274 return true;
275}
276
277
281
282
284{
285 return DM_CORNER;
286}
287
288bool clipToOtherLine( NODE* aNode, const LINE& aRef, LINE& aClipped )
289{
290 std::set<OBSTACLE> obstacles;
291 COLLISION_SEARCH_CONTEXT ctx( obstacles );
292
293 constexpr int clipLengthThreshold = 100;
294
295 //DEBUG_DECORATOR* dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
296
297 LINE l( aClipped );
298 SHAPE_LINE_CHAIN tightest;
299
300 bool didClip = false;
301 int curL = l.CLine().Length();
302 int step = curL / 2 - 1;
303
304 while( step > clipLengthThreshold )
305 {
306 SHAPE_LINE_CHAIN sl_tmp( aClipped.CLine() );
307 VECTOR2I pclip = sl_tmp.PointAlong( curL );
308 int idx = sl_tmp.Split( pclip );
309 sl_tmp = sl_tmp.Slice(0, idx);
310
311 l.SetShape( sl_tmp );
312
313 //PNS_DBG( dbg, 3int, pclip, WHITE, 500000, wxT(""));
314
315 if( l.Collide( &aRef, aNode, l.Layer(), &ctx ) )
316 {
317 didClip = true;
318 curL -= step;
319 step /= 2;
320 }
321 else
322 {
323 tightest = std::move( sl_tmp );
324
325 if( didClip )
326 {
327 curL += step;
328 step /= 2;
329 }
330 else
331 {
332 break;
333 }
334 }
335 }
336
337 aClipped.SetShape( tightest );
338
339 return didClip;
340}
341
342
343
344
345const std::vector<NET_HANDLE> MULTI_DRAGGER::CurrentNets() const
346{
347 std::set<NET_HANDLE> uniqueNets;
348 for( auto &l : m_mdragLines )
349 {
350 NET_HANDLE net = l.draggedLine.Net();
351 if( net )
352 uniqueNets.insert( net );
353 }
354
355 return std::vector<NET_HANDLE>( uniqueNets.begin(), uniqueNets.end() );
356}
357
358// this is what ultimately gets called when the user clicks/releases the mouse button
359// during drag.
360bool MULTI_DRAGGER::FixRoute( bool aForceCommit )
361{
362 NODE* node = CurrentNode();
363
364 if( node )
365 {
366 // last drag status is OK?
367 if( !m_dragStatus && !Settings().AllowDRCViolations() )
368 return false;
369
370 // commit the current world state
371 Router()->CommitRouting( node );
372 return true;
373 }
374
375 return false;
376}
377
378bool MULTI_DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
379{
380 WALKAROUND walkaround( aNode, Router() );
381 bool ok = false;
382 walkaround.SetSolidsOnly( false );
383 walkaround.SetDebugDecorator( Dbg() );
384 walkaround.SetLogger( Logger() );
385 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
386 walkaround.SetLengthLimit( true, 3.0 );
388
389 aWalk = aOrig;
390
391 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
392
394 {
395 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
396 return true;
397 }
398
399 return false;
400}
401
403{
404 const SEG origLeader = aLine.preDragLine.CSegment( aLine.leaderSegIndex );
405 const DIRECTION_45 origLeaderDir( origLeader );
406
407 for ( int i = 0; i < aLine.draggedLine.SegmentCount(); i++ )
408 {
409 const SEG& curSeg = aLine.draggedLine.CSegment(i);
410 const DIRECTION_45 curDir( curSeg );
411
412 auto ip = curSeg.IntersectLines( m_guide );
413 PNS_DBG(Dbg(), Message, wxString::Format("s %d ip=%d c=%s o=%s", i, ip?1:0, curDir.Format(), origLeaderDir.Format() ));
414 if( ip && curSeg.Contains( *ip ) )
415 {
416 if( curDir == origLeaderDir || curDir == origLeaderDir.Opposite() )
417 return i;
418 }
419 }
420
421 return -1;
422}
423
424void MULTI_DRAGGER::restoreLeaderSegments( std::vector<MDRAG_LINE>& aCompletedLines )
425{
426 m_leaderSegments.clear();
427
428 for( auto& l : aCompletedLines )
429 {
430 if( l.dragOK )
431 {
432 if( m_dragMode == DM_CORNER )
433 {
434 if( l.draggedLine.LinkCount() > 0 )
435 {
436 m_leaderSegments.push_back(
437 static_cast<PNS::ITEM*>( l.draggedLine.GetLink( -1 ) ) );
438 }
439 }
440 else
441 {
442 int newLeaderIdx = findNewLeaderSegment( l );
443 if( newLeaderIdx >= 0 && newLeaderIdx < l.draggedLine.LinkCount() )
444 {
445 m_leaderSegments.push_back(
446 static_cast<PNS::ITEM*>( l.draggedLine.GetLink( newLeaderIdx ) ) );
447 }
448 }
449 }
450 }
451}
452
453bool MULTI_DRAGGER::multidragWalkaround( std::vector<MDRAG_LINE>& aCompletedLines )
454{
455 // fixme: rewrite using shared_ptr...
456 if( m_lastNode )
457 {
458 delete m_lastNode;
459 m_lastNode = nullptr;
460 }
461
462 auto compareDragStartDist = []( const MDRAG_LINE& a, const MDRAG_LINE& b ) -> int
463 {
464 return a.dragDist < b.dragDist;
465 };
466
467 std::sort( aCompletedLines.begin(), aCompletedLines.end(), compareDragStartDist );
468
469
470 NODE* preWalkNode = m_world->Branch();
471
472 for( auto& l : aCompletedLines )
473 {
474 PNS_DBG( Dbg(), AddItem, &l.originalLine, BLUE, 100000, wxString::Format("prewalk-remove lc=%d", l.originalLine.LinkCount() ) );
475 preWalkNode->Remove( l.originalLine );
476 }
477
478 bool fail = false;
479
480 NODE* tmpNodes[2];
481 int totalLength[2];
482
483 for( int attempt = 0; attempt < 2; attempt++ )
484 {
485 NODE *node = tmpNodes[attempt] = preWalkNode->Branch();
486 totalLength[attempt] = 0;
487 fail = false;
488
489 for( int lidx = 0; lidx < aCompletedLines.size(); lidx++ )
490 {
491 MDRAG_LINE& l = aCompletedLines[attempt ? aCompletedLines.size() - 1 - lidx : lidx];
492
493 LINE walk( l.draggedLine );
494 auto result = tryWalkaround( node, l.draggedLine, walk );
495
496 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) );
497 PNS_DBG( Dbg(), AddItem, &walk, BLUE, 100000, wxString::Format("walk lidx=%d attempt=%d", lidx, attempt) );
498
499
500 if( result )
501 {
502 node->Add( walk );
503 totalLength[attempt] += walk.CLine().Length() - l.draggedLine.CLine().Length();
504 l.draggedLine = std::move( walk );
505 }
506 else
507 {
508 delete node;
509 tmpNodes[attempt] = nullptr;
510 fail = true;
511 break;
512 }
513 }
514 }
515
516 if( fail )
517 return false;
518
519
520 bool rv = false;
521
522 if( tmpNodes[0] && tmpNodes[1] )
523 {
524 if ( totalLength[0] < totalLength[1] )
525 {
526 delete tmpNodes[1];
527 m_lastNode = tmpNodes[0];
528 rv = true;
529 }
530 else
531 {
532 delete tmpNodes[0];
533 m_lastNode = tmpNodes[1];
534 rv = true;
535 }
536 }
537 else if ( tmpNodes[0] )
538 {
539 m_lastNode = tmpNodes[0];
540 rv = true;
541 }
542 else if ( tmpNodes[1] )
543 {
544 m_lastNode = tmpNodes[1];
545 rv = true;
546 }
547
548 restoreLeaderSegments( aCompletedLines );
549
550 return rv;
551}
552
553
554bool MULTI_DRAGGER::multidragMarkObstacles( std::vector<MDRAG_LINE>& aCompletedLines )
555{
556
557// fixme: rewrite using shared_ptr...
558 if( m_lastNode )
559 {
560 delete m_lastNode;
561 m_lastNode = nullptr;
562 }
563
564 // m_lastNode contains the temporary (post-modification) state. Think of it as
565 // of an efficient undo buffer. We don't change the PCB directly, but a branch of it
566 // created below. We can then commit its state (applying the modifications to the host board
567 // by calling ROUTING::CommitRouting(m_lastNode) or simply discard it.
568 m_lastNode = m_world->Branch();
569
570
571 int nclipped = 0;
572 for( int l1 = 0; l1 < aCompletedLines.size(); l1++ )
573 {
574 for( int l2 = l1 + 1; l2 < aCompletedLines.size(); l2++ )
575 {
576 const auto& l1l = aCompletedLines[l1].draggedLine;
577 auto l2l = aCompletedLines[l2].draggedLine;
578
579 if( clipToOtherLine( m_lastNode, l1l, l2l ) )
580 {
581 aCompletedLines[l2].draggedLine = l2l;
582 nclipped++;
583 }
584 }
585 }
586
587 for ( auto&l : aCompletedLines )
588 {
589 m_lastNode->Remove( l.originalLine );
590 m_lastNode->Add( l.draggedLine );
591 }
592
593 restoreLeaderSegments( aCompletedLines );
594
595 return true;
596}
597
598bool MULTI_DRAGGER::multidragShove( std::vector<MDRAG_LINE>& aCompletedLines )
599{
600 if( m_lastNode )
601 {
602 delete m_lastNode;
603 m_lastNode = nullptr;
604 }
605
606 if( !m_shove )
607 return false;
608
609 auto compareDragStartDist = []( const MDRAG_LINE& a, const MDRAG_LINE& b ) -> int
610 {
611 return a.dragDist < b.dragDist;
612 };
613
614 std::sort( aCompletedLines.begin(), aCompletedLines.end(), compareDragStartDist );
615
616 auto iface = Router()->GetInterface();
617
618 for( auto& l : m_mdragLines )
619 {
620 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"),
621 iface->GetNetName( l.draggedLine.Net() ),
622 (int) l.isCorner?1:0,
623 (int) l.isStrict?1:0,
624 (int) l.cornerDistance,
625 (int) l.leaderSegDistance,
626 (int) l.leaderSegIndex,
627 (int) l.cornerIsLast?1:0,
628 (int) l.dragDist ) );
629 }
630
631
632 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE );
633 m_shove->ClearHeads();
634
635 for( auto& l : aCompletedLines )
636 {
637 PNS_DBG( Dbg(), AddItem, &l.draggedLine, GREEN, 0, "dragged-line" );
638 m_shove->AddHeads( l.draggedLine, SHOVE::SHP_SHOVE | SHOVE::SHP_DONT_OPTIMIZE );
639 }
640
641 auto status = m_shove->Run();
642
643 m_lastNode = m_shove->CurrentNode()->Branch();
644
645 if( status == SHOVE::SH_OK )
646 {
647 for( int i = 0; i < aCompletedLines.size(); i++ )
648 {
649 MDRAG_LINE&l = aCompletedLines[i];
650 if( m_shove->HeadsModified( i ) )
651 l.draggedLine = m_shove->GetModifiedHead( i );
652
653 // this should not be linked (assert in rt-test)
655
656 m_lastNode->Add( l.draggedLine );
657 }
658 }
659 else
660 {
661 return false;
662 }
663
664 restoreLeaderSegments( aCompletedLines );
665
666 return true;
667}
668
669// this is called every time the user moves the mouse while dragging a set of multiple tracks
671{
672 std::optional<LINE> primaryPreDrag, primaryDragged;
673
674
675
676 SEG lastPreDrag;
677 DIRECTION_45 primaryDir;
678 VECTOR2I perp;
679
680 DIRECTION_45 primaryLastSegDir;
681 std::vector<MDRAG_LINE> completed;
682
683 auto tryPosture = [&] ( int aVariant ) -> bool
684 {
685 MDRAG_LINE* primaryLine = nullptr;
686
687 for( auto &l : m_mdragLines )
688 {
689 l.dragOK = false;
690 l.preDragLine = l.originalLine;
691 //PNS_DBG( Dbg(), AddItem, &l.originalLine, GREEN, 300000, "par" );
692 if( l.isPrimaryLine )
693 {
694
695 //PNS_DBG( Dbg(), AddItem, &l.originalLine, BLUE, 300000, wxT("mdrag-prim"));
696
697 // create a copy of the primary line (pre-drag and post-drag).
698 // the pre-drag version is necessary for NODE::Remove() to be able to
699 // find out the segments before modification by the multidrag algorithm
700 primaryDragged = l.originalLine;
701 primaryDragged->ClearLinks();
702 primaryPreDrag = l.originalLine;
703 primaryLine = &l;
704
705 }
706 }
707
708 if( aVariant == 1 && (primaryPreDrag->PointCount() > 2) )
709 {
710 primaryPreDrag->Line().Remove( -1 );
711 primaryDragged->Line().Remove( -1 );
712
713 for( auto&l : m_mdragLines )
714 {
715 l.preDragLine.Line().Remove(-1);
716 }
717 }
718
719 completed.clear();
720
721 int snapThreshold = Settings().SmoothDraggedSegments() ? primaryDragged->Width() / 4 : 0;
722
723 if( m_dragMode == DM_CORNER )
724 {
725 // first, drag only the primary line
726 PNS_DBG( Dbg(), AddPoint, primaryDragged->CLastPoint(), YELLOW, 600000, wxT("mdrag-sec"));
727
728 lastPreDrag = primaryPreDrag->CSegment( -1 );
729 primaryDir = DIRECTION_45( lastPreDrag );
730
731 primaryDragged->SetSnapThreshhold( snapThreshold );
732 primaryDragged->DragCorner( aP, primaryDragged->PointCount() - 1, false );
733
734
735 if( primaryDragged->SegmentCount() > 0 )
736 {
737 SEG lastPrimDrag = primaryDragged->CSegment( -1 );
738
739 if ( aVariant == 2 )
740 lastPrimDrag = lastPreDrag;
741
742 auto lastSeg = primaryDragged->CSegment( -1 );
743 if( DIRECTION_45( lastSeg ) != primaryDir )
744 {
745 if( lastSeg.Length() < primaryDragged->Width() )
746 {
747 lastPrimDrag = lastPreDrag;
748 }
749 }
750
751 perp = (lastPrimDrag.B - lastPrimDrag.A).Perpendicular();
752 primaryLastSegDir = DIRECTION_45( lastPrimDrag );
753
754
755 PNS_DBG( Dbg(), AddItem, &(*primaryDragged), LIGHTGRAY, 100000, "prim" );
756 PNS_DBG( Dbg(), AddShape, SEG(lastPrimDrag.B, lastPrimDrag.B + perp), LIGHTGRAY, 100000, wxString::Format("prim-perp-seg") );
757 } else {
758 return false;
759 }
760
761
762
763// PNS_DBG( Dbg(), AddShape, &ll, LIGHTBLUE, 200000, "par" );
764
765 }
766 else
767 {
768
769 SHAPE_LINE_CHAIN ll2( { lastPreDrag.A, lastPreDrag.B } );
770 PNS_DBG( Dbg(), AddShape, &ll2, LIGHTYELLOW, 300000, "par" );
771 lastPreDrag = primaryDragged->CSegment( primaryLine->leaderSegIndex );
772 primaryDragged->SetSnapThreshhold( snapThreshold );
773 primaryDragged->DragSegment( aP, primaryLine->leaderSegIndex );
774 perp = (primaryLine->midSeg.B - primaryLine->midSeg.A).Perpendicular();
775 m_guide = SEG( aP, aP + perp );
776 }
777
778
780 m_draggedItems.Clear();
781
782 // now drag all other lines
783 for( auto& l : m_mdragLines )
784 {
785 //PNS_DBG( Dbg(), AddPoint, l.originalLine.CPoint( l.cornerIndex ), WHITE, 1000000, wxT("l-end"));
786 if( l.isDraggable )
787 {
788 l.dragOK = false;
789 //PNS_DBG( Dbg(), AddItem, &l.originalLine, GREEN, 100000, wxT("mdrag-sec"));
790
791 // reject nulls
792 if( l.preDragLine.SegmentCount() >= 1 )
793 {
794
795 //PNS_DBG( Dbg(), AddPoint, l.preDragLine.CPoint( l.cornerIndex ), YELLOW, 600000, wxT("mdrag-sec"));
796
797 // check the direction of the last segment of the line against the direction of
798 // the last segment of the primary line (both before dragging) and perform drag
799 // only when the directions are the same. The algorithm here is quite trival and
800 // otherwise would produce really awkward results. There's of course a TON of
801 // room for improvement here :-)
802
803 if( m_dragMode == DM_CORNER )
804 {
805 DIRECTION_45 parallelDir( l.preDragLine.CSegment( -1 ) );
806
807 auto leadAngle = primaryDir.Angle( parallelDir );
808
809 if( leadAngle == DIRECTION_45::ANG_OBTUSE
810 || leadAngle == DIRECTION_45::ANG_STRAIGHT )
811 {
812 // compute the distance between the primary line and the last point of
813 // the currently processed line
814 int dist = lastPreDrag.LineDistance( l.preDragLine.CLastPoint(), true );
815
816 // now project it on the perpendicular line we computed before
817 auto projected = aP + perp.Resize( dist );
818
819
820 LINE parallelDragged( l.preDragLine );
821
822 PNS_DBG( Dbg(), AddPoint, projected, LIGHTGRAY, 100000, "dragged-c" );
823 PNS_DBG( Dbg(), AddPoint, parallelDragged.CLastPoint(), LIGHTGRAY, 100000, wxString::Format("orig-c cil %d", l.cornerIsLast?1:0) );
824
825 parallelDragged.ClearLinks();
826 //m_lastNode->Remove( parallelDragged );
827 // drag the non-primary line's end trying to place it at the projected point
828 parallelDragged.DragCorner( projected, parallelDragged.PointCount() - 1,
829 false, primaryLastSegDir );
830
831 PNS_DBG( Dbg(), AddPoint, projected, LIGHTYELLOW, 600000,
832 wxT( "l-end" ) );
833
834 l.dragOK = true;
835
836 if( !l.isPrimaryLine )
837 {
838 l.draggedLine = parallelDragged;
839 completed.push_back( l );
840 m_draggedItems.Add( parallelDragged );
841 }
842 }
843 }
844 else if ( m_dragMode == DM_SEGMENT )
845 {
846 SEG sdrag = l.midSeg;
847 DIRECTION_45 refDir( lastPreDrag );
848 DIRECTION_45 curDir( sdrag );
849 auto ang = refDir.Angle( curDir );
850
852 {
853 int dist = lastPreDrag.LineDistance(
854 l.preDragLine.CPoint( l.leaderSegIndex ), true );
855 auto projected = aP + perp.Resize( dist );
856
857 SEG sperp( aP, aP + perp.Resize( 10000000 ) );
858 VECTOR2I startProj = sperp.LineProject( m_dragStartPoint );
859
860 SHAPE_LINE_CHAIN ll( { sperp.A, sperp.B } );
861
862
863 PNS_DBG( Dbg(), AddShape, &ll, LIGHTBLUE, 100000, "par" );
864 SHAPE_LINE_CHAIN ll2( { sdrag.A, sdrag.B } );
865 PNS_DBG( Dbg(), AddShape, &ll2, LIGHTBLUE, 100000, "sdrag" );
866 VECTOR2I v = projected - startProj;
867 l.dragDist = v.EuclideanNorm() * sign( v.Dot( perp ) );
868 l.dragOK = true;
869
870 if( !l.isPrimaryLine )
871 {
872 l.draggedLine = l.preDragLine;
873 l.draggedLine.ClearLinks();
874 l.draggedLine.SetSnapThreshhold( snapThreshold );
875 l.draggedLine.DragSegment( projected, l.leaderSegIndex, false );
876 completed.push_back( l );
877 PNS_DBG( Dbg(), AddItem, &l.draggedLine, LIGHTBLUE, 100000,
878 "dragged" );
879 }
880
881
882 PNS_DBG( Dbg(), AddPoint, startProj, LIGHTBLUE, 400000,
883 wxT( "startProj" ) );
884 PNS_DBG( Dbg(), AddPoint, projected, LIGHTRED, 400000,
885 wxString::Format( "pro dd=%d", l.dragDist ) );
886 }
887 }
888 }
889 }
890
891 if (l.isPrimaryLine)
892 {
893 l.draggedLine = *primaryDragged;
894 l.dragOK = true;
895 completed.push_back( l );
896 }
897 }
898
899 if( m_dragMode == DM_SEGMENT )
900 return true;
901 else
902 {
903 for ( const auto &l: completed )
904 {
905 if( !l.dragOK && aVariant < 2 )
906 return false;
907
908 if( l.isPrimaryLine )
909 continue;
910
911 DIRECTION_45 lastDir ( l.draggedLine.CSegment(-1) );
912
913 if( lastDir != primaryLastSegDir )
914 return false;
915 }
916 }
917
918 return true;
919 };
920
921 bool res = false;
922
923 for( int variant = 0; variant < 3; variant++ )
924 {
925 res = tryPosture( 0 );
926 if( res )
927 break;
928 }
929
930 switch( Settings().Mode() )
931 {
932 case RM_Walkaround:
933 m_dragStatus = multidragWalkaround ( completed );
934 break;
935
936 case RM_Shove:
937 m_dragStatus = multidragShove ( completed );
938 break;
939
940 case RM_MarkObstacles:
942 break;
943
944
945
946 default:
947 break;
948 }
949
950 return m_dragStatus;
951}
952
953
958
959
961{
962 return m_draggedItems;
963}
964
965
967{
968 // fixme: should we care?
969 return 0;
970}
971
972
973} // 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()
ROUTER * Router() const
Return current router settings.
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
DEBUG_DECORATOR * Dbg() const
DRAG_ALGO(ROUTER *aRouter)
bool Empty() const
Definition pns_itemset.h:82
bool Contains(ITEM *aItem) const
std::vector< ITEM * > & Items()
Definition pns_itemset.h:87
ITEM * FindVertex(const VECTOR2I &aV) const
Base class for PNS router board items.
Definition pns_item.h:98
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:176
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)
Return the shape of the line.
Definition pns_line.h:131
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:142
const VECTOR2I & CLastPoint() const
Definition pns_line.h:151
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:831
int SegmentCount() const
Definition pns_line.h:144
int PointCount() const
Definition pns_line.h:145
const SEG CSegment(int aIdx) const
Set line width.
Definition pns_line.h:152
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
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()
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:232
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:143
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition pns_node.cpp:665
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:909
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:232
void CommitRouting()
bool SmoothDraggedSegments() const
Enable/disable smoothing segments during dragging.
The actual Push and Shove algorithm.
Definition pns_shove.h:46
@ SHP_DONT_OPTIMIZE
Definition pns_shove.h:65
@ SHP_DONT_LOCK_ENDPOINTS
Definition pns_shove.h:66
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:42
VECTOR2I A
Definition seg.h:49
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:717
VECTOR2I B
Definition seg.h:50
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:220
bool Contains(const SEG &aSeg) const
Definition seg.h:324
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition seg.cpp:656
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.
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.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:554
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
@ LIGHTBLUE
Definition color4d.h:62
@ BLUE
Definition color4d.h:56
@ LIGHTGRAY
Definition color4d.h:47
@ LIGHTYELLOW
Definition color4d.h:49
@ GREEN
Definition color4d.h:57
@ YELLOW
Definition color4d.h:67
@ LIGHTRED
Definition color4d.h:65
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:71
@ DM_CORNER
Definition pns_router.h:72
@ DM_SEGMENT
Definition pns_router.h:73
bool clipToOtherLine(NODE *aNode, const LINE &aRef, LINE &aClipped)
#define PNS_DBG(dbg, method,...)
std::vector< PNS::ITEM * > originalLeaders
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
VECTOR3I res
wxString result
Test unit parsing edge cases and error handling.
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695