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