KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_diff_pair_placer.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2015 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 <core/typeinfo.h>
23
24#include "pns_walkaround.h"
25#include "pns_shove.h"
26#include "pns_router.h"
28#include "pns_solid.h"
29#include "pns_topology.h"
30#include "pns_debug_decorator.h"
31#include "pns_arc.h"
32#include "pns_utils.h"
33
34namespace PNS {
35
36
37static void drawGateways( PNS::DEBUG_DECORATOR *dbg, const wxString& groupName, PNS::DP_PRIMITIVE_PAIR& prims, PNS::DP_GATEWAYS& gws, VECTOR2D offset, VECTOR2D step );
38
39
41 PLACEMENT_ALGO( aRouter )
42{
44 m_chainedPlacement = false;
45 m_initialDiagonal = false;
46 m_startDiagonal = false;
47 m_fitOk = false;
48 m_netP = nullptr;
49 m_netN = nullptr;
50 m_iteration = 0;
51 m_world = nullptr;
52 m_shove = nullptr;
53 m_currentNode = nullptr;
54 m_lastNode = nullptr;
55 m_lastFixNode = nullptr;
56 m_placingVia = false;
57 m_viaDiameter = 0;
58 m_viaDrill = 0;
61 m_startsOnVia = false;
62 m_orthoMode = false;
63 m_snapOnTarget = false;
64 m_currentEndItem = nullptr;
65 m_currentTraceOk = false;
66 m_idle = true;
67 m_hasFixedAnything = false;
68}
69
71{
72 if( m_target )
73 m_target->Unlink();
74 if( m_prevPair )
75 m_prevPair->Unlink();
76}
77
78
80{
81 m_world = aWorld;
82}
83
84
86{
88
89 VIA v( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), aNet, m_sizes.ViaType() );
90
91 return v;
92}
93
94
95void DIFF_PAIR_PLACER::SetOrthoMode ( bool aOrthoMode )
96{
97 m_orthoMode = aOrthoMode;
98
99 if( !m_idle )
100 Move( m_currentEnd, nullptr );
101}
102
103
104bool DIFF_PAIR_PLACER::ToggleVia( bool aEnabled )
105{
106 m_placingVia = aEnabled;
107
108 if( !m_idle )
109 Move( m_currentEnd, nullptr );
110
111 return true;
112}
113
114
116{
117 if( !routeHead( aP ) )
118 return false;
119
120
122 ctxOpts.m_filter = [&]( const PNS::ITEM* aTestItem, const PNS::ITEM* aRefItem ) -> bool
123 {
124 if( aTestItem->Net() == m_currentTrace.NetP() || aTestItem->Net() == m_currentTrace.NetN() )
125 {
126 return false;
127 }
128 return true;
129 };
130
131 bool collP = ( m_currentNode->CheckColliding( &m_currentTrace.PLine(), ctxOpts ) ).has_value();
132 bool collN = ( m_currentNode->CheckColliding( &m_currentTrace.NLine(), ctxOpts ) ).has_value();
133
134 m_fitOk = !( collP || collN );
135
136 return m_fitOk;
137}
138
139
141{
142 VIA virtHead = makeVia( aP, nullptr );
143
144 if( m_placingVia )
145 {
146 virtHead.SetDiameter( 0, viaGap() + 2 * virtHead.Diameter( 0 ) );
147 }
148 else
149 {
150 virtHead.SetLayer( m_currentLayer );
151 virtHead.SetDiameter( 0, m_sizes.DiffPairGap() + 2 * m_sizes.DiffPairWidth() );
152 }
153
154 bool solidsOnly = true;
155
156 if( Settings().Mode() == RM_MarkObstacles )
157 {
158 aNewP = aP;
159 return true;
160 }
161 else if( Settings().Mode() == RM_Walkaround )
162 {
163 solidsOnly = false;
164 }
165
166 // fixme: I'm too lazy to do it well. Circular approximaton will do for the moment.
167
168 // Note: this code is lifted from VIA::PushoutForce and then optimized for this use case and to
169 // check proper clearances to the diff pair line. It can be removed if some specialized
170 // pushout for traces / diff pairs is implemented. Just calling VIA::PushoutForce does not work
171 // as the via may have different resolved clearance to items than the diff pair should.
172 int maxIter = 40;
173 int iter = 0;
174 bool collided = false;
175 VECTOR2I force, totalForce;
176 std::set<const ITEM*> handled;
177
179 ctxOpts.m_filter = [&]( const PNS::ITEM* aTestItem, const PNS::ITEM* aRefItem ) -> bool
180 {
181 if( aTestItem->Net() == m_currentTrace.NetP() || aTestItem->Net() == m_currentTrace.NetN() )
182 {
183 return false;
184 }
185 return true;
186 };
187
188 ctxOpts.m_kindMask = solidsOnly ? ITEM::SOLID_T : ITEM::ANY_T;
189 while( iter < maxIter )
190 {
191 NODE::OPT_OBSTACLE obs = m_currentNode->CheckColliding( &virtHead, ctxOpts );
192 if( !obs || handled.count( obs->m_item ) )
193 break;
194
195 int clearance = m_currentNode->GetClearance( obs->m_item, &m_currentTrace.PLine(), false );
196 VECTOR2I layerForce;
197 collided = false;
198
199 for( int viaLayer : virtHead.RelevantShapeLayers( obs->m_item ) )
200 {
201 collided |= obs->m_item->Shape( viaLayer )->Collide( virtHead.Shape( viaLayer ), clearance, &layerForce );
202
203 if( layerForce.SquaredEuclideanNorm() > force.SquaredEuclideanNorm() )
204 force = layerForce;
205 }
206
207 if( collided )
208 {
209 totalForce += force;
210 virtHead.SetPos( virtHead.Pos() + force );
211 }
212
213 handled.insert( obs->m_item );
214
215 iter++;
216 }
217
218 bool succeeded = ( !collided || iter != maxIter );
219
220 if( succeeded )
221 {
222 aNewP = aP + force;
223 return true;
224 }
225
226 return false;
227}
228
229
231 bool aPFirst, bool aWindCw, bool aSolidsOnly )
232{
233 WALKAROUND walkaround( aNode, Router() );
235
236 auto excludeHeadDp = [aCurrent]( const ITEM* aTestItem, const ITEM* aRefItem ) -> bool
237 {
238 if( aTestItem->Net() == aCurrent->NetN() || aTestItem->Net() == aCurrent->NetP() )
239 {
240 return false;
241 }
242 return true;
243 };
244
245 walkaround.SetSolidsOnly( aSolidsOnly );
246 walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
248 walkaround.SetCollisionFilter( excludeHeadDp );
249 walkaround.SetDebugDecorator( Dbg() );
250
251
252 SHOVE shove( aNode, Router() );
253 LINE walkP, walkN;
254
255 aWalk = *aCurrent;
256
257 int iter = 0;
258
259 DIFF_PAIR cur( *aCurrent );
260
261 bool currentIsP = aPFirst;
262
263 int mask = aSolidsOnly ? ITEM::SOLID_T : ITEM::ANY_T;
264
265 opts.m_kindMask = mask;
266 opts.m_filter = excludeHeadDp;
267
268 do
269 {
270 LINE preWalk = ( currentIsP ? cur.PLine() : cur.NLine() );
271 LINE preShove = ( currentIsP ? cur.NLine() : cur.PLine() );
272 LINE postWalk;
273
274 if( !aNode->CheckColliding ( &preWalk, opts ) )
275 {
276 currentIsP = !currentIsP;
277
278 if( !aNode->CheckColliding( &preShove, opts ) )
279 break;
280 else
281 continue;
282 }
283
284 PNS_DBG( Dbg(), AddItem, &preWalk, GREEN, 100000, wxString::Format("preWalk") );
285
286 WALKAROUND::RESULT wf1 = walkaround.Route( preWalk );
287
289 return false;
290
291 postWalk = wf1.lines[ WALKAROUND::WP_SHORTEST ];
292
293 PNS_DBG( Dbg(), AddItem, &postWalk, BLUE, 100000, wxString::Format("postWalk") );
294
295 LINE postShove( preShove );
296
297 shove.ForceClearance( true, cur.Dimensions().Gap() - 2 * PNS_HULL_MARGIN );
298
299 bool sh1;
300
301 sh1 = shove.ShoveObstacleLine( postWalk, preShove, postShove );
302
303 if( !sh1 )
304 return false;
305
306 postWalk.Line().Simplify();
307 postShove.Line().Simplify();
308
309 cur.SetShape( postWalk.CLine(), postShove.CLine(), !currentIsP );
310
311 currentIsP = !currentIsP;
312
313 if( !aNode->CheckColliding( &postShove, opts ) )
314 break;
315
316 iter++;
317 }
318 while( iter < 2 );
319
320 if( iter == 2 )
321 return false;
322
323 aWalk.SetShape( cur.CP(), cur.CN() );
324
325 return true;
326}
327
328
329bool DIFF_PAIR_PLACER::tryWalkDp( NODE* aNode, DIFF_PAIR &aPair, bool aSolidsOnly )
330{
331 DIFF_PAIR best;
332 double bestScore = 100000000000000.0;
333
334 for( int attempt = 0; attempt <= 3; attempt++ )
335 {
336 DIFF_PAIR p;
337 NODE *tmp = m_currentNode->Branch();
338
339 bool pfirst = ( attempt & 1 ) ? true : false;
340 bool wind_cw = ( attempt & 2 ) ? true : false;
341
342 if( attemptWalk( tmp, &aPair, p, pfirst, wind_cw, aSolidsOnly ) )
343 {
344 double cl = 1 + p.CoupledLength();
345 double skew = p.Skew();
346
347 double score = cl + fabs( skew ) * 3.0;
348
349 if( score < bestScore )
350 {
351 bestScore = score;
352 best = std::move( p );
353 }
354 }
355
356 delete tmp;
357 }
358
359 if( bestScore > 0.0 )
360 {
361 OPTIMIZER optimizer( m_currentNode );
362
363 aPair.SetShape( best );
364 optimizer.Optimize( &aPair );
365
366 return true;
367 }
368
369 return false;
370}
371
372
374{
375 if( !routeHead ( aP ) )
376 return false;
377
379
380 return m_fitOk;
381}
382
383
385{
386 switch( Settings().Mode() )
387 {
388 case RM_MarkObstacles:
389 return rhMarkObstacles( aP );
390 case RM_Walkaround:
391 return rhWalkOnly( aP );
392 case RM_Shove:
393 return rhShoveOnly( aP );
394 default:
395 break;
396 }
397
398 return false;
399}
400
401
403{
404 m_currentNode = m_shove->CurrentNode();
405
406 bool ok = routeHead( aP );
407
408 m_fitOk = false;
409
410 if( !ok )
411 return false;
412
413 if( !tryWalkDp( m_currentNode, m_currentTrace, true ) )
414 return false;
415
416 LINE pLine( m_currentTrace.PLine() );
417 LINE nLine( m_currentTrace.NLine() );
418 ITEM_SET head;
419
420 m_shove->ClearHeads();
421 m_shove->AddHeads( pLine );
422 m_shove->AddHeads( nLine );
423
424 auto collisionFilter = [&]( const PNS::ITEM* aTestItem, const PNS::ITEM* aRefItem ) -> bool
425 {
426 if( aTestItem->Net() == m_currentTrace.NetP() || aTestItem->Net() == m_currentTrace.NetN() )
427 {
428 return false;
429 }
430 return true;
431 };
432
433 m_shove->SetCollisionFilter( collisionFilter );
434
435 SHOVE::SHOVE_STATUS status = m_shove->Run();
436
437 m_currentNode = m_shove->CurrentNode();
438
439 if( status == SHOVE::SH_OK )
440 {
441 m_currentNode = m_shove->CurrentNode();
442
443 if( m_shove->HeadsModified( 0 ))
444 pLine = m_shove->GetModifiedHead(0);
445
446 if( m_shove->HeadsModified( 1 ))
447 nLine = m_shove->GetModifiedHead(1);
448
449 // Update m_currentTrace with the shoved shapes so FixRoute() commits correct geometry
450 m_currentTrace.SetShape( pLine.CLine(), nLine.CLine() );
451
452 if( !m_currentNode->CheckColliding( &pLine ) &&
453 !m_currentNode->CheckColliding( &nLine ) )
454 {
455 m_fitOk = true;
456 }
457 }
458 else
459 {
460 // bring back previous state
461 m_currentTrace.SetShape( pLine.CLine(), nLine.CLine() );
462 }
463
464
465 return m_fitOk;
466}
467
468
470{
471 ITEM_SET t;
472
473 t.Add( &m_currentTrace.PLine() );
474 t.Add( &m_currentTrace.NLine() );
475
476 return t;
477}
478
479
481{
483
484 if( !m_idle )
485 Move( m_currentEnd, nullptr );
486}
487
488
489NODE* DIFF_PAIR_PLACER::CurrentNode( bool aLoopsRemoved ) const
490{
491 if( aLoopsRemoved && m_lastNode )
492 return m_lastNode;
493
494 return m_currentNode;
495}
496
497
499{
500 if( m_idle )
501 {
502 m_currentLayer = aLayer;
503 return true;
504 }
505 else if( m_chainedPlacement || !m_prevPair )
506 {
507 return false;
508 }
509 else if( !m_prevPair->PrimP() || ( m_prevPair->PrimP()->OfKind( ITEM::VIA_T ) &&
510 m_prevPair->PrimP()->Layers().Overlaps( aLayer ) ) )
511 {
512 m_currentLayer = aLayer;
515 Move( m_currentEnd, nullptr );
516 return true;
517 }
518
519 return false;
520}
521
522
524{
525 switch( aItem->Kind() )
526 {
527 case ITEM::LINE_T:
528 {
529 LINE* l = static_cast<LINE*>( aItem );
530
531 if( !l->PointCount() )
532 return OPT_VECTOR2I();
533 else
534 return l->CPoint( 0 );
535 }
536 case ITEM::VIA_T:
537 case ITEM::SOLID_T: return aItem->Anchor( 0 );
538
539 case ITEM::ARC_T:
540 {
541 ARC* a = static_cast<ARC*>( aItem );
542
543 const JOINT* jA = aNode->FindJoint( aItem->Anchor( 0 ), aItem );
544 const JOINT* jB = aNode->FindJoint( aItem->Anchor( 1 ), aItem );
545
546 if( jA && jA->LinkCount() == 1 )
547 return a->Arc().GetP0();
548 else if( jB && jB->LinkCount() == 1 )
549 return a->Arc().GetP1();
550 else
551 return OPT_VECTOR2I();
552 }
553 case ITEM::SEGMENT_T:
554 {
555 SEGMENT* s = static_cast<SEGMENT*>( aItem );
556
557 const JOINT* jA = aNode->FindJoint( aItem->Anchor( 0 ), aItem );
558 const JOINT* jB = aNode->FindJoint( aItem->Anchor( 1 ), aItem );
559
560 if( jA && jA->LinkCount() == 1 )
561 return s->Seg().A;
562 else if( jB && jB->LinkCount() == 1 )
563 return s->Seg().B;
564 else
565 return OPT_VECTOR2I();
566 }
567
568 default: return OPT_VECTOR2I();
569 }
570}
571
572
573bool DIFF_PAIR_PLACER::findDpEndingPrimitives( NODE* aWorld, const VECTOR2I& aP, ITEM* aStartItem,
574 DP_PRIMITIVE_PAIR& aPair, wxString* aErrorMsg )
575{
576 NET_HANDLE netP, netN;
577
578 (void) aWorld->GetRuleResolver()->DpNetPair( aStartItem, netP, netN );
579
580 NET_HANDLE refNet = aStartItem->Net();
581 NET_HANDLE coupledNet = ( refNet == netP ) ? netN : netP;
582
583 OPT_VECTOR2I refAnchor = getDanglingAnchor( aWorld, aStartItem );
584 ITEM* primRef = aStartItem;
585
586 double distThreshold = 10000000;
587
588 if( auto seg = dyn_cast<SEGMENT*>( aStartItem ) )
589 {
590 distThreshold = seg->Width() / 2;
591 }
592 else if( auto arc = dyn_cast<ARC*>( aStartItem ) )
593 {
594 distThreshold = arc->Width() / 2;
595 }
596
597 if( !refAnchor || ( refAnchor->Distance( aP ) > distThreshold ) )
598 {
599 if( aErrorMsg )
600 {
601 *aErrorMsg = _( "Can't find a suitable starting point. If starting "
602 "from an existing differential pair make sure you are "
603 "at the end." );
604 }
605
606 return false;
607 }
608
609 std::set<ITEM*> coupledItems;
610
611 aWorld->AllItemsInNet( coupledNet, coupledItems );
612 double bestDist = std::numeric_limits<double>::max();
613 bool found = false;
614
615 for( ITEM* item : coupledItems )
616 {
617 if( item->Kind() == aStartItem->Kind() )
618 {
619 OPT_VECTOR2I anchor = getDanglingAnchor( aWorld, item );
620
621 if( !anchor )
622 continue;
623
624 double dist = ( *anchor - *refAnchor ).EuclideanNorm();
625
626 bool shapeMatches = true;
627
628 if( item->OfKind( ITEM::SOLID_T | ITEM::VIA_T ) && item->Layers() != aStartItem->Layers() )
629 {
630 shapeMatches = false;
631 }
632
633 if( dist < bestDist && shapeMatches )
634 {
635 found = true;
636 bestDist = dist;
637
638 if( refNet != netP )
639 {
640 aPair = DP_PRIMITIVE_PAIR( item, primRef );
641 aPair.SetIsMidtrace( false );
642 aPair.SetAnchors( *anchor, *refAnchor );
643 }
644 else
645 {
646 aPair = DP_PRIMITIVE_PAIR( primRef, item );
647 aPair.SetIsMidtrace( false );
648 aPair.SetAnchors( *refAnchor, *anchor );
649 }
650 }
651 }
652 }
653
654 return found;
655}
656
657
659 DP_PRIMITIVE_PAIR& aPair, wxString* aErrorMsg )
660{
661 PNS::TOPOLOGY topo( aWorld );
662 PNS::DIFF_PAIR originPair;
663 auto startSeg = dyn_cast<PNS::SEGMENT*>( aStartItem );
664
665 if( !startSeg )
666 {
667 return false;
668 }
669
670 if( aStartItem && aStartItem->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
671 {
672 if( !topo.AssembleDiffPair( aStartItem, originPair ) )
673 return false;
674
675 auto ppair = originPair.BuildMidpairIntersection( startSeg, aP );
676
677
678 if( ppair )
679 {
680 ppair->SetIsMidtrace( true );
681
682 aPair = *ppair;
683 return true;
684 }
685 }
686
687 return false;
688}
689
690
692 wxString* aErrorMsg )
693{
694 NET_HANDLE netP, netN;
695
696 bool result = aWorld->GetRuleResolver()->DpNetPair( aItem, netP, netN );
697
698 if( !result )
699 {
700 if( aErrorMsg )
701 {
702 *aErrorMsg = _( "Unable to find complementary differential pair "
703 "nets. Make sure the names of the nets belonging "
704 "to a differential pair end with either N/P or +/-." );
705 }
706 return false;
707 }
708
709 bool found = findDpEndingPrimitives( aWorld, aP, aItem, aPair, aErrorMsg );
710
711 PNS_DBG( Dbg(), Message,
712 wxString::Format( "EP=%d target-p [%d,%d] target-n [%d,%d]", found ? 1 : 0, aPair.AnchorP().x,
713 aPair.AnchorP().y, aPair.AnchorN().x, aPair.AnchorN().y ) );
714
715 if( !found )
716 {
717 found = findDpMidtraceIntersection( aWorld, aP, aItem, aPair, aErrorMsg );
718
719 PNS_DBG( Dbg(), Message,
720 wxString::Format( "MT=%d target-p [%d,%d] target-n [%d,%d]", found ? 1 : 0, aPair.AnchorP().x,
721 aPair.AnchorP().y, aPair.AnchorN().x, aPair.AnchorN().y ) );
722 }
723
724
725 if( !found )
726 {
727 if( aErrorMsg )
728 {
729 *aErrorMsg = wxString::Format( _( "Can't find a suitable starting point for the diff pair" ) );
730 }
731
732 return false;
733 }
734
735 return true;
736}
737
738
740{
741 return m_sizes.EffectiveDiffPairViaGap();
742}
743
744
746{
747 return m_sizes.DiffPairGap() + m_sizes.DiffPairWidth();
748}
749
750
751bool DIFF_PAIR_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
752{
753 VECTOR2I p( aP );
754
755 setWorld( Router()->GetWorld() );
757
758 wxString err_msg;
759
760 if( !FindDpPrimitivePair( m_currentNode, aP, aStartItem, m_start, &err_msg ) )
761 {
762 Router()->SetFailureReason( err_msg );
763 return false;
764 }
765
766 m_netP = m_start.PrimP()->Net();
767 m_netN = m_start.PrimN()->Net();
768
769 m_currentStart = p;
770 m_currentEnd = p;
771 m_placingVia = false;
772 m_chainedPlacement = false;
773 m_hasFixedAnything = false;
774 m_currentTraceOk = false;
776 m_currentTrace.SetNets( m_netP, m_netN );
777 m_lastFixNode = nullptr;
778
780
781 return true;
782}
783
784
786{
787 m_idle = false;
788 m_orthoMode = false;
789 m_currentEndItem = nullptr;
791
792 NODE* world = Router()->GetWorld();
793
794 world->KillChildren();
795 NODE* rootNode = world->Branch();
796
797 PNS_DBG( Dbg(), Message, wxString::Format("Start-is-mid %d wd %d rd %d", m_start.IsMidtrace()?1:0, (int) world->Depth(), (int) rootNode->Depth() ) );
798
799 if( m_start.IsMidtrace() )
800 {
801 SplitAdjacentSegments( rootNode, m_start.PrimP(), m_start.AnchorP() );
802 SplitAdjacentSegments( rootNode, m_start.PrimN(), m_start.AnchorN() );
803 }
804
805 setWorld( rootNode );
806
807 m_lastNode = nullptr;
808 m_currentNode = rootNode;
809
810 m_shove = std::make_unique<SHOVE>( m_currentNode, Router() );
811}
812
813
814static void drawSingleGateway( DEBUG_DECORATOR* dbg, DP_GATEWAY gw, wxString grpName )
815{
816 PNS_DBG( dbg, BeginGroup, grpName, 0 );
817
818 SHAPE_CIRCLE gwp( gw.AnchorP(), 30000 );
819 PNS_DBG( dbg, AddShape, &gwp, LIGHTRED, 20000, "gw-entry-p" );
820
821 if( gw.EntryP().SegmentCount() )
822 {
823 PNS_DBG( dbg, AddShape, &gw.EntryP(), LIGHTRED, 20000, "gwp" );
824 }
825
826 SHAPE_CIRCLE gwn( gw.AnchorN(), 30000 );
827 PNS_DBG( dbg, AddShape, &gwn, LIGHTBLUE, 30000, "gw-entry-n" );
828
829 if( gw.EntryN().SegmentCount() )
830 {
831 PNS_DBG( dbg, AddShape, &gw.EntryN(), LIGHTBLUE, 10000, "gwn" );
832 }
833
834 SHAPE_LINE_CHAIN link( { gw.AnchorN(), gw.AnchorP() } );
835 PNS_DBG( dbg, AddShape, &link, LIGHTGRAY, 10000, "link" );
836
837 PNS_DBGN( dbg, EndGroup );
838}
839
840
842{
843 std::optional<int> minClearance = 0;
844
845 RULE_RESOLVER* ruleResolver = Router()->GetInterface()->GetRuleResolver();
846 DP_DIMENSIONS dims( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(), 0 );
847 DP_GATEWAYS gwsEntry;
848 DP_GATEWAYS gwsTarget;
849
850 m_fitOk = false;
851
852 auto updateMinClearance = [&minClearance, ruleResolver]( const DP_PRIMITIVE_PAIR& aTarget )
853 {
854 if( aTarget.PrimN() && aTarget.PrimP() )
855 {
856 int clearance = ruleResolver->Clearance( aTarget.PrimP(), aTarget.PrimN() );
857 if( minClearance )
858 minClearance = std::max( clearance, minClearance.value() );
859 else
860 minClearance = clearance;
861 }
862 };
863
864 m_target.reset();
865
866 if( !m_prevPair )
868
869
870 if( m_prevPair )
871 updateMinClearance( *m_prevPair );
872
873 DP_DIMENSIONS dims2( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(),
874 minClearance.value() );
875 gwsEntry.SetDimensions( dims2 );
877
878 if( m_prevPair )
879 drawGateways( Dbg(), wxT( "entry-gateways" ), *m_prevPair, gwsEntry, VECTOR2D( 0, 0 ), VECTOR2D( 0, 2000000 ) );
880
881 DP_PRIMITIVE_PAIR target;
882 VECTOR2I midpoint;
883 bool snapVias = false;
884 bool foundTarget = false;
885
887 {
888 if( m_placingVia && ( target.DirP() == target.DirN() ) )
889 {
890 midpoint = ( target.AnchorN() + target.AnchorP() ) / 2;
891 PNS_DBG( Dbg(), AddPoint, midpoint, YELLOW, 100000, wxT( "midpoint" ) );
892 snapVias = true;
893 }
894 else
895 {
896 updateMinClearance( target );
897 DP_DIMENSIONS dimsTarget( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(),
898 minClearance.value() );
899
900 gwsTarget.SetDimensions( dimsTarget );
901 gwsTarget.BuildFromPrimitivePair( target, m_startDiagonal );
902 m_snapOnTarget = true;
903 m_target = target;
904
905 PNS_DBG( Dbg(), Message,
906 wxString::Format( "target-p [%d,%d] target-n [%d,%d], cursor [%d,%d]", m_target->AnchorP().x,
907 m_target->AnchorP().y, m_target->AnchorN().x, m_target->AnchorN().y, aP.x,
908 aP.y ) );
909
910 PNS_DBG( Dbg(), AddPoint, aP, YELLOW, 100000, wxT( "targer-cursor" ) );
911 PNS_DBG( Dbg(), AddPoint, m_target->AnchorP(), RED, 100000, wxT( "anchor+" ) );
912 PNS_DBG( Dbg(), AddPoint, m_target->AnchorN(), BLUE, 100000, wxT( "anchor-" ) );
913 foundTarget = true;
914
915 drawGateways( Dbg(), wxT( "target-gateways" ), target, gwsTarget, VECTOR2D( 0, 0 ),
916 VECTOR2D( 0, 2000000 ) );
917 }
918 }
919
920
921 if( !foundTarget )
922 {
923 VECTOR2I fp;
924
925 if( !propagateDpHeadForces( snapVias ? midpoint : aP, fp ) )
926 return false;
927
928 VECTOR2I midp, dirV;
929 m_prevPair->CursorOrientation( fp, midp, dirV );
930
931 VECTOR2I fpProj = SEG( midp, midp + dirV ).LineProject( fp );
932
933 // compute 'leader point' distance from the cursor (project cursor position
934 // on the extension of the starting segment pair of the DP)
935 int lead_dist = ( fpProj - fp ).EuclideanNorm();
936
937 gwsTarget.SetFitVias( m_placingVia );
938 DP_DIMENSIONS dimsTarget( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(),
939 minClearance.value() );
940 gwsTarget.SetDimensions( dimsTarget );
941
942 int snapThreshold = ( m_sizes.DiffPairGap() + m_sizes.DiffPairWidth() ) / 2;
943
944 PNS_DBG( Dbg(), Message,
945 wxString::Format( "leadDist %d snapVias %d thr %d dirv %s", lead_dist, snapVias ? 1 : 0, snapThreshold,
946 DIRECTION_45( dirV ).Format() ) );
947
948 // far from the initial segment extension line -> allow a 45-degree obtuse turn
949 if( !snapVias && lead_dist > snapThreshold )
950 {
951 gwsTarget.BuildForCursor( fp );
952 }
953 else
954 {
955 // close to the initial segment extension line -> keep straight part only, project
956 // as close as possible to the cursor.
957 int mask = DIRECTION_45( dirV.Perpendicular() ).Mask() | DIRECTION_45( dirV ).Opposite().Mask();
958 gwsTarget.BuildForCursor( snapVias ? fp : fpProj, mask );
959 drawGateways( Dbg(), wxT( "target-aligned-gateways" ), target, gwsTarget, VECTOR2D( 0, 0 ),
960 VECTOR2D( 0, 200000 ) );
961 }
962
963 m_snapOnTarget = false;
964 }
965
966 m_currentTrace.SetDimensions( dims );
967 m_currentTrace.SetLayer( m_currentLayer );
968
969 DP_GAP_CONSTRAINT tmpGapC;
970 tmpGapC.SetOpt( dims.Gap() );
971 tmpGapC.SetMin( dims.Gap() - DP_DEFAULT_GAP_EPSILON );
972 tmpGapC.SetMax( dims.Gap() + DP_DEFAULT_GAP_EPSILON );
973
974 dims.SetGapConstraint( tmpGapC );
975 dims.SetMinClearance( minClearance.value() );
976
977 gwsEntry.SetDimensions( dims );
978 gwsTarget.SetDimensions( dims );
979
980 auto fits = gwsEntry.FitGateways( gwsEntry, gwsTarget, m_placingVia );
981
982 const DP_GATEWAYS::FIT_RESULT* bestFits[2] = { nullptr, nullptr };
983 const DP_GATEWAYS::FIT_RESULT* bestestFit = nullptr;
984
985 for( bool rejectNonObtuseAngles : { true, false } )
986 {
987 bestestFit = nullptr;
988 bestFits[0] = bestFits[1] = nullptr;
989
990 int bestScore[2] = { -100, -100 }; // cater for negative score adjustments
991 float bestCpr[2] = { 0.0f, 0.0f };
992
993 for( const auto& f : fits )
994 {
995 constexpr int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_STRAIGHT;
996
997 PNS_DBG( Dbg(), BeginGroup,
998 wxString::Format( wxT( "fit: bestCpr0=%.3f bestCpr1=%.3f diag=%d cpr=%.2f ar=%.2f score=%d" ),
999 bestCpr[0], bestCpr[1], f.diagonal ? 1 : 0, f.coupledRatio, f.aspectRatio,
1000 f.score ),
1001 0 );
1002 drawSingleGateway( Dbg(), f.entry, wxString::Format( "entry=%s", f.entry.GetName() ) );
1003 drawSingleGateway( Dbg(), f.target, wxString::Format( "target=%s", f.target.GetName() ) );
1004
1005 DIFF_PAIR dp( m_sizes.DiffPairGap() );
1006 dp.SetDimensions( dims );
1007 dp.SetShape( f.p, f.n );
1008
1009 if( rejectNonObtuseAngles )
1010 {
1011 DIRECTION_45 startDirP = m_start.DirP();
1012 DIRECTION_45 startDirN = m_start.DirN();
1013 auto angP = startDirP.Angle( dp.DirP( false ) );
1014 auto angN = startDirN.Angle( dp.DirN( false ) );
1015
1016 if( !( angP & angleMask ) || !( angN & angleMask ) )
1017 {
1018 PNS_DBG( Dbg(), Message,
1019 wxString::Format( " reject dp %s dn %s sd %s %s", dp.DirP( false ).Format(),
1020 dp.DirN( false ).Format(), startDirP.Format(), startDirN.Format() ) );
1021 PNS_DBGN( Dbg(), EndGroup );
1022
1023 continue;
1024 }
1025 }
1026 PNS_DBGN( Dbg(), EndGroup );
1027
1028 int index = f.diagonal ? 1 : 0;
1029 int score = f.score;
1030
1031 if( score > bestScore[index] || f.coupledRatio > bestCpr[index] * 2.0 )
1032 {
1033 bestFits[index] = &f;
1034 bestScore[index] = score;
1035 bestCpr[index] = f.coupledRatio;
1036 }
1037 else if( score == bestScore[index] )
1038 {
1039 if( f.coupledRatio > bestCpr[index] )
1040 {
1041 bestCpr[index] = f.coupledRatio;
1042 bestFits[index] = &f;
1043 }
1044 }
1045 }
1046
1047 if( bestFits[0] || bestFits[1] )
1048 break;
1049 }
1050
1051 for( int index = 0; index < 2; index++ )
1052 {
1053 const DP_GATEWAYS::FIT_RESULT* f = bestFits[index];
1054
1055 if( !f )
1056 continue;
1057
1058
1059 PNS_DBG( Dbg(), BeginGroup,
1060 wxString::Format( wxT( "best: diag=%d cpr=%.2f ar=%.2f score=%d cl=%d" ), f->diagonal ? 1 : 0,
1061 f->coupledRatio, f->aspectRatio, f->score, minClearance.value() ),
1062 0 );
1063
1064 drawSingleGateway( Dbg(), f->entry, wxString::Format( "entry=%s", f->entry.GetName() ) );
1065 drawSingleGateway( Dbg(), f->target, wxString::Format( "target=%s", f->target.GetName() ) );
1066
1067 PNS_DBG( Dbg(), AddShape, &f->p, RED, 20000, wxT( "l+" ) );
1068 PNS_DBG( Dbg(), AddShape, &f->n, BLUE, 20000, wxT( "l-" ) );
1069
1070 PNS_DBGN( Dbg(), EndGroup );
1071 }
1072
1073 if( m_startDiagonal && bestFits[1] )
1074 bestestFit = bestFits[1];
1075 else if( !m_startDiagonal && bestFits[0] )
1076 bestestFit = bestFits[0];
1077 else if( bestFits[1] )
1078 bestestFit = bestFits[1];
1079 else if( bestFits[0] )
1080 bestestFit = bestFits[0];
1081
1082 if( bestestFit )
1083 {
1084 m_currentTraceOk = true;
1085 m_currentTrace.SetShape( bestestFit->p, bestestFit->n );
1086 m_currentTrace.SetNets( m_netP, m_netN );
1087 m_currentTrace.SetDimensions( dims );
1088
1089 if( m_placingVia )
1090 {
1091 m_currentTrace.AppendVias( makeVia( m_currentTrace.CP().CLastPoint(), m_netP ),
1092 makeVia( m_currentTrace.CN().CLastPoint(), m_netN ) );
1093 }
1094 else
1095 {
1096 m_currentTrace.RemoveVias();
1097 }
1098
1099 return true;
1100 }
1101
1102 return m_currentTraceOk;
1103}
1104
1105
1106bool DIFF_PAIR_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1107{
1108 m_currentEndItem = aEndItem;
1109 m_fitOk = false;
1110
1111 delete m_lastNode;
1112 m_lastNode = nullptr;
1113
1114 bool retval = route( aP );
1115
1116 NODE* latestNode = m_currentNode;
1117 m_lastNode = latestNode->Branch();
1118
1119 assert( m_lastNode != nullptr );
1120 m_currentEnd = aP;
1121
1122 PNS_DBG( Dbg(), Message,
1123 wxString::Format( "target %d, p-sc %d n-sc %d", m_target ? 1 : 0,
1124 m_currentTrace.PLine().SegmentCount(), m_currentTrace.NLine().SegmentCount() ) );
1125
1126
1127 if( m_target )
1128 {
1129 if( m_currentTrace.PLine().SegmentCount() && m_currentTrace.NLine().SegmentCount() )
1130 {
1131 if( m_target->PrimN()->Net() == m_currentTrace.NLine().Net() )
1132 SplitAdjacentSegments( m_lastNode, m_target->PrimN(), m_currentTrace.NLine().CLastPoint() );
1133 if( m_target->PrimP()->Net() == m_currentTrace.PLine().Net() )
1134 SplitAdjacentSegments( m_lastNode, m_target->PrimP(), m_currentTrace.PLine().CLastPoint() );
1135
1136 if( Settings().RemoveLoops() )
1137 {
1140 }
1141 }
1142 }
1143
1144
1145 PNS_DBG( Dbg(), AddPoint, m_start.AnchorP(), RED, 100000, wxT( "start-p" ) );
1146 PNS_DBG( Dbg(), AddPoint, m_start.AnchorN(), BLUE, 100000, wxT( "start-n" ) );
1147
1149
1150 return retval;
1151}
1152
1153
1154static void drawGateways( PNS::DEBUG_DECORATOR* dbg, const wxString& groupName, PNS::DP_PRIMITIVE_PAIR& prims,
1155 PNS::DP_GATEWAYS& gws, VECTOR2D offset, VECTOR2D step )
1156{
1157 PNS_DBG( dbg, BeginGroup, groupName, 0 );
1158 for( auto gw : gws.Gateways() )
1159 {
1160 PNS_DBG( dbg, BeginGroup, wxString::Format( wxT( "gw-%s" ), gw.GetName() ), 0 );
1161
1162 SHAPE_CIRCLE gwp( gw.AnchorP(), 30000 );
1163 PNS_DBG( dbg, AddShape, &gwp, LIGHTRED, 20000, "gwp" );
1164
1165 if( gw.EntryP().SegmentCount() )
1166 {
1167 PNS_DBG( dbg, AddShape, &gw.EntryP(), LIGHTRED, 20000, "gwp" );
1168 }
1169
1170 SHAPE_CIRCLE gwn( gw.AnchorN(), 30000 );
1171 PNS_DBG( dbg, AddShape, &gwn, LIGHTBLUE, 30000, "gwn" );
1172
1173 if( gw.EntryN().SegmentCount() )
1174 {
1175 PNS_DBG( dbg, AddShape, &gw.EntryN(), LIGHTBLUE, 10000, "gwn" );
1176 }
1177
1178 SHAPE_LINE_CHAIN link( { gw.AnchorN(), gw.AnchorP() } );
1179 PNS_DBG( dbg, AddShape, &link, LIGHTGRAY, 10000, "link" );
1180
1181 auto midpoint = ( gw.AnchorN() + gw.AnchorP() ) / 2;
1182
1183 if( gw.HasPrimaryDirection() )
1184 {
1185 for( int dir = 0; dir < 8; dir++ )
1186 {
1188 if( dirV.Mask() & gw.PrimaryDirectionMask() )
1189 {
1190 auto dv = dirV.ToVector().Resize( 1000000 );
1191 SHAPE_LINE_CHAIN ds( { midpoint, midpoint + dv } );
1192 PNS_DBG( dbg, AddShape, &ds, LIGHTYELLOW, 10000, wxString::Format( "pdir-%s", dirV.Format() ) );
1193 }
1194 }
1195 }
1196
1197 PNS_DBGN( dbg, EndGroup );
1198 }
1199 PNS_DBGN( dbg, EndGroup );
1200}
1201
1202
1204{
1205 int prevDiffPairWidth = m_sizes.DiffPairWidth();
1206
1207 m_sizes = aSizes;
1208
1209 if( !m_idle )
1210 {
1211 // When continuing from an existing track in connected-track-width mode, preserve the
1212 // inherited diff pair width rather than reverting to the netclass default. This matches
1213 // the guard in LINE_PLACER::UpdateSizes() for single tracks.
1214 if( !m_sizes.TrackWidthIsExplicit() && m_hasFixedAnything )
1215 m_sizes.SetDiffPairWidth( prevDiffPairWidth );
1216
1217 DP_DIMENSIONS dims( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(), 0 );
1218 m_currentTrace.SetDimensions( dims );
1219
1220 if( m_currentTrace.EndsWithVias() )
1221 {
1222 m_currentTrace.SetViaDiameter( m_sizes.ViaDiameter() );
1223 m_currentTrace.SetViaDrill( m_sizes.ViaDrill() );
1224 }
1225 }
1226}
1227
1228
1229bool DIFF_PAIR_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1230{
1231 if( !m_fitOk && !Settings().AllowDRCViolations() )
1232 return false;
1233
1234 if( m_currentTrace.CP().SegmentCount() < 1 || m_currentTrace.CN().SegmentCount() < 1 )
1235 return false;
1236
1237 if( m_currentTrace.CP().SegmentCount() > 1 )
1238 m_initialDiagonal = !DIRECTION_45( m_currentTrace.CP().CSegment( -2 ) ).IsDiagonal();
1239
1240 TOPOLOGY topo( m_lastNode );
1241
1242 if( !m_snapOnTarget && !m_currentTrace.EndsWithVias() && !aForceFinish &&
1243 !Settings().GetFixAllSegments() )
1244 {
1245 SHAPE_LINE_CHAIN newP( m_currentTrace.CP() );
1246 SHAPE_LINE_CHAIN newN( m_currentTrace.CN() );
1247
1248 if( newP.SegmentCount() > 1 && newN.SegmentCount() > 1 )
1249 {
1250 newP.Remove( -1, -1 );
1251 newN.Remove( -1, -1 );
1252 }
1253
1254 m_currentTrace.SetShape( newP, newN );
1255 }
1256
1257 if( m_currentTrace.EndsWithVias() )
1258 {
1259 m_lastNode->Add( Clone( m_currentTrace.PLine().Via() ) );
1260 m_lastNode->Add( Clone( m_currentTrace.NLine().Via() ) );
1261 m_chainedPlacement = false;
1262 }
1263 else
1264 {
1265 m_chainedPlacement = !m_snapOnTarget && !aForceFinish;
1266 }
1267
1268 LINE &lineP = m_currentTrace.PLine();
1269 LINE &lineN = m_currentTrace.NLine();
1270
1271 m_lastNode->Add( lineP );
1272 m_lastNode->Add( lineN );
1273
1274 //topo.SimplifyLine( &lineP );
1275 //topo.SimplifyLine( &lineN );
1276
1277 m_currentTrace.SetLines( lineP, lineN );
1278
1279 m_prevPair = m_currentTrace.EndingPrimitives();
1280
1281 PNS_DBG( Dbg(), Message, wxString::Format("Fix-RT pp-p %p pp-n %p snapon=%d ff=%d", m_prevPair?m_prevPair->PrimP():0, m_prevPair?m_prevPair->PrimN():0, m_snapOnTarget?1:0, aForceFinish?1:0 ) );
1282
1284
1285 // avoid an use-after-free error (CommitPlacement calls NODE::Commit which will invalidate the shove heads state. Need to rethink the memory management).
1286 if( Settings().Mode() == RM_Shove )
1287 m_shove = std::make_unique<SHOVE>( m_world, Router() );
1288
1290
1291 m_currentTrace.Clear();
1292 m_currentTrace.ClearLinks();
1293
1294 m_placingVia = false;
1295 m_lastFixNode = nullptr;
1296
1297 if( m_snapOnTarget || aForceFinish )
1298 {
1299 m_idle = true;
1300 if( m_prevPair )
1301 m_prevPair->Unlink();
1302
1303 if( m_target )
1304 m_target->Unlink();
1305
1306 return true;
1307 }
1308 else
1309 {
1310 m_hasFixedAnything = true;
1312
1313 PNS_DBG( Dbg(), Message, wxString::Format("Fix-RT2 pp-p %p pp-n %p snapon=%d ff=%d", m_prevPair?m_prevPair->PrimP():0, m_prevPair?m_prevPair->PrimN():0, m_snapOnTarget?1:0, aForceFinish?1:0 ) );
1314
1315 initPlacement();
1316 return false;
1317 }
1318}
1319
1320
1322{
1323 m_world->KillChildren();
1324 m_lastNode = nullptr;
1325 return true;
1326}
1327
1328
1330{
1331 return m_currentTrace.CP().SegmentCount() > 0 || m_currentTrace.CN().SegmentCount() > 0;
1332}
1333
1334
1336{
1337 if( m_target )
1338 m_target->Unlink();
1339
1341
1342 if( m_lastFixNode )
1344 else if( m_prevPair )
1345 m_prevPair->Unlink();
1346
1347 m_lastFixNode = nullptr;
1348 m_lastNode = nullptr;
1349 m_currentNode = nullptr;
1350 return true;
1351}
1352
1353
1354void DIFF_PAIR_PLACER::GetModifiedNets( std::vector<NET_HANDLE> &aNets ) const
1355{
1356 aNets.push_back( m_netP );
1357 aNets.push_back( m_netN );
1358}
1359
1360
1362{
1363 SHAPE_LINE_CHAIN ratLineN, ratLineP;
1364 TOPOLOGY topo( m_lastNode );
1365
1366 if( topo.LeadingRatLine( &m_currentTrace.PLine(), ratLineP ) )
1367 m_router->GetInterface()->DisplayRatline( ratLineP, m_netP );
1368
1369 if( topo.LeadingRatLine ( &m_currentTrace.NLine(), ratLineN ) )
1370 m_router->GetInterface()->DisplayRatline( ratLineN, m_netN );
1371}
1372
1373
1374const std::vector<NET_HANDLE> DIFF_PAIR_PLACER::CurrentNets() const
1375{
1376 std::vector<NET_HANDLE> rv;
1377 rv.push_back( m_netP );
1378 rv.push_back( m_netN );
1379 return rv;
1380}
1381
1382}
int index
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.
int Mask() const
const VECTOR2I ToVector() const
Directions
Available directions, there are 8 of them, as on a rectilinear map (north = up) + an extra undefined ...
Definition direction45.h:49
bool IsDiagonal() const
Returns true if the direction is diagonal (e.g.
const std::string Format() const
Format the direction in a human readable word.
DIRECTION_45 Opposite() const
Return a direction opposite (180 degree) to (this).
void SetMin(T v)
Definition minoptmax.h:38
void SetOpt(T v)
Definition minoptmax.h:40
void SetMax(T v)
Definition minoptmax.h:39
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
ROUTER * Router() const
Return current router settings.
ROUTER * m_router
ROUTING_SETTINGS & Settings() const
Return the logger object, allowing to dump geometry to a file.
DEBUG_DECORATOR * Dbg() const
SHAPE_ARC & Arc()
Definition pns_arc.h:115
NODE * CurrentNode(bool aLoopsRemoved=false) const override
Return the most recent world state.
void SetOrthoMode(bool aOrthoMode) override
Function SetOrthoMode()
bool HasPlacedAnything() const override
NODE * m_world
current routing start point (end of tail, beginning of head)
bool tryWalkDp(NODE *aNode, DIFF_PAIR &aPair, bool aSolidsOnly)
route step, walk around mode
bool propagateDpHeadForces(const VECTOR2I &aP, VECTOR2I &aNewP)
bool rhMarkObstacles(const VECTOR2I &aP)
int m_viaDiameter
current via drill
static constexpr int DP_DEFAULT_GAP_EPSILON
std::optional< DP_PRIMITIVE_PAIR > m_prevPair
current algorithm iteration
bool ToggleVia(bool aEnabled) override
Enable/disable a via at the end of currently routed trace.
bool route(const VECTOR2I &aP)
Re-route the current track to point aP.
int m_iteration
pointer to world to search colliding items
bool rhShoveOnly(const VECTOR2I &aP)
route step, mark obstacles mode
bool Start(const VECTOR2I &aP, ITEM *aStartItem) override
Start routing a single track at point aP, taking item aStartItem as anchor (unless NULL).
bool findDpEndingPrimitives(NODE *aWorld, const VECTOR2I &aP, ITEM *aStartItem, DP_PRIMITIVE_PAIR &aPair, wxString *aErrorMsg)
const ITEM_SET Traces() override
Return the complete routed line, as a single-member ITEM_SET.
void GetModifiedNets(std::vector< NET_HANDLE > &aNets) const override
Function GetModifiedNets.
int m_viaDrill
current track width
void FlipPosture() override
Toggle the current posture (straight/diagonal) of the trace head.
DIFF_PAIR_PLACER(ROUTER *aRouter)
bool findDpMidtraceIntersection(NODE *aWorld, const VECTOR2I &aP, ITEM *aStartItem, DP_PRIMITIVE_PAIR &aPair, wxString *aErrorMsg)
bool attemptWalk(NODE *aNode, DIFF_PAIR *aCurrent, DIFF_PAIR &aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly)
void setWorld(NODE *aWorld)
Set the board to route.
NODE * m_currentNode
Postprocessed world state (including marked collisions & removed loops)
void initPlacement()
Initialize placement of a new line with given parameters.
const std::vector< NET_HANDLE > CurrentNets() const override
Return the net of currently routed track.
std::optional< DP_PRIMITIVE_PAIR > m_target
bool routeHead(const VECTOR2I &aP)
void UpdateSizes(const SIZES_SETTINGS &aSizes) override
Perform on-the-fly update of the width, via diameter & drill size from a settings class.
bool FindDpPrimitivePair(NODE *aWorld, const VECTOR2I &aP, ITEM *aItem, DP_PRIMITIVE_PAIR &aPair, wxString *aErrorMsg=nullptr)
bool rhWalkOnly(const VECTOR2I &aP)
route step, shove mode
bool Move(const VECTOR2I &aP, ITEM *aEndItem) override
Move the end of the currently routed trace to the point aP, taking aEndItem as anchor (if not NULL).
bool FixRoute(const VECTOR2I &aP, ITEM *aEndItem, bool aForceFinish) override
Commit the currently routed track to the parent node, taking aP as the final end point and aEndItem a...
bool m_placingVia
current via diameter
void updateLeadingRatLine()
Draw the "leading" ratsnest line, which connects the end of currently routed track and the nearest ye...
const VIA makeVia(const VECTOR2I &aP, NET_HANDLE aNet)
SIZES_SETTINGS m_sizes
Are we placing a via?
bool SetLayer(int aLayer) override
Set the current routing layer.
std::unique_ptr< SHOVE > m_shove
Current world state.
Basic class for a differential pair.
const SHAPE_LINE_CHAIN & CN() const
DIRECTION_45 DirP(bool aEnd) const
double CoupledLength() const
double Skew() const
void SetDimensions(const DP_DIMENSIONS &aDims)
const DP_DIMENSIONS & Dimensions() const
void SetShape(const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN &aN, bool aSwapLanes=false)
NET_HANDLE NetP() const
std::optional< DP_PRIMITIVE_PAIR > BuildMidpairIntersection(PNS::SEGMENT *aStartSeg, const VECTOR2I &aP)
const SHAPE_LINE_CHAIN & CP() const
DIRECTION_45 DirN(bool aEnd) const
NET_HANDLE NetN() const
void SetMinClearance(int aClearance)
void SetGapConstraint(const DP_GAP_CONSTRAINT &aGapConstraint)
A set of gateways calculated for the cursor or starting/ending primitive pair.
void BuildForCursor(const VECTOR2I &aCursorPos, int aDirectionMask=-1)
void BuildFromPrimitivePair(const DP_PRIMITIVE_PAIR &aPair, bool aPreferDiagonal)
std::vector< DP_GATEWAY > & Gateways()
void SetFitVias(bool aEnable)
void SetDimensions(const DP_DIMENSIONS &aDims)
std::vector< FIT_RESULT > FitGateways(DP_GATEWAYS &aEntry, DP_GATEWAYS &aTarget, bool aFitVias)
Define a "gateway" for routing a differential pair - e.g.
const SHAPE_LINE_CHAIN & EntryP() const
const VECTOR2I & AnchorN() const
const VECTOR2I & AnchorP() const
const SHAPE_LINE_CHAIN & EntryN() const
const wxString GetName() const
Store starting/ending primitives (pads, vias or segments) for a differential pair.
DIRECTION_45 DirN() const
const VECTOR2I & AnchorN() const
const VECTOR2I & AnchorP() const
void SetIsMidtrace(bool aMidtrace)
void SetAnchors(const VECTOR2I &aAnchorP, const VECTOR2I &aAnchorN)
DIRECTION_45 DirP() const
void Add(const LINE &aLine)
Base class for PNS router board items.
Definition pns_item.h:98
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
std::set< int > RelevantShapeLayers(const ITEM *aOther) const
Returns the set of layers on which either this or the other item can have a unique shape.
Definition pns_item.cpp:83
void SetLayer(int aLayer)
Definition pns_item.h:215
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:268
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
int LinkCount(int aMask=-1) const
Definition pns_joint.h:318
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
const VECTOR2I & CPoint(int aIdx) const
Definition pns_line.h:154
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:145
int PointCount() const
Definition pns_line.h:149
Keep the router "world" - i.e.
Definition pns_node.h:243
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:157
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition pns_node.cpp:492
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.
std::optional< OBSTACLE > OPT_OBSTACLE
Definition pns_node.h:253
int Depth() const
Definition pns_node.h:304
RULE_RESOLVER * GetRuleResolver() const
Return the number of joints.
Definition pns_node.h:292
void AllItemsInNet(NET_HANDLE aNet, std::set< ITEM * > &aItems, int aKindMask=-1)
void KillChildren()
Perform various optimizations of the lines being routed, attempting to make the lines shorter and les...
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
virtual bool removeLoops(NODE *aNode, LINE &aLatest)
PLACEMENT_ALGO(ROUTER *aRouter)
PNS_LAYER_RANGE GetViaLayerRange(const SIZES_SETTINGS &aSizes) const
Return the layer span a via placed with aSizes occupies.
Definition pns_router.h:144
virtual RULE_RESOLVER * GetRuleResolver()=0
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:254
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:249
void CommitRouting()
NODE * GetWorld() const
Definition pns_router.h:200
virtual int Clearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true)=0
virtual bool DpNetPair(const ITEM *aItem, NET_HANDLE &aNetP, NET_HANDLE &aNetN)=0
const SEG & Seg() const
The actual Push and Shove algorithm.
Definition pns_shove.h:47
void ForceClearance(bool aEnabled, int aClearance)
Definition pns_shove.h:93
bool ShoveObstacleLine(const LINE &aCurLine, const LINE &aObstacleLine, LINE &aResultLine)
bool LeadingRatLine(const LINE *aTrack, SHAPE_LINE_CHAIN &aRatLine)
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
int Diameter(int aLayer) const
Definition pns_via.h:227
void SetDiameter(int aLayer, int aDiameter)
Definition pns_via.h:234
const VECTOR2I & Pos() const
Definition pns_via.h:206
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition pns_via.h:302
void SetPos(const VECTOR2I &aPos)
Definition pns_via.h:208
void SetIterationLimit(const int aIterLimit)
void SetSolidsOnly(bool aSolidsOnly)
STATUS Route(const LINE &aInitialPath, LINE &aWalkPath, bool aOptimize=true)
void SetCollisionFilter(COLLISION_FILTER_FUNC aFilter)
void SetAllowedPolicies(std::vector< WALK_POLICY > aPolicies)
Represent a contiguous set of PCB layers.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition seg.cpp:692
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Simplify(int aTolerance=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
int SegmentCount() const
Return the number of segments in this line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:303
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:310
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
@ LIGHTBLUE
Definition color4d.h:58
@ BLUE
Definition color4d.h:52
@ LIGHTGRAY
Definition color4d.h:43
@ LIGHTYELLOW
Definition color4d.h:45
@ GREEN
Definition color4d.h:53
@ YELLOW
Definition color4d.h:63
@ LIGHTRED
Definition color4d.h:61
@ RED
Definition color4d.h:55
#define _(s)
Push and Shove diff pair dimensions (gap) settings dialog.
MINOPTMAX< int > DP_GAP_CONSTRAINT
static void drawGateways(PNS::DEBUG_DECORATOR *dbg, const wxString &groupName, PNS::DP_PRIMITIVE_PAIR &prims, PNS::DP_GATEWAYS &gws, VECTOR2D offset, VECTOR2D step)
OPT_VECTOR2I getDanglingAnchor(NODE *aNode, ITEM *aItem)
const wxString Format(const MINOPTMAX< int > x)
bool SplitAdjacentSegments(NODE *aNode, ITEM *aSeg, const VECTOR2I &aP)
Snaps the point aP to segment aSeg.
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
void * NET_HANDLE
Definition pns_item.h:55
static void drawSingleGateway(DEBUG_DECORATOR *dbg, DP_GATEWAY gw, wxString grpName)
std::unique_ptr< typename std::remove_const< T >::type > Clone(const T &aItem)
Definition pns_item.h:344
@ DIFF_PAIR
#define PNS_DBG(dbg, method,...)
#define PNS_DBGN(dbg, method)
#define PNS_HULL_MARGIN
Definition pns_line.h:45
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
COLLISION_FILTER_FUNC m_filter
Definition pns_node.h:122
LINE lines[MaxWalkPolicies]
STATUS status[MaxWalkPolicies]
int clearance
wxString result
Test unit parsing edge cases and error handling.
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