KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_dragger.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "pns_dragger.h"
23
24#include <core/typeinfo.h>
25#include <advanced_config.h>
26#include <base_units.h>
27#include <geometry/eda_angle.h>
28#include <geometry/shape_arc.h>
29#include <wx/translation.h>
30
31#include "pns_arc.h"
32#include "pns_segment.h"
33#include "pns_shove.h"
34#include "pns_router.h"
35#include "pns_debug_decorator.h"
36#include "pns_walkaround.h"
37
38
39namespace PNS {
40
42 DRAG_ALGO( aRouter ),
43 m_initialVia( {} ),
44 m_draggedVia( {} )
45{
46 m_world = nullptr;
47 m_lastNode = nullptr;
48 m_mode = DM_SEGMENT;
49 m_draggedSegmentIndex = 0;
50 m_dragStatus = false;
51 m_currentMode = RM_MarkObstacles;
52 m_freeAngleMode = false;
53 m_forceMarkObstaclesMode = false;
54}
55
56
60
61
62bool DRAGGER::propagateViaForces( NODE* node, std::set<VIA*>& vias )
63{
64 VIA* via = *vias.begin();
65
66 VECTOR2I force;
67 VECTOR2I lead = -m_mouseTrailTracer.GetTrailLeadVector();
68
69 const int iterLimit = Settings().ViaForcePropIterationLimit();
70
71 if( via->PushoutForce( node, lead, force, ITEM::ANY_T, iterLimit ) )
72 {
73 via->SetPos( via->Pos() + force );
74 return true;
75 }
76
77 return false;
78}
79
80
82{
83 int w2 = aSeg->Width() / 2;
84
85 auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm();
86 auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm();
87
88 VECTOR2I psnap;
89
90 if( distA <= w2 )
91 {
92 psnap = aSeg->Seg().A;
93 }
94 else if( distB <= w2 )
95 {
96 psnap = aSeg->Seg().B;
97 }
98 else
99 {
100 return nullptr;
101 }
102
103 const JOINT *jt = m_world->FindJoint( psnap, aSeg );
104
105 if ( !jt )
106 return nullptr;
107
108 for( ITEM* item : jt->LinkList() )
109 {
110 if( item->IsVirtual() && item->OfKind( ITEM::VIA_T ))
111 return static_cast<VVIA*>( item );
112 }
113
114 return nullptr;
115}
116
117
119{
120 int w2 = aSeg->Width() / 2;
121
122 m_draggedLine = m_world->AssembleLine( aSeg, &m_draggedSegmentIndex );
124
125 auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm();
126 auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm();
127
128 if( distA < w2 || distB < w2 )
129 {
131
132 if( distB <= distA )
134 }
135 else if( m_freeAngleMode )
136 {
137 if( distB < distA &&
138 ( m_draggedSegmentIndex < m_draggedLine.PointCount() - 2 ) &&
139 ( !m_draggedLine.CLine().IsPtOnArc( static_cast<size_t>(m_draggedSegmentIndex) + 1 ) ) )
140 {
142 }
143
145 }
146 else
147 {
149 }
150
151 return true;
152}
153
154
155bool DRAGGER::startDragArc( const VECTOR2D& aP, ARC* aArc )
156{
157 EDA_ANGLE maxDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
158
159 EDA_ANGLE centralAngle( std::abs( aArc->CArc().GetCentralAngle().AsDegrees() ), DEGREES_T );
160
161 if( centralAngle + maxDeviation >= ANGLE_180 )
162 {
163 EDA_ANGLE limit = ANGLE_180 - maxDeviation;
165 wxString::Format( _( "Unable to drag arc tracks of %.1f degrees or greater." ), limit.AsDegrees() ) );
166 return false;
167 }
168
169 int probeIdx = 0;
170 LINE probe = m_world->AssembleLine( aArc, &probeIdx );
171
172 ssize_t arcIdx = -1;
173 int firstArcPt = -1;
174 int lastArcPt = -1;
175
176 for( int i = 0; i < probe.PointCount(); i++ )
177 {
178 ssize_t a = probe.CLine().ArcIndex( i );
179
180 if( a < 0 )
181 continue;
182
183 if( arcIdx < 0 )
184 arcIdx = a;
185
186 if( a == arcIdx )
187 {
188 if( firstArcPt < 0 )
189 firstArcPt = i;
190
191 lastArcPt = i;
192 }
193 }
194
195 bool isolatedStart = ( firstArcPt == 0 );
196 bool isolatedEnd = ( lastArcPt == probe.PointCount() - 1 );
197
198 if( isolatedStart || isolatedEnd )
199 {
200 int maxStubIU = KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
201 int stubLen = std::max( 1, maxStubIU / 2 );
202
203 const SHAPE_ARC& sharc = aArc->CArc();
204 VECTOR2I center = sharc.GetCenter();
205 VECTOR2I mid = sharc.GetArcMid();
206
207 auto outwardTangent = [&]( const VECTOR2I& aEndpoint ) -> VECTOR2I
208 {
209 VECTOR2I radial = aEndpoint - center;
210 VECTOR2I perp( -radial.y, radial.x );
211 VECTOR2I toMid = mid - aEndpoint;
212
213 if( perp.x * toMid.x + perp.y * toMid.y > 0 )
214 perp = VECTOR2I( radial.y, -radial.x );
215
216 double mag = std::hypot( (double) perp.x, (double) perp.y );
217
218 if( mag <= 0 )
219 return VECTOR2I( stubLen, 0 );
220
221 return VECTOR2I( KiROUND( perp.x * stubLen / mag ), KiROUND( perp.y * stubLen / mag ) );
222 };
223
224 if( isolatedStart )
225 {
226 VECTOR2I p0 = sharc.GetP0();
227 VECTOR2I stubFar = p0 + outwardTangent( p0 );
228 auto stub = std::make_unique<SEGMENT>( SEG( stubFar, p0 ), aArc->Net() );
229 stub->SetWidth( aArc->Width() );
230 stub->SetLayers( aArc->Layers() );
231 m_preDragNode->Add( std::move( stub ) );
232 }
233
234 if( isolatedEnd )
235 {
236 VECTOR2I p1 = sharc.GetP1();
237 VECTOR2I stubFar = p1 + outwardTangent( p1 );
238 auto stub = std::make_unique<SEGMENT>( SEG( p1, stubFar ), aArc->Net() );
239 stub->SetWidth( aArc->Width() );
240 stub->SetLayers( aArc->Layers() );
241 m_preDragNode->Add( std::move( stub ) );
242 }
243
244 m_draggedLine = m_preDragNode->AssembleLine( aArc, &m_draggedSegmentIndex );
245 }
246 else
247 {
248 m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex );
249 }
250
251 m_mode = DM_ARC;
252
253 return true;
254}
255
256
258{
259 m_initialVia = aVia->MakeHandle();
261
262 m_mode = DM_VIA;
263
264 return true;
265}
266
268{
269 ITEM_SET rv;
270
271 const JOINT* jt = aNode->FindJoint( handle.pos, handle.layers.Start(), handle.net );
272
273 if( !jt )
274 return rv;
275
276 bool foundVia = false;
277
278 for( ITEM* item : jt->LinkList() )
279 {
280 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
281 {
282 int segIndex;
283 LINKED_ITEM* seg = ( LINKED_ITEM*) item;
284 LINE l = aNode->AssembleLine( seg, &segIndex );
285
286 if( segIndex != 0 )
287 l.Reverse();
288
289 rv.Add( l );
290 }
291 else if( item->OfKind( ITEM::VIA_T ) )
292 {
293 if( !foundVia )
294 {
295 rv.Add( item );
296 foundVia = true;
297 }
298 }
299 }
300
301 return rv;
302}
303
304bool DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
305{
306 if( aPrimitives.Empty() )
307 return false;
308
309 ITEM* startItem = aPrimitives[0];
310
311 m_lastNode = nullptr;
312 m_draggedItems.Clear();
316 m_lastValidPoint = aP;
317
318 m_mouseTrailTracer.Clear();
319 m_mouseTrailTracer.AddTrailPoint( aP );
320
321 m_preDragNode = m_world->Branch();
322
324 {
325 m_shove = std::make_unique<SHOVE>( m_preDragNode, Router() );
326 m_shove->SetLogger( Logger() );
327 m_shove->SetDebugDecorator( Dbg() );
328 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE );
329 }
330
331 startItem->Unmark( MK_LOCKED );
332
333 PNS_DBG( Dbg(), Message, wxString::Format( "StartDragging: item %p [kind %d]",
334 startItem, (int) startItem->Kind() ) );
335
336 switch( startItem->Kind() )
337 {
338 case ITEM::SEGMENT_T:
339 {
340 SEGMENT* seg = static_cast<SEGMENT*>( startItem );
341 VVIA* vvia = checkVirtualVia( aP, seg );
342
343 if( vvia )
344 return startDragVia( vvia );
345 else
346 return startDragSegment( aP, seg );
347 }
348 case ITEM::VIA_T:
349 return startDragVia( static_cast<VIA*>( startItem ) );
350
351 case ITEM::ARC_T:
352 return startDragArc( aP, static_cast<ARC*>( startItem ) );
353
354 default:
355 return false;
356 }
357}
358
359
361{
362 m_mode = static_cast<int>( aMode );
363}
364
365
367{
368 return static_cast<PNS::DRAG_MODE>( m_mode );
369}
370
371
372const std::vector<NET_HANDLE> DRAGGER::CurrentNets() const
373{
374 if( m_mode == PNS::DM_VIA )
375 return std::vector<NET_HANDLE>( 1, m_draggedVia.net );
376 else
377 return std::vector<NET_HANDLE>( 1, m_draggedLine.Net() );
378}
379
380
382{
383 // fixme: rewrite using shared_ptr...
384 if( m_lastNode )
385 {
386 delete m_lastNode;
387 m_lastNode = nullptr;
388 }
389
390 m_lastNode = m_preDragNode->Branch();
391
392 switch( m_mode )
393 {
394 case DM_SEGMENT:
395 case DM_CORNER:
396 {
397 //TODO: Make threshold configurable
398 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
399 LINE origLine( m_draggedLine );
400 LINE dragged( m_draggedLine );
401 dragged.SetSnapThreshhold( thresh );
402 dragged.ClearLinks();
403
404 if( m_mode == DM_SEGMENT )
405 dragged.DragSegment( aP, m_draggedSegmentIndex );
406 else
408
409 m_lastNode->Remove( origLine );
410 m_lastNode->Add( dragged );
411
412 m_draggedItems.Clear();
413 m_draggedItems.Add( dragged );
414
415 break;
416 }
417
418 case DM_ARC:
419 {
420 LINE origLine( m_draggedLine );
421 LINE dragged( m_draggedLine );
422 dragged.ClearLinks();
423
424 dragged.DragArc( aP, m_draggedSegmentIndex );
425
426 // A collapsed arc drag leaves an empty chain, so Add() is a no-op and the arc
427 // is simply dropped from the route, which is the intended outcome here.
428 m_lastNode->Remove( origLine );
429 m_lastNode->Add( dragged );
430
431 m_draggedItems.Clear();
432 m_draggedItems.Add( dragged );
433
434 break;
435 }
436
437 case DM_VIA: // fixme...
439
440 break;
441 }
442
443 if( Settings().AllowDRCViolations() )
444 m_dragStatus = true;
445 else
446 m_dragStatus = !m_lastNode->CheckColliding( m_draggedItems );
447
448 return true;
449}
450
451
452bool DRAGGER::dragViaMarkObstacles( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
453{
454 m_draggedItems.Clear();
455
456 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
457
458 if( fanout.Empty() )
459 return true;
460
461 for( ITEM* item : fanout.Items() )
462 {
463 if( const LINE* l = dyn_cast<const LINE*>( item ) )
464 {
465 LINE origLine( *l );
466 LINE draggedLine( *l );
467
468 draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode );
469 draggedLine.ClearLinks();
470
471 m_draggedItems.Add( draggedLine );
472
473 m_lastNode->Remove( origLine );
474 m_lastNode->Add( draggedLine );
475 }
476 else if ( VIA *via = dyn_cast<VIA*>( item ) )
477 {
478 auto nvia = Clone( *via );
479
480 nvia->SetPos( aP );
481 m_draggedItems.Add( nvia.get() );
482
483 m_lastNode->Remove( via );
484 m_lastNode->Add( std::move( nvia ) );
485 }
486 }
487
488 return true;
489}
490
491
492bool DRAGGER::dragViaWalkaround( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
493{
494 m_draggedItems.Clear();
495
496 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
497
498 if( fanout.Empty() )
499 return true;
500
501 bool viaPropOk = false;
502 VECTOR2I viaTargetPos;
503
504 for( ITEM* item : fanout.Items() )
505 {
506 if( VIA *via = dyn_cast<VIA*>( item ) )
507 {
508 std::unique_ptr<VIA> draggedVia = Clone( *via );
509
510 draggedVia->SetPos( aP );
511 m_draggedItems.Add( draggedVia.get() );
512
513 std::set<VIA*> vias;
514
515 vias.insert( draggedVia.get() );
516
517 m_lastNode->Remove( via );
518
519 bool ok = propagateViaForces( m_lastNode, vias );
520
521 if( ok )
522 {
523 viaTargetPos = draggedVia->Pos();
524 viaPropOk = true;
525 m_lastNode->Add( std::move(draggedVia) );
526 }
527 }
528 }
529
530 if( !viaPropOk ) // can't force-propagate the via? bummer...
531 return false;
532
533 for( ITEM* item : fanout.Items() )
534 {
535 if( const LINE* l = dyn_cast<const LINE*>( item ) )
536 {
537 LINE origLine( *l );
538 LINE draggedLine( *l );
539 LINE walkLine( *l );
540
541 draggedLine.DragCorner( viaTargetPos, origLine.CLine().Find( aHandle.pos ),
543 draggedLine.ClearLinks();
544
545 if ( m_world->CheckColliding( &draggedLine ) )
546 {
547 bool ok = tryWalkaround( m_lastNode, draggedLine, walkLine );
548
549 if( !ok )
550 return false;
551
552 m_lastNode->Remove( origLine );
553 optimizeAndUpdateDraggedLine( walkLine, origLine, aP );
554 }
555 else
556 {
557 m_draggedItems.Add( draggedLine );
558
559 m_lastNode->Remove( origLine );
560 m_lastNode->Add( draggedLine );
561 }
562 }
563 }
564
565 return true;
566}
567
568
569void DRAGGER::optimizeAndUpdateDraggedLine( LINE& aDragged, const LINE& aOrig, const VECTOR2I& aP )
570{
571 LINE draggedPostOpt, origLine( aOrig );
572
573 aDragged.ClearLinks();
574 aDragged.Unmark();
575
576 if( Settings().GetOptimizeEntireDraggedTrack() )
577 {
578 OPTIMIZER optimizer( m_lastNode );
579
580 int effort =
582
583 if( Settings().SmoothDraggedSegments() )
585
586 optimizer.SetEffortLevel( effort );
587
588 OPT_BOX2I affectedArea = aDragged.ChangedArea( &aOrig );
589 VECTOR2I anchor( aP );
590
591 if( aDragged.CLine().Find( aP ) < 0 )
592 anchor = aDragged.CLine().NearestPoint( aP );
593
594 optimizer.SetPreserveVertex( anchor );
595
596 if( !affectedArea )
597 affectedArea = BOX2I( aP ); // No valid area yet? set to minimum to disable optimization
598
599 PNS_DBG( Dbg(), AddPoint, anchor, YELLOW, 100000, wxT( "drag-anchor" ) );
600 PNS_DBG( Dbg(), AddShape, *affectedArea, RED, 0, wxT( "drag-affected-area" ) );
601
602 optimizer.SetRestrictArea( *affectedArea );
603
604 PNS_DBG( Dbg(), AddItem, &aDragged, RED, 0, wxT( "drag-preopt" ) );
605 aDragged.Line().Split( anchor );
606
607 optimizer.Optimize( &aDragged, &draggedPostOpt, &origLine );
608 PNS_DBG( Dbg(), AddItem, &aDragged, GREEN, 0, wxT( "drag-postopt" ) );
609 }
610 else
611 {
612 draggedPostOpt = aDragged;
613 }
614
615 m_lastNode->Add( draggedPostOpt );
616 m_draggedItems.Clear();
617 m_draggedItems.Add( draggedPostOpt );
618}
619
620
621bool DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
622{
623 WALKAROUND walkaround( aNode, Router() );
624 walkaround.SetSolidsOnly( false );
625 walkaround.SetDebugDecorator( Dbg() );
626 walkaround.SetLogger( Logger() );
627 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
628 walkaround.SetLengthLimit( true, 30.0 );
630
631 aWalk = aOrig;
632
633 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
634
636 {
637 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
638 return true;
639 }
640
641 return false;
642}
643
644
646{
647 bool ok = false;
648
649 // fixme: rewrite using shared_ptr...
650 if( m_lastNode )
651 {
652 delete m_lastNode;
653 m_lastNode = nullptr;
654 }
655
656 m_lastNode = m_preDragNode->Branch();
657
658 switch( m_mode )
659 {
660 case DM_SEGMENT:
661 case DM_CORNER:
662 {
663 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
664 LINE dragged( m_draggedLine );
665 LINE draggedWalk( m_draggedLine );
666 LINE origLine( m_draggedLine );
667
668 dragged.SetSnapThreshhold( thresh );
669
670 if( m_mode == DM_SEGMENT )
671 dragged.DragSegment( aP, m_draggedSegmentIndex );
672 else
673 dragged.DragCorner( aP, m_draggedSegmentIndex );
674
675 if ( m_world->CheckColliding( &dragged ) )
676 {
677 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
678 }
679 else
680 {
681 draggedWalk = dragged;
682 ok = true;
683 }
684
685 if( draggedWalk.CLine().PointCount() < 2 )
686 ok = false;
687
688 if( ok )
689 {
690 PNS_DBG( Dbg(), AddShape, &origLine.CLine(), BLUE, 50000, wxT( "drag-orig-line" ) );
691 PNS_DBG( Dbg(), AddShape, &draggedWalk.CLine(), CYAN, 75000, wxT( "drag-walk" ) );
692 m_lastNode->Remove( origLine );
693 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
694 }
695
696 break;
697 }
698 case DM_ARC:
699 {
700 LINE dragged( m_draggedLine );
701 LINE draggedWalk( m_draggedLine );
702 LINE origLine( m_draggedLine );
703
704 dragged.DragArc( aP, m_draggedSegmentIndex );
705
706 if( m_world->CheckColliding( &dragged ) )
707 {
708 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
709 }
710 else
711 {
712 draggedWalk = dragged;
713 ok = true;
714 }
715
716 if( draggedWalk.CLine().PointCount() < 2 )
717 ok = false;
718
719 if( ok )
720 {
721 m_lastNode->Remove( origLine );
722 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
723 }
724
725 break;
726 }
727 case DM_VIA: // fixme...
729 break;
730 }
731
732 m_dragStatus = ok;
733
734 return ok;
735}
736
737
739{
740
741 if( m_lastNode )
742 {
743 delete m_lastNode;
744 m_lastNode = nullptr;
745 }
746
747 switch( m_mode )
748 {
749 case DM_SEGMENT:
750 case DM_CORNER:
751 {
752 bool ok = false;
753 //TODO: Make threshold configurable
754 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0;
755 LINE draggedPreShove( m_draggedLine );
756 draggedPreShove.SetSnapThreshhold( thresh );
757
758 if( m_mode == DM_SEGMENT )
759 draggedPreShove.DragSegment( aP, m_draggedSegmentIndex );
760 else
761 draggedPreShove.DragCorner( aP, m_draggedSegmentIndex );
762
763 auto preShoveNode = m_shove->CurrentNode();
764
765 if( preShoveNode )
766 preShoveNode->Remove( draggedPreShove );
767
769
770 PNS_DBG( Dbg(), Message, wxString::Format( "drag seg index %d", m_draggedSegmentIndex ) );
771
772 if( m_mode == DM_CORNER && m_draggedSegmentIndex == 0 )
773 policy |= SHOVE::SHP_REVERSED;
774
775 m_shove->ClearHeads();
776 m_shove->AddHeads( draggedPreShove, policy );
777 ok = m_shove->Run() == SHOVE::SH_OK;
778
779 LINE draggedPostShove( draggedPreShove );
780
781 if( ok )
782 {
783 if( m_shove->HeadsModified() )
784 draggedPostShove = m_shove->GetModifiedHead( 0 );
785 }
786
787 m_lastNode = m_shove->CurrentNode()->Branch();
788
789 if( ok )
790 {
791 draggedPostShove.ClearLinks();
792 draggedPostShove.Unmark();
793 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
794 m_lastDragSolution = std::move( draggedPostShove );
795 }
796
797 m_dragStatus = ok;
798 break;
799 }
800
801 case DM_ARC:
802 {
803 bool ok = false;
804
805 LINE draggedPreShove( m_draggedLine );
806 draggedPreShove.DragArc( aP, m_draggedSegmentIndex );
807
808 // A collapsed arc drag can leave fewer than two points, which is not a valid
809 // shove head. Treat that as an unsuccessful shove (as dragWalkaround does) rather
810 // than feeding a degenerate line to AddHeads.
811 if( draggedPreShove.CLine().PointCount() >= 2 )
812 {
813 auto preShoveNode = m_shove->CurrentNode();
814
815 if( preShoveNode )
816 preShoveNode->Remove( draggedPreShove );
817
819
820 m_shove->ClearHeads();
821 m_shove->AddHeads( draggedPreShove, policy );
822 ok = m_shove->Run() == SHOVE::SH_OK;
823 }
824
825 LINE draggedPostShove( draggedPreShove );
826
827 if( ok && m_shove->HeadsModified() )
828 draggedPostShove = m_shove->GetModifiedHead( 0 );
829
830 m_lastNode = m_shove->CurrentNode()->Branch();
831
832 if( ok )
833 {
834 draggedPostShove.ClearLinks();
835 draggedPostShove.Unmark();
836 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
837 m_lastDragSolution = std::move( draggedPostShove );
838 }
839
840 m_dragStatus = ok;
841 break;
842 }
843
844 case DM_VIA:
845 {
846 VIA_HANDLE newVia;
847
848 // corner count limiter intended to avoid excessive optimization produces mediocre results for via shoving.
849 // this is a hack that disables it, before I figure out a more reliable solution
850 m_shove->DisablePostShoveOptimizations( OPTIMIZER::LIMIT_CORNER_COUNT );
851
852 m_shove->ClearHeads();
853 m_shove->AddHeads( m_draggedVia, aP, SHOVE::SHP_SHOVE );
854
855 SHOVE::SHOVE_STATUS st = m_shove->Run(); //ShoveDraggingVia( m_draggedVia, aP, newVia );
856
857 PNS_DBG( Dbg(), Message, wxString::Format("head-mod %d",
858 m_shove->HeadsModified() ? 1: 0 ) );
859
860 if( m_shove->HeadsModified() )
861 {
862 newVia = m_shove->GetModifiedHeadVia( 0 );
863
864 PNS_DBG( Dbg(), Message, wxString::Format("newvia %d %d %d %d",
865 newVia.pos.x,
866 newVia.pos.y,
867 newVia.layers.Start(),
868 newVia.layers.End()
869 ) );
870
871 m_draggedVia = newVia;
872 }
873
874
875 m_lastNode = m_shove->CurrentNode()->Branch();
876
877 m_draggedItems.Clear();
878
879 // If drag didn't work (i.e. dragged onto a collision) try walkaround instead
880 if( st != SHOVE::SH_OK )
882 else
883 m_dragStatus = true;
884
885 break;
886 }
887 }
888
889 return m_dragStatus;
890}
891
892
893bool DRAGGER::FixRoute( bool aForceCommit )
894{
895 NODE* node = CurrentNode();
896
897 if( node )
898 {
899 if( m_dragStatus )
900 {
901 Router()->CommitRouting( node );
902 return true;
903 }
904 else if( m_forceMarkObstaclesMode )
905 {
906 if( aForceCommit )
907 {
908 Router()->CommitRouting( node );
909 return true;
910 }
911
912 return false;
913 }
914 else
915 {
916 // If collisions exist, we can fix in shove/smart mode because all tracks to be
917 // committed will be in valid positions (even if the current routing solution to
918 // the mouse cursor is invalid).
920 node = CurrentNode();
921
922 if( node && m_dragStatus )
923 {
924 Router()->CommitRouting( node );
925 return true;
926 }
927 }
928 }
929
930 return false;
931}
932
933
934bool DRAGGER::Drag( const VECTOR2I& aP )
935{
936 m_mouseTrailTracer.AddTrailPoint( aP );
937
938 bool firstDrag = m_lastNode == nullptr;
939 bool ret = false;
940
942 {
943 ret = dragMarkObstacles( aP );
944 }
945 else
946 {
947 switch( m_currentMode )
948 {
949 case RM_MarkObstacles: ret = dragMarkObstacles( aP ); break;
950 case RM_Shove: ret = dragShove( aP ); break;
951 case RM_Walkaround: ret = dragWalkaround( aP ); break;
952 default: break;
953 }
954 }
955
956 if( ret )
957 {
958 m_lastValidPoint = aP;
959 }
960 else
961 {
962 if( firstDrag )
963 {
964 // First collision resolution failed, switch to highlight mode
966
967 ret = dragMarkObstacles( aP );
968
969 if( ret )
970 m_lastValidPoint = aP;
971 }
972 else if( m_lastNode )
973 {
974 // Restore last solution
975 NODE* parent = m_lastNode->GetParent()->Branch();
976 delete m_lastNode;
977 m_lastNode = parent;
978 m_draggedItems.Clear();
979 m_lastDragSolution.ClearLinks();
981 }
982 }
983
984 return ret;
985}
986
987
989{
990 return m_lastNode ? m_lastNode : m_world;
991}
992
993
995{
996 return m_draggedItems;
997}
998
999}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
double AsDegrees() const
Definition eda_angle.h:116
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
int Width() const override
Definition pns_arc.h:88
const SHAPE_ARC & CArc() const
Definition pns_arc.h:116
ITEM_SET m_draggedItems
If true, moves the connection lines without maintaining 45 degrees corners.
PNS::DRAG_MODE Mode() const override
void optimizeAndUpdateDraggedLine(LINE &aDragged, const LINE &aOrig, const VECTOR2I &aP)
const ITEM_SET findViaFanoutByHandle(NODE *aNode, const VIA_HANDLE &handle)
bool startDragSegment(const VECTOR2D &aP, SEGMENT *aSeg)
VECTOR2D m_lastValidPoint
Contains the list of items that are currently modified by the dragger.
virtual bool Start(const VECTOR2I &aP, ITEM_SET &aPrimitives) override
Function Start()
NODE * CurrentNode() const override
Function CurrentNode()
DRAGGER(ROUTER *aRouter)
bool dragViaMarkObstacles(const VIA_HANDLE &aHandle, NODE *aNode, const VECTOR2I &aP)
bool Drag(const VECTOR2I &aP) override
Function Drag()
bool startDragVia(VIA *aVia)
LINE m_lastDragSolution
int m_draggedSegmentIndex
const std::vector< NET_HANDLE > CurrentNets() const override
Function CurrentNets()
bool dragShove(const VECTOR2I &aP)
void SetMode(PNS::DRAG_MODE aDragMode) override
bool dragMarkObstacles(const VECTOR2I &aP)
bool FixRoute(bool aForceCommit) override
Function FixRoute()
std::unique_ptr< SHOVE > m_shove
NODE * m_lastNode
bool dragWalkaround(const VECTOR2I &aP)
VVIA * checkVirtualVia(const VECTOR2D &aP, SEGMENT *aSeg)
VIA_HANDLE m_draggedVia
bool dragViaWalkaround(const VIA_HANDLE &aHandle, NODE *aNode, const VECTOR2I &aP)
bool m_freeAngleMode
bool m_forceMarkObstaclesMode
NODE * m_preDragNode
PNS_MODE m_currentMode
const ITEM_SET Traces() override
Function Traces()
bool tryWalkaround(NODE *aNode, LINE &aOrig, LINE &aWalk)
bool startDragArc(const VECTOR2D &aP, ARC *aArc)
MOUSE_TRAIL_TRACER m_mouseTrailTracer
VIA_HANDLE m_initialVia
bool propagateViaForces(NODE *node, std::set< VIA * > &vias)
DRAG_ALGO(ROUTER *aRouter)
bool Empty() const
Definition pns_itemset.h:90
void Add(const LINE &aLine)
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
Base class for PNS router board items.
Definition pns_item.h:98
virtual void Unmark(int aMarker=-1) const
Definition pns_item.h:262
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
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
const std::vector< ITEM * > & LinkList() const
Definition pns_joint.h:303
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
OPT_BOX2I ChangedArea(const LINE *aOther) const
void DragArc(const VECTOR2I &aP, int aIndex)
Definition pns_line.cpp:863
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:142
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:836
void SetSnapThreshhold(int aThreshhold)
Definition pns_line.h:252
virtual void Unmark(int aMarker=-1) const override
Definition pns_line.cpp:184
int PointCount() const
Definition pns_line.h:145
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition pns_line.cpp:850
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
Keep the router "world" - i.e.
Definition pns_node.h:242
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.
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false, bool aAllowSegmentSizeMismatch=true)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
Perform various optimizations of the lines being routed, attempting to make the lines shorter and les...
void SetPreserveVertex(const VECTOR2I &aV)
void SetRestrictArea(const BOX2I &aArea, bool aStrict=true)
void SetEffortLevel(int aEffort)
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
@ LIMIT_CORNER_COUNT
Do not attempt to optimize if the resulting line's corner count is outside the predefined range.
@ MERGE_SEGMENTS
Reduce corner cost iteratively.
@ MERGE_COLINEAR
Merge co-linear segments.
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:235
void CommitRouting()
bool SmoothDraggedSegments() const
Enable/disable smoothing segments during dragging.
PNS_MODE Mode() const
Set the routing mode.
const SEG & Seg() const
int Width() const override
Definition pns_segment.h:96
@ SHP_DONT_LOCK_ENDPOINTS
Definition pns_shove.h:66
const VIA_HANDLE MakeHandle() const
Definition pns_via.cpp:310
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)
int Start() const
int End() const
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
EDA_ANGLE GetCentralAngle() const
Get the "central angle" of the arc - this is the angle at the point of the "pie slice".
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
const VECTOR2I & GetCenter() 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.
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
@ BLUE
Definition color4d.h:52
@ GREEN
Definition color4d.h:53
@ CYAN
Definition color4d.h:54
@ YELLOW
Definition color4d.h:63
@ RED
Definition color4d.h:55
#define _(s)
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
Push and Shove diff pair dimensions (gap) settings dialog.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
DRAG_MODE
Definition pns_router.h:76
@ DM_CORNER
Definition pns_router.h:77
@ DM_FREE_ANGLE
Definition pns_router.h:80
@ DM_VIA
Definition pns_router.h:79
@ DM_SEGMENT
Definition pns_router.h:78
@ DM_ARC
Definition pns_router.h:81
@ MK_LOCKED
Definition pns_item.h:45
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition pns_item.h:344
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#define PNS_DBG(dbg, method,...)
VECTOR2I pos
Definition pns_via.h:55
NET_HANDLE net
Definition pns_via.h:57
PNS_LAYER_RANGE layers
Definition pns_via.h:56
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
VECTOR2I center
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:56
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682