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 OPTIMIZER optimizer( m_lastNode );
577
578 int effort = OPTIMIZER::MERGE_SEGMENTS;
579
580 if( Settings().SmoothDraggedSegments() )
582
583 if( Settings().GetRestrictAngles() )
585
586 optimizer.SetEffortLevel( effort );
587
588 VECTOR2I anchor( aP );
589
590 if( aDragged.CLine().Find( aP ) < 0 )
591 anchor = bestAnchorForPoint( aDragged.CLine(), aP );
592
593 optimizer.SetPreserveVertex( anchor );
594 aDragged.Line().Split( anchor );
595
596 PNS_DBG( Dbg(), AddPoint, anchor, YELLOW, 100000, wxT( "drag-anchor" ) );
597
598 if( !Settings().GetOptimizeEntireDraggedTrack() )
599 {
600 OPT_BOX2I affectedArea = aDragged.ChangedArea( &aOrig );
601
602 if( !affectedArea )
603 affectedArea = BOX2I( aP ); // No valid area yet? set to minimum to disable optimization
604
605 PNS_DBG( Dbg(), AddShape, *affectedArea, RED, 0, wxT( "drag-affected-area" ) );
606
607 optimizer.SetRestrictArea( *affectedArea );
608 }
609
610 PNS_DBG( Dbg(), AddItem, &aDragged, RED, 0, wxT( "drag-preopt" ) );
611
612 optimizer.Optimize( &aDragged, &draggedPostOpt, &origLine );
613 aDragged = draggedPostOpt;
614 PNS_DBG( Dbg(), AddItem, &aDragged, GREEN, 0, wxT( "drag-postopt" ) );
615
616 m_lastNode->Add( draggedPostOpt );
617 m_draggedItems.Clear();
618 m_draggedItems.Add( draggedPostOpt );
619}
620
621
622bool DRAGGER::pointHasBadCorner( const SHAPE_LINE_CHAIN& aLine, int aVertexIndex ) const
623{
624 if( aVertexIndex <= 0 || aVertexIndex >= aLine.PointCount() - 1 )
625 return false;
626
627 SEG segBefore( aLine.CPoint( aVertexIndex - 1 ), aLine.CPoint( aVertexIndex ) );
628 SEG segAfter( aLine.CPoint( aVertexIndex ), aLine.CPoint( aVertexIndex + 1 ) );
629
630 DIRECTION_45 dirBefore( segBefore );
631 DIRECTION_45 dirAfter( segAfter );
632
633 return dirBefore.Angle( dirAfter ) & ( DIRECTION_45::ANG_ACUTE
636}
637
638
640{
641 VECTOR2I nearest = aLine.NearestPoint( aP );
642 int vertIdx = aLine.Find( nearest );
643
644 if( vertIdx < 0 || !pointHasBadCorner( aLine, vertIdx ) )
645 {
646 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt used (vert=%d)", vertIdx ) );
647 return nearest;
648 }
649
650 for( int offset = 1; offset < aLine.PointCount(); offset++ )
651 {
652 int rightIdx = vertIdx + offset;
653 std::optional<VECTOR2I> candidate;
654
655 if( rightIdx < aLine.PointCount() && !pointHasBadCorner( aLine, rightIdx ) )
656 candidate = aLine.CPoint( rightIdx );
657
658 int leftIdx = vertIdx - offset;
659
660 if( leftIdx >= 0 && !pointHasBadCorner( aLine, leftIdx ) )
661 {
662 VECTOR2I leftPt = aLine.CPoint( leftIdx );
663
664 if( !candidate || ( leftPt - aP ).SquaredEuclideanNorm() < ( *candidate - aP ).SquaredEuclideanNorm() )
665 {
666 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", leftIdx ) );
667 PNS_DBG( Dbg(), AddPoint, leftPt, GREEN, 100000, wxT( "drag-anchor-alt" ) );
668 return leftPt;
669 }
670 }
671
672 if( candidate.has_value() )
673 {
674 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", rightIdx ) );
675 PNS_DBG( Dbg(), AddPoint, *candidate, GREEN, 100000, wxT( "drag-anchor-alt" ) );
676 return *candidate;
677 }
678 }
679
680 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt bad; no alternative (vert=%d)", vertIdx ) );
681 return nearest;
682}
683
684
685bool DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
686{
687 WALKAROUND walkaround( aNode, Router() );
688 walkaround.SetSolidsOnly( false );
689 walkaround.SetDebugDecorator( Dbg() );
690 walkaround.SetLogger( Logger() );
691 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
692 walkaround.SetLengthLimit( true, 30.0 );
694
695 aWalk = aOrig;
696
697 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
698
700 {
701 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
702 return true;
703 }
704
705 return false;
706}
707
708
710{
711 bool ok = false;
712
713 // fixme: rewrite using shared_ptr...
714 if( m_lastNode )
715 {
716 delete m_lastNode;
717 m_lastNode = nullptr;
718 }
719
720 m_lastNode = m_preDragNode->Branch();
721
722 switch( m_mode )
723 {
724 case DM_SEGMENT:
725 case DM_CORNER:
726 {
727 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
728 LINE dragged( m_draggedLine );
729 LINE draggedWalk( m_draggedLine );
730 LINE origLine( m_draggedLine );
731
732 dragged.SetSnapThreshhold( thresh );
733
734 if( m_mode == DM_SEGMENT )
735 dragged.DragSegment( aP, m_draggedSegmentIndex );
736 else
737 dragged.DragCorner( aP, m_draggedSegmentIndex );
738
739 if ( m_world->CheckColliding( &dragged ) )
740 {
741 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
742 }
743 else
744 {
745 draggedWalk = dragged;
746 ok = true;
747 }
748
749 if( draggedWalk.CLine().PointCount() < 2 )
750 ok = false;
751
752 if( ok )
753 {
754 PNS_DBG( Dbg(), AddShape, &origLine.CLine(), BLUE, 50000, wxT( "drag-orig-line" ) );
755 PNS_DBG( Dbg(), AddShape, &draggedWalk.CLine(), CYAN, 75000, wxT( "drag-walk" ) );
756 m_lastNode->Remove( origLine );
757 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
758 }
759
760 break;
761 }
762 case DM_ARC:
763 {
764 LINE dragged( m_draggedLine );
765 LINE draggedWalk( m_draggedLine );
766 LINE origLine( m_draggedLine );
767
768 dragged.DragArc( aP, m_draggedSegmentIndex );
769
770 if( m_world->CheckColliding( &dragged ) )
771 {
772 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
773 }
774 else
775 {
776 draggedWalk = dragged;
777 ok = true;
778 }
779
780 if( draggedWalk.CLine().PointCount() < 2 )
781 ok = false;
782
783 if( ok )
784 {
785 m_lastNode->Remove( origLine );
786 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
787 }
788
789 break;
790 }
791 case DM_VIA: // fixme...
793 break;
794 }
795
796 m_dragStatus = ok;
797
798 return ok;
799}
800
801
803{
804
805 if( m_lastNode )
806 {
807 delete m_lastNode;
808 m_lastNode = nullptr;
809 }
810
811 switch( m_mode )
812 {
813 case DM_SEGMENT:
814 case DM_CORNER:
815 {
816 bool ok = false;
817 //TODO: Make threshold configurable
818 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0;
819 LINE draggedPreShove( m_draggedLine );
820 draggedPreShove.SetSnapThreshhold( thresh );
821
822 if( m_mode == DM_SEGMENT )
823 draggedPreShove.DragSegment( aP, m_draggedSegmentIndex );
824 else
825 draggedPreShove.DragCorner( aP, m_draggedSegmentIndex );
826
827 auto preShoveNode = m_shove->CurrentNode();
828
829 if( preShoveNode )
830 preShoveNode->Remove( draggedPreShove );
831
833
834 PNS_DBG( Dbg(), Message, wxString::Format( "drag seg index %d", m_draggedSegmentIndex ) );
835
836 if( m_mode == DM_CORNER && m_draggedSegmentIndex == 0 )
837 policy |= SHOVE::SHP_REVERSED;
838
839 m_shove->ClearHeads();
840 m_shove->AddHeads( draggedPreShove, policy );
841 ok = m_shove->Run() == SHOVE::SH_OK;
842
843 LINE draggedPostShove( draggedPreShove );
844
845 if( ok )
846 {
847 if( m_shove->HeadsModified() )
848 draggedPostShove = m_shove->GetModifiedHead( 0 );
849 }
850
851 m_lastNode = m_shove->CurrentNode()->Branch();
852
853 if( ok )
854 {
855 draggedPostShove.ClearLinks();
856 draggedPostShove.Unmark();
857 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
858 m_lastDragSolution = std::move( draggedPostShove );
859 }
860
861 m_dragStatus = ok;
862 break;
863 }
864
865 case DM_ARC:
866 {
867 bool ok = false;
868
869 LINE draggedPreShove( m_draggedLine );
870 draggedPreShove.DragArc( aP, m_draggedSegmentIndex );
871
872 // A collapsed arc drag can leave fewer than two points, which is not a valid
873 // shove head. Treat that as an unsuccessful shove (as dragWalkaround does) rather
874 // than feeding a degenerate line to AddHeads.
875 if( draggedPreShove.CLine().PointCount() >= 2 )
876 {
877 auto preShoveNode = m_shove->CurrentNode();
878
879 if( preShoveNode )
880 preShoveNode->Remove( draggedPreShove );
881
883
884 m_shove->ClearHeads();
885 m_shove->AddHeads( draggedPreShove, policy );
886 ok = m_shove->Run() == SHOVE::SH_OK;
887 }
888
889 LINE draggedPostShove( draggedPreShove );
890
891 if( ok && m_shove->HeadsModified() )
892 draggedPostShove = m_shove->GetModifiedHead( 0 );
893
894 m_lastNode = m_shove->CurrentNode()->Branch();
895
896 if( ok )
897 {
898 draggedPostShove.ClearLinks();
899 draggedPostShove.Unmark();
900 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
901 m_lastDragSolution = std::move( draggedPostShove );
902 }
903
904 m_dragStatus = ok;
905 break;
906 }
907
908 case DM_VIA:
909 {
910 VIA_HANDLE newVia;
911
912 // corner count limiter intended to avoid excessive optimization produces mediocre results for via shoving.
913 // this is a hack that disables it, before I figure out a more reliable solution
914 m_shove->DisablePostShoveOptimizations( OPTIMIZER::LIMIT_CORNER_COUNT );
915
916 m_shove->ClearHeads();
917 m_shove->AddHeads( m_draggedVia, aP, SHOVE::SHP_SHOVE );
918
919 SHOVE::SHOVE_STATUS st = m_shove->Run(); //ShoveDraggingVia( m_draggedVia, aP, newVia );
920
921 PNS_DBG( Dbg(), Message, wxString::Format("head-mod %d",
922 m_shove->HeadsModified() ? 1: 0 ) );
923
924 if( m_shove->HeadsModified() )
925 {
926 newVia = m_shove->GetModifiedHeadVia( 0 );
927
928 PNS_DBG( Dbg(), Message, wxString::Format("newvia %d %d %d %d",
929 newVia.pos.x,
930 newVia.pos.y,
931 newVia.layers.Start(),
932 newVia.layers.End()
933 ) );
934
935 m_draggedVia = newVia;
936 }
937
938
939 m_lastNode = m_shove->CurrentNode()->Branch();
940
941 m_draggedItems.Clear();
942
943 // If drag didn't work (i.e. dragged onto a collision) try walkaround instead
944 if( st != SHOVE::SH_OK )
946 else
947 m_dragStatus = true;
948
949 break;
950 }
951 }
952
953 return m_dragStatus;
954}
955
956
957bool DRAGGER::FixRoute( bool aForceCommit )
958{
959 NODE* node = CurrentNode();
960
961 if( node )
962 {
963 if( m_dragStatus )
964 {
965 Router()->CommitRouting( node );
966 return true;
967 }
968 else if( m_forceMarkObstaclesMode )
969 {
970 if( aForceCommit )
971 {
972 Router()->CommitRouting( node );
973 return true;
974 }
975
976 return false;
977 }
978 else
979 {
980 // If collisions exist, we can fix in shove/smart mode because all tracks to be
981 // committed will be in valid positions (even if the current routing solution to
982 // the mouse cursor is invalid).
984 node = CurrentNode();
985
986 if( node && m_dragStatus )
987 {
988 Router()->CommitRouting( node );
989 return true;
990 }
991 }
992 }
993
994 return false;
995}
996
997
998bool DRAGGER::Drag( const VECTOR2I& aP )
999{
1000 m_mouseTrailTracer.AddTrailPoint( aP );
1001
1002 bool firstDrag = m_lastNode == nullptr;
1003 bool ret = false;
1004
1006 {
1007 ret = dragMarkObstacles( aP );
1008 }
1009 else
1010 {
1011 switch( m_currentMode )
1012 {
1013 case RM_MarkObstacles: ret = dragMarkObstacles( aP ); break;
1014 case RM_Shove: ret = dragShove( aP ); break;
1015 case RM_Walkaround: ret = dragWalkaround( aP ); break;
1016 default: break;
1017 }
1018 }
1019
1020 if( ret )
1021 {
1022 m_lastValidPoint = aP;
1023 }
1024 else
1025 {
1026 if( firstDrag )
1027 {
1028 // First collision resolution failed, switch to highlight mode
1030
1031 ret = dragMarkObstacles( aP );
1032
1033 if( ret )
1034 m_lastValidPoint = aP;
1035 }
1036 else if( m_lastNode )
1037 {
1038 // Restore last solution
1039 NODE* parent = m_lastNode->GetParent()->Branch();
1040 delete m_lastNode;
1041 m_lastNode = parent;
1042 m_draggedItems.Clear();
1043 m_lastDragSolution.ClearLinks();
1045 }
1046 }
1047
1048 return ret;
1049}
1050
1051
1053{
1054 return m_lastNode ? m_lastNode : m_world;
1055}
1056
1057
1059{
1060 return m_draggedItems;
1061}
1062
1063}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:931
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
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.
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
bool pointHasBadCorner(const SHAPE_LINE_CHAIN &aLine, int aVertexIndex) const
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)
When the cursor is unreachable (e.g.
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)
VECTOR2I bestAnchorForPoint(const SHAPE_LINE_CHAIN &aLine, const VECTOR2I &aP) const
Returns true if the vertex at aVertexIndex has a non-obtuse corner.
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:911
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:145
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:884
void SetSnapThreshhold(int aThreshhold)
Definition pns_line.h:256
virtual void Unmark(int aMarker=-1) const override
Definition pns_line.cpp:184
int PointCount() const
Definition pns_line.h:149
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition pns_line.cpp:898
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:243
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.
@ REQUIRE_OBTUSE_ANGLES
Try to prevent 90-degree or acute corners in a drag.
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:249
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:67
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
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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 & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
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:426
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:411
#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:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682