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.x * toMid.x + perp.y * toMid.y > 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 return VECTOR2I( KiROUND( perp.x * stubLen / mag ), KiROUND( perp.y * stubLen / mag ) );
223 };
224
225 if( isolatedStart )
226 {
227 VECTOR2I p0 = sharc.GetP0();
228 VECTOR2I stubFar = p0 + outwardTangent( p0 );
229 auto stub = std::make_unique<SEGMENT>( SEG( stubFar, p0 ), aArc->Net() );
230 stub->SetWidth( aArc->Width() );
231 stub->SetLayers( aArc->Layers() );
232 m_preDragNode->Add( std::move( stub ) );
233 }
234
235 if( isolatedEnd )
236 {
237 VECTOR2I p1 = sharc.GetP1();
238 VECTOR2I stubFar = p1 + outwardTangent( p1 );
239 auto stub = std::make_unique<SEGMENT>( SEG( p1, stubFar ), aArc->Net() );
240 stub->SetWidth( aArc->Width() );
241 stub->SetLayers( aArc->Layers() );
242 m_preDragNode->Add( std::move( stub ) );
243 }
244
245 m_draggedLine = m_preDragNode->AssembleLine( aArc, &m_draggedSegmentIndex );
246 }
247 else
248 {
249 m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex );
250 }
251
252 m_mode = DM_ARC;
253
254 return true;
255}
256
257
259{
260 m_initialVia = aVia->MakeHandle();
262
263 m_mode = DM_VIA;
264
265 return true;
266}
267
269{
270 ITEM_SET rv;
271
272 const JOINT* jt = aNode->FindJoint( handle.pos, handle.layers.Start(), handle.net );
273
274 if( !jt )
275 return rv;
276
277 bool foundVia = false;
278
279 for( ITEM* item : jt->LinkList() )
280 {
281 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
282 {
283 int segIndex;
284 LINKED_ITEM* seg = ( LINKED_ITEM*) item;
285 LINE l = aNode->AssembleLine( seg, &segIndex );
286
287 if( segIndex != 0 )
288 l.Reverse();
289
290 rv.Add( l );
291 }
292 else if( item->OfKind( ITEM::VIA_T ) )
293 {
294 if( !foundVia )
295 {
296 rv.Add( item );
297 foundVia = true;
298 }
299 }
300 }
301
302 return rv;
303}
304
305bool DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
306{
307 if( aPrimitives.Empty() )
308 return false;
309
310 ITEM* startItem = aPrimitives[0];
311
312 m_lastNode = nullptr;
313 m_draggedItems.Clear();
317 m_lastValidPoint = aP;
318
319 m_mouseTrailTracer.Clear();
320 m_mouseTrailTracer.AddTrailPoint( aP );
321
322 m_preDragNode = m_world->Branch();
323
325 {
326 m_shove = std::make_unique<SHOVE>( m_preDragNode, Router() );
327 m_shove->SetLogger( Logger() );
328 m_shove->SetDebugDecorator( Dbg() );
329 m_shove->SetDefaultShovePolicy( SHOVE::SHP_SHOVE );
330 }
331
332 startItem->Unmark( MK_LOCKED );
333
334 PNS_DBG( Dbg(), Message, wxString::Format( "StartDragging: item %p [kind %d]",
335 startItem, (int) startItem->Kind() ) );
336
337 switch( startItem->Kind() )
338 {
339 case ITEM::SEGMENT_T:
340 {
341 SEGMENT* seg = static_cast<SEGMENT*>( startItem );
342 VVIA* vvia = checkVirtualVia( aP, seg );
343
344 if( vvia )
345 return startDragVia( vvia );
346 else
347 return startDragSegment( aP, seg );
348 }
349 case ITEM::VIA_T:
350 return startDragVia( static_cast<VIA*>( startItem ) );
351
352 case ITEM::ARC_T:
353 return startDragArc( aP, static_cast<ARC*>( startItem ) );
354
355 default:
356 return false;
357 }
358}
359
360
362{
363 m_mode = static_cast<int>( aMode );
364}
365
366
368{
369 return static_cast<PNS::DRAG_MODE>( m_mode );
370}
371
372
373bool DRAGGER::IsDragOrigin( const ITEM* aItem ) const
374{
375 if( !aItem )
376 return false;
377
378 if( m_mode != DM_VIA )
379 {
380 const LINKED_ITEM* li = dynamic_cast<const LINKED_ITEM*>( aItem );
381 return li && m_draggedLine.ContainsLink( li );
382 }
383
384 if( !m_initialVia.valid || !m_world || !aItem->OfKind( ITEM::VIA_T | ITEM::SEGMENT_T | ITEM::ARC_T ) )
385 return false;
386
387 const JOINT* jt = m_world->FindJoint( m_initialVia.pos, m_initialVia.layers.Start(), m_initialVia.net );
388
389 return jt && alg::contains( jt->LinkList(), aItem );
390}
391
392
393const std::vector<NET_HANDLE> DRAGGER::CurrentNets() const
394{
395 if( m_mode == PNS::DM_VIA )
396 return std::vector<NET_HANDLE>( 1, m_draggedVia.net );
397 else
398 return std::vector<NET_HANDLE>( 1, m_draggedLine.Net() );
399}
400
401
403{
404 // fixme: rewrite using shared_ptr...
405 if( m_lastNode )
406 {
407 delete m_lastNode;
408 m_lastNode = nullptr;
409 }
410
411 m_lastNode = m_preDragNode->Branch();
412
413 switch( m_mode )
414 {
415 case DM_SEGMENT:
416 case DM_CORNER:
417 {
418 //TODO: Make threshold configurable
419 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
420 LINE origLine( m_draggedLine );
421 LINE dragged( m_draggedLine );
422 dragged.SetSnapThreshhold( thresh );
423 dragged.ClearLinks();
424
425 if( m_mode == DM_SEGMENT )
426 dragged.DragSegment( aP, m_draggedSegmentIndex );
427 else
429
430 m_lastNode->Remove( origLine );
431 m_lastNode->Add( dragged );
432
433 m_draggedItems.Clear();
434 m_draggedItems.Add( dragged );
435
436 break;
437 }
438
439 case DM_ARC:
440 {
441 LINE origLine( m_draggedLine );
442 LINE dragged( m_draggedLine );
443 dragged.ClearLinks();
444
445 dragged.DragArc( aP, m_draggedSegmentIndex );
446
447 // A collapsed arc drag leaves an empty chain, so Add() is a no-op and the arc
448 // is simply dropped from the route, which is the intended outcome here.
449 m_lastNode->Remove( origLine );
450 m_lastNode->Add( dragged );
451
452 m_draggedItems.Clear();
453 m_draggedItems.Add( dragged );
454
455 break;
456 }
457
458 case DM_VIA: // fixme...
460
461 break;
462 }
463
464 if( Settings().AllowDRCViolations() )
465 m_dragStatus = true;
466 else
467 m_dragStatus = !m_lastNode->CheckColliding( m_draggedItems );
468
469 return true;
470}
471
472
473bool DRAGGER::dragViaMarkObstacles( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
474{
475 m_draggedItems.Clear();
476
477 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
478
479 if( fanout.Empty() )
480 return true;
481
482 for( ITEM* item : fanout.Items() )
483 {
484 if( const LINE* l = dyn_cast<const LINE*>( item ) )
485 {
486 LINE origLine( *l );
487 LINE draggedLine( *l );
488
489 draggedLine.DragCorner( aP, origLine.CLine().Find( aHandle.pos ), m_freeAngleMode );
490 draggedLine.ClearLinks();
491
492 m_draggedItems.Add( draggedLine );
493
494 m_lastNode->Remove( origLine );
495 m_lastNode->Add( draggedLine );
496 }
497 else if ( VIA *via = dyn_cast<VIA*>( item ) )
498 {
499 auto nvia = Clone( *via );
500
501 nvia->SetPos( aP );
502 m_draggedItems.Add( nvia.get() );
503
504 m_lastNode->Remove( via );
505 m_lastNode->Add( std::move( nvia ) );
506 }
507 }
508
509 return true;
510}
511
512
513bool DRAGGER::dragViaWalkaround( const VIA_HANDLE& aHandle, NODE* aNode, const VECTOR2I& aP )
514{
515 m_draggedItems.Clear();
516
517 ITEM_SET fanout = findViaFanoutByHandle( aNode, aHandle );
518
519 if( fanout.Empty() )
520 return true;
521
522 bool viaPropOk = false;
523 VECTOR2I viaTargetPos;
524
525 for( ITEM* item : fanout.Items() )
526 {
527 if( VIA *via = dyn_cast<VIA*>( item ) )
528 {
529 std::unique_ptr<VIA> draggedVia = Clone( *via );
530
531 draggedVia->SetPos( aP );
532 m_draggedItems.Add( draggedVia.get() );
533
534 std::set<VIA*> vias;
535
536 vias.insert( draggedVia.get() );
537
538 m_lastNode->Remove( via );
539
540 bool ok = propagateViaForces( m_lastNode, vias );
541
542 if( ok )
543 {
544 viaTargetPos = draggedVia->Pos();
545 viaPropOk = true;
546 m_lastNode->Add( std::move(draggedVia) );
547 }
548 }
549 }
550
551 if( !viaPropOk ) // can't force-propagate the via? bummer...
552 return false;
553
554 for( ITEM* item : fanout.Items() )
555 {
556 if( const LINE* l = dyn_cast<const LINE*>( item ) )
557 {
558 LINE origLine( *l );
559 LINE draggedLine( *l );
560 LINE walkLine( *l );
561
562 draggedLine.DragCorner( viaTargetPos, origLine.CLine().Find( aHandle.pos ),
564 draggedLine.ClearLinks();
565
566 if ( m_world->CheckColliding( &draggedLine ) )
567 {
568 bool ok = tryWalkaround( m_lastNode, draggedLine, walkLine );
569
570 if( !ok )
571 return false;
572
573 m_lastNode->Remove( origLine );
574 optimizeAndUpdateDraggedLine( walkLine, origLine, aP );
575 }
576 else
577 {
578 m_draggedItems.Add( draggedLine );
579
580 m_lastNode->Remove( origLine );
581 m_lastNode->Add( draggedLine );
582 }
583 }
584 }
585
586 return true;
587}
588
589
590void DRAGGER::optimizeAndUpdateDraggedLine( LINE& aDragged, const LINE& aOrig, const VECTOR2I& aP )
591{
592 LINE draggedPostOpt, origLine( aOrig );
593
594 aDragged.ClearLinks();
595 aDragged.Unmark();
596
597 OPTIMIZER optimizer( m_lastNode );
598
599 int effort = OPTIMIZER::MERGE_SEGMENTS;
600
601 if( Settings().SmoothDraggedSegments() )
603
604 if( Settings().GetRestrictAngles() )
606
607 optimizer.SetEffortLevel( effort );
608
609 VECTOR2I anchor( aP );
610
611 if( aDragged.CLine().Find( aP ) < 0 )
612 anchor = bestAnchorForPoint( aDragged.CLine(), aP );
613
614 optimizer.SetPreserveVertex( anchor );
615 aDragged.Line().Split( anchor );
616
617 PNS_DBG( Dbg(), AddPoint, anchor, YELLOW, 100000, wxT( "drag-anchor" ) );
618
619 if( !Settings().GetOptimizeEntireDraggedTrack() )
620 {
621 OPT_BOX2I affectedArea = aDragged.ChangedArea( &aOrig );
622
623 if( !affectedArea )
624 affectedArea = BOX2I( aP ); // No valid area yet? set to minimum to disable optimization
625
626 PNS_DBG( Dbg(), AddShape, *affectedArea, RED, 0, wxT( "drag-affected-area" ) );
627
628 optimizer.SetRestrictArea( *affectedArea );
629 }
630
631 PNS_DBG( Dbg(), AddItem, &aDragged, RED, 0, wxT( "drag-preopt" ) );
632
633 optimizer.Optimize( &aDragged, &draggedPostOpt, &origLine );
634 aDragged = draggedPostOpt;
635 PNS_DBG( Dbg(), AddItem, &aDragged, GREEN, 0, wxT( "drag-postopt" ) );
636
637 m_lastNode->Add( draggedPostOpt );
638 m_draggedItems.Clear();
639 m_draggedItems.Add( draggedPostOpt );
640}
641
642
643bool DRAGGER::pointHasBadCorner( const SHAPE_LINE_CHAIN& aLine, int aVertexIndex ) const
644{
645 if( aVertexIndex <= 0 || aVertexIndex >= aLine.PointCount() - 1 )
646 return false;
647
648 SEG segBefore( aLine.CPoint( aVertexIndex - 1 ), aLine.CPoint( aVertexIndex ) );
649 SEG segAfter( aLine.CPoint( aVertexIndex ), aLine.CPoint( aVertexIndex + 1 ) );
650
651 DIRECTION_45 dirBefore( segBefore );
652 DIRECTION_45 dirAfter( segAfter );
653
654 return dirBefore.Angle( dirAfter ) & ( DIRECTION_45::ANG_ACUTE
657}
658
659
661{
662 VECTOR2I nearest = aLine.NearestPoint( aP );
663 int vertIdx = aLine.Find( nearest );
664
665 if( vertIdx < 0 || !pointHasBadCorner( aLine, vertIdx ) )
666 {
667 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt used (vert=%d)", vertIdx ) );
668 return nearest;
669 }
670
671 for( int offset = 1; offset < aLine.PointCount(); offset++ )
672 {
673 int rightIdx = vertIdx + offset;
674 std::optional<VECTOR2I> candidate;
675
676 if( rightIdx < aLine.PointCount() && !pointHasBadCorner( aLine, rightIdx ) )
677 candidate = aLine.CPoint( rightIdx );
678
679 int leftIdx = vertIdx - offset;
680
681 if( leftIdx >= 0 && !pointHasBadCorner( aLine, leftIdx ) )
682 {
683 VECTOR2I leftPt = aLine.CPoint( leftIdx );
684
685 if( !candidate || ( leftPt - aP ).SquaredEuclideanNorm() < ( *candidate - aP ).SquaredEuclideanNorm() )
686 {
687 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", leftIdx ) );
688 PNS_DBG( Dbg(), AddPoint, leftPt, GREEN, 100000, wxT( "drag-anchor-alt" ) );
689 return leftPt;
690 }
691 }
692
693 if( candidate.has_value() )
694 {
695 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: good alt vertex idx %d", rightIdx ) );
696 PNS_DBG( Dbg(), AddPoint, *candidate, GREEN, 100000, wxT( "drag-anchor-alt" ) );
697 return *candidate;
698 }
699 }
700
701 PNS_DBG( Dbg(), Message, wxString::Format( "anchor: nearest pt bad; no alternative (vert=%d)", vertIdx ) );
702 return nearest;
703}
704
705
706bool DRAGGER::tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk )
707{
708 WALKAROUND walkaround( aNode, Router() );
709 walkaround.SetSolidsOnly( false );
710 walkaround.SetDebugDecorator( Dbg() );
711 walkaround.SetLogger( Logger() );
712 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
713 walkaround.SetLengthLimit( true, 30.0 );
715
716 aWalk = aOrig;
717
718 WALKAROUND::RESULT wr = walkaround.Route( aWalk );
719
721 {
722 aWalk = wr.lines[ WALKAROUND::WP_SHORTEST ];
723 return true;
724 }
725
726 return false;
727}
728
729
731{
732 bool ok = false;
733
734 // fixme: rewrite using shared_ptr...
735 if( m_lastNode )
736 {
737 delete m_lastNode;
738 m_lastNode = nullptr;
739 }
740
741 m_lastNode = m_preDragNode->Branch();
742
743 switch( m_mode )
744 {
745 case DM_SEGMENT:
746 case DM_CORNER:
747 {
748 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 4 : 0;
749 LINE dragged( m_draggedLine );
750 LINE draggedWalk( m_draggedLine );
751 LINE origLine( m_draggedLine );
752
753 dragged.SetSnapThreshhold( thresh );
754
755 if( m_mode == DM_SEGMENT )
756 dragged.DragSegment( aP, m_draggedSegmentIndex );
757 else
758 dragged.DragCorner( aP, m_draggedSegmentIndex );
759
760 if ( m_world->CheckColliding( &dragged ) )
761 {
762 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
763 }
764 else
765 {
766 draggedWalk = dragged;
767 ok = true;
768 }
769
770 if( draggedWalk.CLine().PointCount() < 2 )
771 ok = false;
772
773 if( ok )
774 {
775 PNS_DBG( Dbg(), AddShape, &origLine.CLine(), BLUE, 50000, wxT( "drag-orig-line" ) );
776 PNS_DBG( Dbg(), AddShape, &draggedWalk.CLine(), CYAN, 75000, wxT( "drag-walk" ) );
777 m_lastNode->Remove( origLine );
778 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
779 }
780
781 break;
782 }
783 case DM_ARC:
784 {
785 LINE dragged( m_draggedLine );
786 LINE draggedWalk( m_draggedLine );
787 LINE origLine( m_draggedLine );
788
789 dragged.DragArc( aP, m_draggedSegmentIndex );
790
791 if( m_world->CheckColliding( &dragged ) )
792 {
793 ok = tryWalkaround( m_lastNode, dragged, draggedWalk );
794 }
795 else
796 {
797 draggedWalk = dragged;
798 ok = true;
799 }
800
801 if( draggedWalk.CLine().PointCount() < 2 )
802 ok = false;
803
804 if( ok )
805 {
806 m_lastNode->Remove( origLine );
807 optimizeAndUpdateDraggedLine( draggedWalk, origLine, aP );
808 }
809
810 break;
811 }
812 case DM_VIA: // fixme...
814 break;
815 }
816
817 m_dragStatus = ok;
818
819 return ok;
820}
821
822
824{
825
826 if( m_lastNode )
827 {
828 delete m_lastNode;
829 m_lastNode = nullptr;
830 }
831
832 switch( m_mode )
833 {
834 case DM_SEGMENT:
835 case DM_CORNER:
836 {
837 bool ok = false;
838 //TODO: Make threshold configurable
839 int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine.Width() / 2 : 0;
840 LINE draggedPreShove( m_draggedLine );
841 draggedPreShove.SetSnapThreshhold( thresh );
842
843 if( m_mode == DM_SEGMENT )
844 draggedPreShove.DragSegment( aP, m_draggedSegmentIndex );
845 else
846 draggedPreShove.DragCorner( aP, m_draggedSegmentIndex );
847
848 auto preShoveNode = m_shove->CurrentNode();
849
850 if( preShoveNode )
851 preShoveNode->Remove( draggedPreShove );
852
854
855 PNS_DBG( Dbg(), Message, wxString::Format( "drag seg index %d", m_draggedSegmentIndex ) );
856
857 if( m_mode == DM_CORNER && m_draggedSegmentIndex == 0 )
858 policy |= SHOVE::SHP_REVERSED;
859
860 m_shove->ClearHeads();
861 m_shove->AddHeads( draggedPreShove, policy );
862 ok = m_shove->Run() == SHOVE::SH_OK;
863
864 LINE draggedPostShove( draggedPreShove );
865
866 if( ok )
867 {
868 if( m_shove->HeadsModified() )
869 draggedPostShove = m_shove->GetModifiedHead( 0 );
870 }
871
872 m_lastNode = m_shove->CurrentNode()->Branch();
873
874 if( ok )
875 {
876 draggedPostShove.ClearLinks();
877 draggedPostShove.Unmark();
878 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
879 m_lastDragSolution = std::move( draggedPostShove );
880 }
881
882 m_dragStatus = ok;
883 break;
884 }
885
886 case DM_ARC:
887 {
888 bool ok = false;
889
890 LINE draggedPreShove( m_draggedLine );
891 draggedPreShove.DragArc( aP, m_draggedSegmentIndex );
892
893 // A collapsed arc drag can leave fewer than two points, which is not a valid
894 // shove head. Treat that as an unsuccessful shove (as dragWalkaround does) rather
895 // than feeding a degenerate line to AddHeads.
896 if( draggedPreShove.CLine().PointCount() >= 2 )
897 {
898 auto preShoveNode = m_shove->CurrentNode();
899
900 if( preShoveNode )
901 preShoveNode->Remove( draggedPreShove );
902
904
905 m_shove->ClearHeads();
906 m_shove->AddHeads( draggedPreShove, policy );
907 ok = m_shove->Run() == SHOVE::SH_OK;
908 }
909
910 LINE draggedPostShove( draggedPreShove );
911
912 if( ok && m_shove->HeadsModified() )
913 draggedPostShove = m_shove->GetModifiedHead( 0 );
914
915 m_lastNode = m_shove->CurrentNode()->Branch();
916
917 if( ok )
918 {
919 draggedPostShove.ClearLinks();
920 draggedPostShove.Unmark();
921 optimizeAndUpdateDraggedLine( draggedPostShove, m_draggedLine, aP );
922 m_lastDragSolution = std::move( draggedPostShove );
923 }
924
925 m_dragStatus = ok;
926 break;
927 }
928
929 case DM_VIA:
930 {
931 VIA_HANDLE newVia;
932
933 // corner count limiter intended to avoid excessive optimization produces mediocre results for via shoving.
934 // this is a hack that disables it, before I figure out a more reliable solution
935 m_shove->DisablePostShoveOptimizations( OPTIMIZER::LIMIT_CORNER_COUNT );
936
937 m_shove->ClearHeads();
938 m_shove->AddHeads( m_draggedVia, aP, SHOVE::SHP_SHOVE );
939
940 SHOVE::SHOVE_STATUS st = m_shove->Run(); //ShoveDraggingVia( m_draggedVia, aP, newVia );
941
942 PNS_DBG( Dbg(), Message, wxString::Format("head-mod %d",
943 m_shove->HeadsModified() ? 1: 0 ) );
944
945 if( m_shove->HeadsModified() )
946 {
947 newVia = m_shove->GetModifiedHeadVia( 0 );
948
949 PNS_DBG( Dbg(), Message, wxString::Format("newvia %d %d %d %d",
950 newVia.pos.x,
951 newVia.pos.y,
952 newVia.layers.Start(),
953 newVia.layers.End()
954 ) );
955
956 m_draggedVia = newVia;
957 }
958
959
960 m_lastNode = m_shove->CurrentNode()->Branch();
961
962 m_draggedItems.Clear();
963
964 // If drag didn't work (i.e. dragged onto a collision) try walkaround instead
965 if( st != SHOVE::SH_OK )
967 else
968 m_dragStatus = true;
969
970 break;
971 }
972 }
973
974 return m_dragStatus;
975}
976
977
978bool DRAGGER::FixRoute( bool aForceCommit )
979{
980 NODE* node = CurrentNode();
981
982 if( node )
983 {
984 if( m_dragStatus )
985 {
986 Router()->CommitRouting( node );
987 return true;
988 }
989 else if( m_forceMarkObstaclesMode )
990 {
991 if( aForceCommit )
992 {
993 Router()->CommitRouting( node );
994 return true;
995 }
996
997 return false;
998 }
999 else
1000 {
1001 // If collisions exist, we can fix in shove/smart mode because all tracks to be
1002 // committed will be in valid positions (even if the current routing solution to
1003 // the mouse cursor is invalid).
1005 node = CurrentNode();
1006
1007 if( node && m_dragStatus )
1008 {
1009 Router()->CommitRouting( node );
1010 return true;
1011 }
1012 }
1013 }
1014
1015 return false;
1016}
1017
1018
1019bool DRAGGER::Drag( const VECTOR2I& aP )
1020{
1021 m_mouseTrailTracer.AddTrailPoint( aP );
1022
1023 bool firstDrag = m_lastNode == nullptr;
1024 bool ret = false;
1025
1027 {
1028 ret = dragMarkObstacles( aP );
1029 }
1030 else
1031 {
1032 switch( m_currentMode )
1033 {
1034 case RM_MarkObstacles: ret = dragMarkObstacles( aP ); break;
1035 case RM_Shove: ret = dragShove( aP ); break;
1036 case RM_Walkaround: ret = dragWalkaround( aP ); break;
1037 default: break;
1038 }
1039 }
1040
1041 if( ret )
1042 {
1043 m_lastValidPoint = aP;
1044 }
1045 else
1046 {
1047 if( firstDrag )
1048 {
1049 // First collision resolution failed, switch to highlight mode
1051
1052 ret = dragMarkObstacles( aP );
1053
1054 if( ret )
1055 m_lastValidPoint = aP;
1056 }
1057 else if( m_lastNode )
1058 {
1059 // Restore last solution
1060 NODE* parent = m_lastNode->GetParent()->Branch();
1061 delete m_lastNode;
1062 m_lastNode = parent;
1063 m_draggedItems.Clear();
1064 m_lastDragSolution.ClearLinks();
1066 }
1067 }
1068
1069 return ret;
1070}
1071
1072
1074{
1075 return m_lastNode ? m_lastNode : m_world;
1076}
1077
1078
1080{
1081 return m_draggedItems;
1082}
1083
1084}
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:911
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
SHAPE_LINE_CHAIN & Line()
Modifiable accessor to the underlying shape.
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:259
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()
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:256
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.
@ 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