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/kicad_algo.h>
25#include <core/typeinfo.h>
26#include <advanced_config.h>
27#include <base_units.h>
28#include <geometry/eda_angle.h>
29#include <geometry/shape_arc.h>
30#include <wx/translation.h>
31
32#include "pns_arc.h"
33#include "pns_segment.h"
34#include "pns_shove.h"
35#include "pns_router.h"
36#include "pns_debug_decorator.h"
37#include "pns_walkaround.h"
38
39
40namespace PNS {
41
43 DRAG_ALGO( aRouter ),
44 m_initialVia( {} ),
45 m_draggedVia( {} )
46{
47 m_world = nullptr;
48 m_lastNode = nullptr;
49 m_mode = DM_SEGMENT;
50 m_draggedSegmentIndex = 0;
51 m_dragStatus = false;
52 m_currentMode = RM_MarkObstacles;
53 m_freeAngleMode = false;
54 m_forceMarkObstaclesMode = false;
55}
56
57
61
62
63bool DRAGGER::propagateViaForces( NODE* node, std::set<VIA*>& vias )
64{
65 VIA* via = *vias.begin();
66
67 VECTOR2I force;
68 VECTOR2I lead = -m_mouseTrailTracer.GetTrailLeadVector();
69
70 const int iterLimit = Settings().ViaForcePropIterationLimit();
71
72 if( via->PushoutForce( node, lead, force, ITEM::ANY_T, iterLimit ) )
73 {
74 via->SetPos( via->Pos() + force );
75 return true;
76 }
77
78 return false;
79}
80
81
83{
84 int w2 = aSeg->Width() / 2;
85
86 auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm();
87 auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm();
88
89 VECTOR2I psnap;
90
91 if( distA <= w2 )
92 {
93 psnap = aSeg->Seg().A;
94 }
95 else if( distB <= w2 )
96 {
97 psnap = aSeg->Seg().B;
98 }
99 else
100 {
101 return nullptr;
102 }
103
104 const JOINT *jt = m_world->FindJoint( psnap, aSeg );
105
106 if ( !jt )
107 return nullptr;
108
109 for( ITEM* item : jt->LinkList() )
110 {
111 if( item->IsVirtual() && item->OfKind( ITEM::VIA_T ))
112 return static_cast<VVIA*>( item );
113 }
114
115 return nullptr;
116}
117
118
120{
121 int w2 = aSeg->Width() / 2;
122
123 m_draggedLine = m_world->AssembleLine( aSeg, &m_draggedSegmentIndex );
125
126 auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm();
127 auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm();
128
129 if( distA < w2 || distB < w2 )
130 {
132
133 if( distB <= distA )
135 }
136 else if( m_freeAngleMode )
137 {
138 if( distB < distA &&
139 ( m_draggedSegmentIndex < m_draggedLine.PointCount() - 2 ) &&
140 ( !m_draggedLine.CLine().IsPtOnArc( static_cast<size_t>(m_draggedSegmentIndex) + 1 ) ) )
141 {
143 }
144
146 }
147 else
148 {
150 }
151
152 return true;
153}
154
155
156bool DRAGGER::startDragArc( const VECTOR2D& aP, ARC* aArc )
157{
158 EDA_ANGLE maxDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
159
160 EDA_ANGLE centralAngle( std::abs( aArc->CArc().GetCentralAngle().AsDegrees() ), DEGREES_T );
161
162 if( centralAngle + maxDeviation >= ANGLE_180 )
163 {
164 EDA_ANGLE limit = ANGLE_180 - maxDeviation;
166 wxString::Format( _( "Unable to drag arc tracks of %.1f degrees or greater." ), limit.AsDegrees() ) );
167 return false;
168 }
169
170 int probeIdx = 0;
171 LINE probe = m_world->AssembleLine( aArc, &probeIdx );
172
173 ssize_t arcIdx = -1;
174 int firstArcPt = -1;
175 int lastArcPt = -1;
176
177 for( int i = 0; i < probe.PointCount(); i++ )
178 {
179 ssize_t a = probe.CLine().ArcIndex( i );
180
181 if( a < 0 )
182 continue;
183
184 if( arcIdx < 0 )
185 arcIdx = a;
186
187 if( a == arcIdx )
188 {
189 if( firstArcPt < 0 )
190 firstArcPt = i;
191
192 lastArcPt = i;
193 }
194 }
195
196 bool isolatedStart = ( firstArcPt == 0 );
197 bool isolatedEnd = ( lastArcPt == probe.PointCount() - 1 );
198
199 if( isolatedStart || isolatedEnd )
200 {
201 int maxStubIU = KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
202 int stubLen = std::max( 1, maxStubIU / 2 );
203
204 const SHAPE_ARC& sharc = aArc->CArc();
205 VECTOR2I center = sharc.GetCenter();
206 VECTOR2I mid = sharc.GetArcMid();
207
208 auto outwardTangent = [&]( const VECTOR2I& aEndpoint ) -> VECTOR2I
209 {
210 VECTOR2I radial = aEndpoint - center;
211 VECTOR2I perp( -radial.y, radial.x );
212 VECTOR2I toMid = mid - aEndpoint;
213
214 if( perp.Dot( toMid ) > 0 )
215 perp = VECTOR2I( radial.y, -radial.x );
216
217 double mag = std::hypot( (double) perp.x, (double) perp.y );
218
219 if( mag <= 0 )
220 return VECTOR2I( stubLen, 0 );
221
222 // A unit vector first, since a coordinate times the stub length does not fit an int
223 return VECTOR2I( KiROUND( perp.x / mag * stubLen ), KiROUND( perp.y / mag * stubLen ) );
224 };
225
226 if( isolatedStart )
227 {
228 VECTOR2I p0 = sharc.GetP0();
229 VECTOR2I stubFar = p0 + outwardTangent( p0 );
230 auto stub = std::make_unique<SEGMENT>( SEG( stubFar, p0 ), aArc->Net() );
231 stub->SetWidth( aArc->Width() );
232 stub->SetLayers( aArc->Layers() );
233 m_preDragNode->Add( std::move( stub ) );
234 }
235
236 if( isolatedEnd )
237 {
238 VECTOR2I p1 = sharc.GetP1();
239 VECTOR2I stubFar = p1 + outwardTangent( p1 );
240 auto stub = std::make_unique<SEGMENT>( SEG( p1, stubFar ), aArc->Net() );
241 stub->SetWidth( aArc->Width() );
242 stub->SetLayers( aArc->Layers() );
243 m_preDragNode->Add( std::move( stub ) );
244 }
245
246 m_draggedLine = m_preDragNode->AssembleLine( aArc, &m_draggedSegmentIndex );
247 }
248 else
249 {
250 m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex );
251 }
252
253 m_mode = DM_ARC;
254
255 return true;
256}
257
258
260{
261 m_initialVia = aVia->MakeHandle();
263
264 m_mode = DM_VIA;
265
266 return true;
267}
268
270{
271 ITEM_SET rv;
272
273 const JOINT* jt = aNode->FindJoint( handle.pos, handle.layers.Start(), handle.net );
274
275 if( !jt )
276 return rv;
277
278 bool foundVia = false;
279
280 for( ITEM* item : jt->LinkList() )
281 {
282 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
283 {
284 int segIndex;
285 LINKED_ITEM* seg = ( LINKED_ITEM*) item;
286 LINE l = aNode->AssembleLine( seg, &segIndex );
287
288 if( segIndex != 0 )
289 l.Reverse();
290
291 rv.Add( l );
292 }
293 else if( item->OfKind( ITEM::VIA_T ) )
294 {
295 if( !foundVia )
296 {
297 rv.Add( item );
298 foundVia = true;
299 }
300 }
301 }
302
303 return rv;
304}
305
306bool DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
307{
308 if( aPrimitives.Empty() )
309 return false;
310
311 ITEM* startItem = aPrimitives[0];
312
313 m_lastNode = nullptr;
314 m_draggedItems.Clear();
318 m_lastValidPoint = aP;
319
320 m_mouseTrailTracer.Clear();
321 m_mouseTrailTracer.AddTrailPoint( aP );
322
323 m_preDragNode = m_world->Branch();
324
326 {
327 m_shove = std::make_unique<SHOVE>( m_preDragNode, Router() );
328 m_shove->SetLogger( Logger() );
329 m_shove->SetDebugDecorator( Dbg() );
330 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE );
331 }
332
333 startItem->Unmark( MK_LOCKED );
334
335 PNS_DBG( Dbg(), Message, wxString::Format( "StartDragging: item %p [kind %d]",
336 startItem, (int) startItem->Kind() ) );
337
338 switch( startItem->Kind() )
339 {
340 case ITEM::SEGMENT_T:
341 {
342 SEGMENT* seg = static_cast<SEGMENT*>( startItem );
343 VVIA* vvia = checkVirtualVia( aP, seg );
344
345 if( vvia )
346 return startDragVia( vvia );
347 else
348 return startDragSegment( aP, seg );
349 }
350 case ITEM::VIA_T:
351 return startDragVia( static_cast<VIA*>( startItem ) );
352
353 case ITEM::ARC_T:
354 return startDragArc( aP, static_cast<ARC*>( startItem ) );
355
356 default:
357 return false;
358 }
359}
360
361
363{
364 m_mode = static_cast<int>( aMode );
365}
366
367
369{
370 return static_cast<PNS::DRAG_MODE>( m_mode );
371}
372
373
374bool DRAGGER::IsDragOrigin( const ITEM* aItem ) const
375{
376 if( !aItem )
377 return false;
378
379 if( m_mode != DM_VIA )
380 {
381 const LINKED_ITEM* li = dynamic_cast<const LINKED_ITEM*>( aItem );
382 return li && m_draggedLine.ContainsLink( li );
383 }
384
385 if( !m_initialVia.valid || !m_world || !aItem->OfKind( ITEM::VIA_T | ITEM::SEGMENT_T | ITEM::ARC_T ) )
386 return false;
387
388 const JOINT* jt = m_world->FindJoint( m_initialVia.pos, m_initialVia.layers.Start(), m_initialVia.net );
389
390 return jt && alg::contains( jt->LinkList(), aItem );
391}
392
393
394const std::vector<NET_HANDLE> DRAGGER::CurrentNets() const
395{
396 if( m_mode == PNS::DM_VIA )
397 return std::vector<NET_HANDLE>( 1, m_draggedVia.net );
398 else
399 return std::vector<NET_HANDLE>( 1, m_draggedLine.Net() );
400}
401
402
404{
405 // fixme: rewrite using shared_ptr...
406 if( m_lastNode )
407 {
408 delete m_lastNode;
409 m_lastNode = nullptr;
410 }
411
412 m_lastNode = m_preDragNode->Branch();
413
414 switch( m_mode )
415 {
416 case DM_SEGMENT:
417 case DM_CORNER:
418 {
419 //TODO: Make threshold configurable
420 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
421 LINE origLine( m_draggedLine );
422 LINE dragged( m_draggedLine );
423 dragged.SetSnapThreshhold( thresh );
424 dragged.ClearLinks();
425
426 if( m_mode == DM_SEGMENT )
427 dragged.DragSegment( aP, m_draggedSegmentIndex );
428 else
430
431 m_lastNode->Remove( origLine );
432 m_lastNode->Add( dragged );
433
434 m_draggedItems.Clear();
435 m_draggedItems.Add( dragged );
436
437 break;
438 }
439
440 case DM_ARC:
441 {
442 LINE origLine( m_draggedLine );
443 LINE dragged( m_draggedLine );
444 dragged.ClearLinks();
445
446 dragged.DragArc( aP, m_draggedSegmentIndex );
447
448 // A collapsed arc drag leaves an empty chain, so Add() is a no-op and the arc
449 // is simply dropped from the route, which is the intended outcome here.
450 m_lastNode->Remove( origLine );
451 m_lastNode->Add( dragged );
452
453 m_draggedItems.Clear();
454 m_draggedItems.Add( dragged );
455
456 break;
457 }
458
459 case DM_VIA: // fixme...
461
462 break;
463 }
464
465 if( Settings().AllowDRCViolations() )
466 m_dragStatus = true;
467 else
468 m_dragStatus = !m_lastNode->CheckColliding( m_draggedItems );
469
470 return true;
471}
472
473
474bool DRAGGER::dragViaMarkObstacles( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
475{
476 m_draggedItems.Clear();
477
478 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
479
480 if( fanout.Empty() )
481 return true;
482
483 for( ITEM* item : fanout.Items() )
484 {
485 if( const LINE* l = dyn_cast<const LINE*>( item ) )
486 {
487 LINE origLine( *l );
488 LINE draggedLine( *l );
489
490 draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode );
491 draggedLine.ClearLinks();
492
493 m_draggedItems.Add( draggedLine );
494
495 m_lastNode->Remove( origLine );
496 m_lastNode->Add( draggedLine );
497 }
498 else if ( VIA *via = dyn_cast<VIA*>( item ) )
499 {
500 auto nvia = Clone( *via );
501
502 nvia->SetPos( aP );
503 m_draggedItems.Add( nvia.get() );
504
505 m_lastNode->Remove( via );
506 m_lastNode->Add( std::move( nvia ) );
507 }
508 }
509
510 return true;
511}
512
513
514bool DRAGGER::dragViaWalkaround( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
515{
516 m_draggedItems.Clear();
517
518 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
519
520 if( fanout.Empty() )
521 return true;
522
523 bool viaPropOk = false;
524 VECTOR2I viaTargetPos;
525
526 for( ITEM* item : fanout.Items() )
527 {
528 if( VIA *via = dyn_cast<VIA*>( item ) )
529 {
530 std::unique_ptr<VIA> draggedVia = Clone( *via );
531
532 draggedVia->SetPos( aP );
533 m_draggedItems.Add( draggedVia.get() );
534
535 std::set<VIA*> vias;
536
537 vias.insert( draggedVia.get() );
538
539 m_lastNode->Remove( via );
540
541 bool ok = propagateViaForces( m_lastNode, vias );
542
543 if( ok )
544 {
545 viaTargetPos = draggedVia->Pos();
546 viaPropOk = true;
547 m_lastNode->Add( std::move(draggedVia) );
548 }
549 }
550 }
551
552 if( !viaPropOk ) // can't force-propagate the via? bummer...
553 return false;
554
555 for( ITEM* item : fanout.Items() )
556 {
557 if( const LINE* l = dyn_cast<const LINE*>( item ) )
558 {
559 LINE origLine( *l );
560 LINE draggedLine( *l );
561 LINE walkLine( *l );
562
563 draggedLine.DragCorner( viaTargetPos, origLine.CLine().Find( aHandle.pos ),
565 draggedLine.ClearLinks();
566
567 if ( m_world->CheckColliding( &draggedLine ) )
568 {
569 bool ok = tryWalkaround( m_lastNode, draggedLine, walkLine );
570
571 if( !ok )
572 return false;
573
574 m_lastNode->Remove( origLine );
575 optimizeAndUpdateDraggedLine( walkLine, origLine, aP );
576 }
577 else
578 {
579 m_draggedItems.Add( draggedLine );
580
581 m_lastNode->Remove( origLine );
582 m_lastNode->Add( draggedLine );
583 }
584 }
585 }
586
587 return true;
588}
589
590
591void DRAGGER::optimizeAndUpdateDraggedLine( LINE& aDragged, const LINE& aOrig, const VECTOR2I& aP )
592{
593 LINE draggedPostOpt, origLine( aOrig );
594
595 aDragged.ClearLinks();
596 aDragged.Unmark();
597
598 OPTIMIZER optimizer( m_lastNode );
599
600 int effort = OPTIMIZER::MERGE_SEGMENTS;
601
602 if( Settings().SmoothDraggedSegments() )
604
605 if( Settings().GetRestrictAngles() )
607
608 optimizer.SetEffortLevel( effort );
609
610 VECTOR2I anchor( aP );
611
612 if( aDragged.CLine().Find( aP ) < 0 )
613 anchor = bestAnchorForPoint( aDragged.CLine(), aP );
614
615 optimizer.SetPreserveVertex( anchor );
616 aDragged.Line().Split( anchor );
617
618 PNS_DBG( Dbg(), AddPoint, anchor, YELLOW, 100000, wxT( "drag-anchor" ) );
619
620 if( !Settings().GetOptimizeEntireDraggedTrack() )
621 {
622 OPT_BOX2I affectedArea = aDragged.ChangedArea( &aOrig );
623
624 if( !affectedArea )
625 affectedArea = BOX2I( aP ); // No valid area yet? set to minimum to disable optimization
626
627 PNS_DBG( Dbg(), AddShape, *affectedArea, RED, 0, wxT( "drag-affected-area" ) );
628
629 optimizer.SetRestrictArea( *affectedArea );
630 }
631
632 PNS_DBG( Dbg(), AddItem, &aDragged, RED, 0, wxT( "drag-preopt" ) );
633
634 optimizer.Optimize( &aDragged, &draggedPostOpt, &origLine );
635 aDragged = draggedPostOpt;
636 PNS_DBG( Dbg(), AddItem, &aDragged, GREEN, 0, wxT( "drag-postopt" ) );
637
638 m_lastNode->Add( draggedPostOpt );
639 m_draggedItems.Clear();
640 m_draggedItems.Add( draggedPostOpt );
641}
642
643
644bool DRAGGER::pointHasBadCorner( const SHAPE_LINE_CHAIN& aLine, int aVertexIndex ) const
645{
646 if( aVertexIndex <= 0 || aVertexIndex >= aLine.PointCount() - 1 )
647 return false;
648
649 SEG segBefore( aLine.CPoint( aVertexIndex - 1 ), aLine.CPoint( aVertexIndex ) );
650 SEG segAfter( aLine.CPoint( aVertexIndex ), aLine.CPoint( aVertexIndex + 1 ) );
651
652 DIRECTION_45 dirBefore( segBefore );
653 DIRECTION_45 dirAfter( segAfter );
654
655 return dirBefore.Angle( dirAfter ) & ( DIRECTION_45::ANG_ACUTE
658}
659
660
662{
663 VECTOR2I nearest = aLine.NearestPoint( aP );
664 int vertIdx = aLine.Find( nearest );
665
666 if( vertIdx < 0 || !pointHasBadCorner( aLine, vertIdx ) )
667 {
668 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt used (vert=%d)", vertIdx ) );
669 return nearest;
670 }
671
672 for( int offset = 1; offset < aLine.PointCount(); offset++ )
673 {
674 int rightIdx = vertIdx + offset;
675 std::optional<VECTOR2I> candidate;
676
677 if( rightIdx < aLine.PointCount() && !pointHasBadCorner( aLine, rightIdx ) )
678 candidate = aLine.CPoint( rightIdx );
679
680 int leftIdx = vertIdx - offset;
681
682 if( leftIdx >= 0 && !pointHasBadCorner( aLine, leftIdx ) )
683 {
684 VECTOR2I leftPt = aLine.CPoint( leftIdx );
685
686 if( !candidate || ( leftPt - aP ).SquaredEuclideanNorm() < ( *candidate - aP ).SquaredEuclideanNorm() )
687 {
688 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", leftIdx ) );
689 PNS_DBG( Dbg(), AddPoint, leftPt, GREEN, 100000, wxT( "drag-anchor-alt" ) );
690 return leftPt;
691 }
692 }
693
694 if( candidate.has_value() )
695 {
696 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", rightIdx ) );
697 PNS_DBG( Dbg(), AddPoint, *candidate, GREEN, 100000, wxT( "drag-anchor-alt" ) );
698 return *candidate;
699 }
700 }
701
702 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt bad; no alternative (vert=%d)", vertIdx ) );
703 return nearest;
704}
705
706
707bool DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
708{
709 WALKAROUND walkaround( aNode, Router() );
710 walkaround.SetSolidsOnly( false );
711 walkaround.SetDebugDecorator( Dbg() );
712 walkaround.SetLogger( Logger() );
713 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
714 walkaround.SetLengthLimit( true, 30.0 );
716
717 aWalk = aOrig;
718
719 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
720
722 {
723 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
724 return true;
725 }
726
727 return false;
728}
729
730
732{
733 bool ok = false;
734
735 // fixme: rewrite using shared_ptr...
736 if( m_lastNode )
737 {
738 delete m_lastNode;
739 m_lastNode = nullptr;
740 }
741
742 m_lastNode = m_preDragNode->Branch();
743
744 switch( m_mode )
745 {
746 case DM_SEGMENT:
747 case DM_CORNER:
748 {
749 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
750 LINE dragged( m_draggedLine );
751 LINE draggedWalk( m_draggedLine );
752 LINE origLine( m_draggedLine );
753
754 dragged.SetSnapThreshhold( thresh );
755
756 if( m_mode == DM_SEGMENT )
757 dragged.DragSegment( aP, m_draggedSegmentIndex );
758 else
759 dragged.DragCorner( aP, m_draggedSegmentIndex );
760
761 if ( m_world->CheckColliding( &dragged ) )
762 {
763 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
764 }
765 else
766 {
767 draggedWalk = dragged;
768 ok = true;
769 }
770
771 if( draggedWalk.CLine().PointCount() < 2 )
772 ok = false;
773
774 if( ok )
775 {
776 PNS_DBG( Dbg(), AddShape, &origLine.CLine(), BLUE, 50000, wxT( "drag-orig-line" ) );
777 PNS_DBG( Dbg(), AddShape, &draggedWalk.CLine(), CYAN, 75000, wxT( "drag-walk" ) );
778 m_lastNode->Remove( origLine );
779 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
780 }
781
782 break;
783 }
784 case DM_ARC:
785 {
786 LINE dragged( m_draggedLine );
787 LINE draggedWalk( m_draggedLine );
788 LINE origLine( m_draggedLine );
789
790 dragged.DragArc( aP, m_draggedSegmentIndex );
791
792 if( m_world->CheckColliding( &dragged ) )
793 {
794 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
795 }
796 else
797 {
798 draggedWalk = dragged;
799 ok = true;
800 }
801
802 if( draggedWalk.CLine().PointCount() < 2 )
803 ok = false;
804
805 if( ok )
806 {
807 m_lastNode->Remove( origLine );
808 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
809 }
810
811 break;
812 }
813 case DM_VIA: // fixme...
815 break;
816 }
817
818 m_dragStatus = ok;
819
820 return ok;
821}
822
823
825{
826
827 if( m_lastNode )
828 {
829 delete m_lastNode;
830 m_lastNode = nullptr;
831 }
832
833 switch( m_mode )
834 {
835 case DM_SEGMENT:
836 case DM_CORNER:
837 {
838 bool ok = false;
839 //TODO: Make threshold configurable
840 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0;
841 LINE draggedPreShove( m_draggedLine );
842 draggedPreShove.SetSnapThreshhold( thresh );
843
844 if( m_mode == DM_SEGMENT )
845 draggedPreShove.DragSegment( aP, m_draggedSegmentIndex );
846 else
847 draggedPreShove.DragCorner( aP, m_draggedSegmentIndex );
848
849 auto preShoveNode = m_shove->CurrentNode();
850
851 if( preShoveNode )
852 preShoveNode->Remove( draggedPreShove );
853
855
856 PNS_DBG( Dbg(), Message, wxString::Format( "drag seg index %d", m_draggedSegmentIndex ) );
857
858 if( m_mode == DM_CORNER && m_draggedSegmentIndex == 0 )
859 policy |= SHOVE::SHP_REVERSED;
860
861 m_shove->ClearHeads();
862 m_shove->AddHeads( draggedPreShove, policy );
863 ok = m_shove->Run() == SHOVE::SH_OK;
864
865 LINE draggedPostShove( draggedPreShove );
866
867 if( ok )
868 {
869 if( m_shove->HeadsModified() )
870 draggedPostShove = m_shove->GetModifiedHead( 0 );
871 }
872
873 m_lastNode = m_shove->CurrentNode()->Branch();
874
875 if( ok )
876 {
877 draggedPostShove.ClearLinks();
878 draggedPostShove.Unmark();
879 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
880 m_lastDragSolution = std::move( draggedPostShove );
881 }
882
883 m_dragStatus = ok;
884 break;
885 }
886
887 case DM_ARC:
888 {
889 bool ok = false;
890
891 LINE draggedPreShove( m_draggedLine );
892 draggedPreShove.DragArc( aP, m_draggedSegmentIndex );
893
894 // A collapsed arc drag can leave fewer than two points, which is not a valid
895 // shove head. Treat that as an unsuccessful shove (as dragWalkaround does) rather
896 // than feeding a degenerate line to AddHeads.
897 if( draggedPreShove.CLine().PointCount() >= 2 )
898 {
899 auto preShoveNode = m_shove->CurrentNode();
900
901 if( preShoveNode )
902 preShoveNode->Remove( draggedPreShove );
903
905
906 m_shove->ClearHeads();
907 m_shove->AddHeads( draggedPreShove, policy );
908 ok = m_shove->Run() == SHOVE::SH_OK;
909 }
910
911 LINE draggedPostShove( draggedPreShove );
912
913 if( ok && m_shove->HeadsModified() )
914 draggedPostShove = m_shove->GetModifiedHead( 0 );
915
916 m_lastNode = m_shove->CurrentNode()->Branch();
917
918 if( ok )
919 {
920 draggedPostShove.ClearLinks();
921 draggedPostShove.Unmark();
922 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
923 m_lastDragSolution = std::move( draggedPostShove );
924 }
925
926 m_dragStatus = ok;
927 break;
928 }
929
930 case DM_VIA:
931 {
932 VIA_HANDLE newVia;
933
934 // corner count limiter intended to avoid excessive optimization produces mediocre results for via shoving.
935 // this is a hack that disables it, before I figure out a more reliable solution
936 m_shove->DisablePostShoveOptimizations( OPTIMIZER::LIMIT_CORNER_COUNT );
937
938 m_shove->ClearHeads();
939 m_shove->AddHeads( m_draggedVia, aP, SHOVE::SHP_SHOVE );
940
941 SHOVE::SHOVE_STATUS st = m_shove->Run(); //ShoveDraggingVia( m_draggedVia, aP, newVia );
942
943 PNS_DBG( Dbg(), Message, wxString::Format("head-mod %d",
944 m_shove->HeadsModified() ? 1: 0 ) );
945
946 if( m_shove->HeadsModified() )
947 {
948 newVia = m_shove->GetModifiedHeadVia( 0 );
949
950 PNS_DBG( Dbg(), Message, wxString::Format("newvia %d %d %d %d",
951 newVia.pos.x,
952 newVia.pos.y,
953 newVia.layers.Start(),
954 newVia.layers.End()
955 ) );
956
957 m_draggedVia = newVia;
958 }
959
960
961 m_lastNode = m_shove->CurrentNode()->Branch();
962
963 m_draggedItems.Clear();
964
965 // If drag didn't work (i.e. dragged onto a collision) try walkaround instead
966 if( st != SHOVE::SH_OK )
968 else
969 m_dragStatus = true;
970
971 break;
972 }
973 }
974
975 return m_dragStatus;
976}
977
978
979bool DRAGGER::FixRoute( bool aForceCommit )
980{
981 NODE* node = CurrentNode();
982
983 if( node )
984 {
985 if( m_dragStatus )
986 {
987 Router()->CommitRouting( node );
988 return true;
989 }
990 else if( m_forceMarkObstaclesMode )
991 {
992 if( aForceCommit )
993 {
994 Router()->CommitRouting( node );
995 return true;
996 }
997
998 return false;
999 }
1000 else
1001 {
1002 // If collisions exist, we can fix in shove/smart mode because all tracks to be
1003 // committed will be in valid positions (even if the current routing solution to
1004 // the mouse cursor is invalid).
1006 node = CurrentNode();
1007
1008 if( node && m_dragStatus )
1009 {
1010 Router()->CommitRouting( node );
1011 return true;
1012 }
1013 }
1014 }
1015
1016 return false;
1017}
1018
1019
1020bool DRAGGER::Drag( const VECTOR2I& aP )
1021{
1022 m_mouseTrailTracer.AddTrailPoint( aP );
1023
1024 bool firstDrag = m_lastNode == nullptr;
1025 bool ret = false;
1026
1028 {
1029 ret = dragMarkObstacles( aP );
1030 }
1031 else
1032 {
1033 switch( m_currentMode )
1034 {
1035 case RM_MarkObstacles: ret = dragMarkObstacles( aP ); break;
1036 case RM_Shove: ret = dragShove( aP ); break;
1037 case RM_Walkaround: ret = dragWalkaround( aP ); break;
1038 default: break;
1039 }
1040 }
1041
1042 if( ret )
1043 {
1044 m_lastValidPoint = aP;
1045 }
1046 else
1047 {
1048 if( firstDrag )
1049 {
1050 // First collision resolution failed, switch to highlight mode
1052
1053 ret = dragMarkObstacles( aP );
1054
1055 if( ret )
1056 m_lastValidPoint = aP;
1057 }
1058 else if( m_lastNode )
1059 {
1060 // Restore last solution
1061 NODE* parent = m_lastNode->GetParent()->Branch();
1062 delete m_lastNode;
1063 m_lastNode = parent;
1064 m_draggedItems.Clear();
1065 m_lastDragSolution.ClearLinks();
1067 }
1068 }
1069
1070 return ret;
1071}
1072
1073
1075{
1076 return m_lastNode ? m_lastNode : m_world;
1077}
1078
1079
1081{
1082 return m_draggedItems;
1083}
1084
1085}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
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:115
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
void SetLogger(LOGGER *aLogger)
virtual LOGGER * Logger()
Return the logger object, allowing to dump geometry to a file.
ROUTER * Router() const
Return the instance of our router.
ROUTING_SETTINGS & Settings() const
Return current router settings.
DEBUG_DECORATOR * Dbg() const
int Width() const override
Definition pns_arc.h:88
const SHAPE_ARC & CArc() const
Definition pns_arc.h:116
ITEM_SET m_draggedItems
Contains the list of items that are currently modified by the dragger.
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
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 IsDragOrigin(const ITEM *aItem) const
Return true if aItem is a world item that moves with the drag: a link of the original line,...
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
Returns true if the vertex at aVertexIndex has a non-obtuse corner.
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
If true, moves the connection lines without maintaining 45 degrees corners.
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
When the cursor is unreachable (e.g.
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:266
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
bool OfKind(int aKindMask) const
Definition pns_item.h:181
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:307
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:905
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:149
SHAPE_LINE_CHAIN & Line()
Modifiable accessor to the underlying shape.
Definition pns_line.h:148
void DragCorner(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false, DIRECTION_45 aPreferredEndingDirection=DIRECTION_45())
Definition pns_line.cpp:878
void SetSnapThreshhold(int aThreshhold)
Definition pns_line.h:262
virtual void Unmark(int aMarker=-1) const override
Definition pns_line.cpp:178
int PointCount() const
Definition pns_line.h:152
void DragSegment(const VECTOR2I &aP, int aIndex, bool aFreeAngle=false)
Definition pns_line.cpp:892
void Reverse()
Reverse the point/vertex order.
Keep the router "world" - i.e.
Definition pns_node.h:244
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))
A quick shortcut to optimize a line without creating and setting up an optimizer.
@ 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:260
void CommitRouting()
bool SmoothDraggedSegments() const
Return true if smoothing segments during dragging is enabled.
PNS_MODE Mode() const
Return 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:326
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.
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:567
@ 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:30
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
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:77
@ DM_CORNER
Definition pns_router.h:78
@ DM_FREE_ANGLE
Definition pns_router.h:81
@ DM_VIA
Definition pns_router.h:80
@ DM_SEGMENT
Definition pns_router.h:79
@ DM_ARC
Definition pns_router.h:82
@ MK_LOCKED
Definition pns_item.h:45
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition pns_item.h:348
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
#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:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707