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 int bestId[2] = {-1, -1 };
993
994 int current_id = 0;
995
996 for( const auto& f : fits )
997 {
998 constexpr int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_STRAIGHT;
999
1000 PNS_DBG( Dbg(), BeginGroup,
1001 wxString::Format( wxT( "fit[%d]: bestId0=%d bestId1=%d bestS0=%d bestS1=%d bestCpr0=%.3f bestCpr1=%.3f diag=%d cpr=%.2f ar=%.2f score=%d scoreEnt=%d scoreTgt=%d" ),
1002 current_id, bestId[0], bestId[1], bestScore[0], bestScore[1], bestCpr[0], bestCpr[1], f.diagonal ? 1 : 0, f.coupledRatio, f.aspectRatio,
1003 f.score, f.entry.Priority(), f.target.Priority() ),
1004 0 );
1005 drawSingleGateway( Dbg(), f.entry, wxString::Format( "entry=%s", f.entry.GetName() ) );
1006 drawSingleGateway( Dbg(), f.target, wxString::Format( "target=%s", f.target.GetName() ) );
1007
1008 DIFF_PAIR dp( m_sizes.DiffPairGap() );
1009 dp.SetDimensions( dims );
1010 dp.SetShape( f.p, f.n );
1011
1012 if( rejectNonObtuseAngles )
1013 {
1014 DIRECTION_45 startDirP = m_start.DirP();
1015 DIRECTION_45 startDirN = m_start.DirN();
1016 auto angP = startDirP.Angle( dp.DirP( false ) );
1017 auto angN = startDirN.Angle( dp.DirN( false ) );
1018
1019 bool startHasDirection = m_start.DirP().IsDefined() && m_start.DirN().IsDefined();
1020
1021 if( startHasDirection && ( !( angP & angleMask ) || !( angN & angleMask ) ) )
1022 {
1023 PNS_DBG( Dbg(), Message,
1024 wxString::Format( " reject dp %s dn %s sd %s %s", dp.DirP( false ).Format(),
1025 dp.DirN( false ).Format(), startDirP.Format(), startDirN.Format() ) );
1026 PNS_DBGN( Dbg(), EndGroup );
1027 current_id++;
1028 continue;
1029 }
1030 }
1031 PNS_DBGN( Dbg(), EndGroup );
1032
1033 int index = f.diagonal ? 1 : 0;
1034 int score = f.score;
1035
1036 if( score >= bestScore[index] || (f.coupledRatio > bestCpr[index] * 5.0 && bestCpr[index] < 0.2 ) )
1037 {
1038 bestFits[index] = &f;
1039 bestScore[index] = score;
1040 bestCpr[index] = f.coupledRatio;
1041 bestId[index] = current_id;
1042 }
1043 else if( score == bestScore[index] )
1044 {
1045 if( f.coupledRatio >= bestCpr[index] )
1046 {
1047 bestCpr[index] = f.coupledRatio;
1048 bestFits[index] = &f;
1049 bestId[index] = current_id;
1050 }
1051 }
1052
1053 current_id++;
1054 }
1055
1056 if( bestFits[0] || bestFits[1] )
1057 break;
1058 }
1059
1060 for( int index = 0; index < 2; index++ )
1061 {
1062 const DP_GATEWAYS::FIT_RESULT* f = bestFits[index];
1063
1064 if( !f )
1065 continue;
1066
1067
1068 PNS_DBG( Dbg(), BeginGroup,
1069 wxString::Format( wxT( "best: diag=%d cpr=%.2f ar=%.2f score=%d cl=%d" ), f->diagonal ? 1 : 0,
1070 f->coupledRatio, f->aspectRatio, f->score, minClearance.value() ),
1071 0 );
1072
1073 drawSingleGateway( Dbg(), f->entry, wxString::Format( "entry=%s", f->entry.GetName() ) );
1074 drawSingleGateway( Dbg(), f->target, wxString::Format( "target=%s", f->target.GetName() ) );
1075
1076 PNS_DBG( Dbg(), AddShape, &f->p, RED, 20000, wxT( "l+" ) );
1077 PNS_DBG( Dbg(), AddShape, &f->n, BLUE, 20000, wxT( "l-" ) );
1078
1079 PNS_DBGN( Dbg(), EndGroup );
1080 }
1081
1082 // Corner case for making a 45-degree turn with poorly aligned exits (eg #21343), can result with a diff pair with a gap
1083 // completely out of range (very low CPR). Force other posture if its CPR exceeds the chosen posture by a big enough factor
1084 if( bestFits[0] && bestFits[1] )
1085 {
1086 if( bestFits[0]->coupledRatio > DP_CPR_SWITCH_POSTURE_RATIO * bestFits[1]->coupledRatio )
1087 bestestFit = bestFits[0];
1088 else if ( bestFits[1]->coupledRatio > DP_CPR_SWITCH_POSTURE_RATIO * bestFits[0]->coupledRatio )
1089 bestestFit = bestFits[1];
1090 }
1091
1092 if( !bestestFit )
1093 {
1094 if( m_startDiagonal && bestFits[1] )
1095 bestestFit = bestFits[1];
1096 else if( !m_startDiagonal && bestFits[0] )
1097 bestestFit = bestFits[0];
1098 else if( bestFits[1] )
1099 bestestFit = bestFits[1];
1100 else if( bestFits[0] )
1101 bestestFit = bestFits[0];
1102 }
1103
1104 if( bestestFit )
1105 {
1106 m_currentTraceOk = true;
1107 m_currentTrace.SetShape( bestestFit->p, bestestFit->n );
1108 m_currentTrace.SetNets( m_netP, m_netN );
1109 m_currentTrace.SetDimensions( dims );
1110
1111 if( m_placingVia )
1112 {
1113 m_currentTrace.AppendVias( makeVia( m_currentTrace.CP().CLastPoint(), m_netP ),
1114 makeVia( m_currentTrace.CN().CLastPoint(), m_netN ) );
1115 }
1116 else
1117 {
1118 m_currentTrace.RemoveVias();
1119 }
1120
1121 return true;
1122 }
1123
1124 return m_currentTraceOk;
1125}
1126
1127
1128bool DIFF_PAIR_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
1129{
1130 m_currentEndItem = aEndItem;
1131 m_fitOk = false;
1132
1133 delete m_lastNode;
1134 m_lastNode = nullptr;
1135
1136 bool retval = route( aP );
1137
1138 NODE* latestNode = m_currentNode;
1139 m_lastNode = latestNode->Branch();
1140
1141 assert( m_lastNode != nullptr );
1142 m_currentEnd = aP;
1143
1144 PNS_DBG( Dbg(), Message,
1145 wxString::Format( "target %d, p-sc %d n-sc %d", m_target ? 1 : 0,
1146 m_currentTrace.PLine().SegmentCount(), m_currentTrace.NLine().SegmentCount() ) );
1147
1148
1149 if( m_target && m_target->PrimP() && m_target->PrimN() )
1150 {
1151 if( m_currentTrace.PLine().SegmentCount() && m_currentTrace.NLine().SegmentCount() )
1152 {
1153 if( m_target->PrimN()->Net() == m_currentTrace.NLine().Net() )
1154 SplitAdjacentSegments( m_lastNode, m_target->PrimN(), m_currentTrace.NLine().CLastPoint() );
1155 if( m_target->PrimP()->Net() == m_currentTrace.PLine().Net() )
1156 SplitAdjacentSegments( m_lastNode, m_target->PrimP(), m_currentTrace.PLine().CLastPoint() );
1157
1158 if( Settings().RemoveLoops() )
1159 {
1162 }
1163 }
1164 }
1165
1166
1167 PNS_DBG( Dbg(), AddPoint, m_start.AnchorP(), RED, 100000, wxT( "start-p" ) );
1168 PNS_DBG( Dbg(), AddPoint, m_start.AnchorN(), BLUE, 100000, wxT( "start-n" ) );
1169
1171
1172 return retval;
1173}
1174
1175
1176static void drawGateways( PNS::DEBUG_DECORATOR* dbg, const wxString& groupName, PNS::DP_PRIMITIVE_PAIR& prims,
1177 PNS::DP_GATEWAYS& gws, VECTOR2D offset, VECTOR2D step )
1178{
1179 PNS_DBG( dbg, BeginGroup, groupName, 0 );
1180 for( auto gw : gws.Gateways() )
1181 {
1182 PNS_DBG( dbg, BeginGroup, wxString::Format( wxT( "gw-%s" ), gw.GetName() ), 0 );
1183
1184 SHAPE_CIRCLE gwp( gw.AnchorP(), 30000 );
1185 PNS_DBG( dbg, AddShape, &gwp, LIGHTRED, 20000, "gwp" );
1186
1187 if( gw.EntryP().SegmentCount() )
1188 {
1189 PNS_DBG( dbg, AddShape, &gw.EntryP(), LIGHTRED, 20000, "gwp" );
1190 }
1191
1192 SHAPE_CIRCLE gwn( gw.AnchorN(), 30000 );
1193 PNS_DBG( dbg, AddShape, &gwn, LIGHTBLUE, 30000, "gwn" );
1194
1195 if( gw.EntryN().SegmentCount() )
1196 {
1197 PNS_DBG( dbg, AddShape, &gw.EntryN(), LIGHTBLUE, 10000, "gwn" );
1198 }
1199
1200 SHAPE_LINE_CHAIN link( { gw.AnchorN(), gw.AnchorP() } );
1201 PNS_DBG( dbg, AddShape, &link, LIGHTGRAY, 10000, "link" );
1202
1203 auto midpoint = ( gw.AnchorN() + gw.AnchorP() ) / 2;
1204
1205 if( gw.HasPrimaryDirection() )
1206 {
1207 for( int dir = 0; dir < 8; dir++ )
1208 {
1210 if( dirV.Mask() & gw.PrimaryDirectionMask() )
1211 {
1212 auto dv = dirV.ToVector().Resize( 1000000 );
1213 SHAPE_LINE_CHAIN ds( { midpoint, midpoint + dv } );
1214 PNS_DBG( dbg, AddShape, &ds, LIGHTYELLOW, 10000, wxString::Format( "pdir-%s", dirV.Format() ) );
1215 }
1216 }
1217 }
1218
1219 PNS_DBGN( dbg, EndGroup );
1220 }
1221 PNS_DBGN( dbg, EndGroup );
1222}
1223
1224
1226{
1227 int prevDiffPairWidth = m_sizes.DiffPairWidth();
1228
1229 m_sizes = aSizes;
1230
1231 if( !m_idle )
1232 {
1233 // When continuing from an existing track in connected-track-width mode, preserve the
1234 // inherited diff pair width rather than reverting to the netclass default. This matches
1235 // the guard in LINE_PLACER::UpdateSizes() for single tracks.
1236 if( !m_sizes.TrackWidthIsExplicit() && m_hasFixedAnything )
1237 m_sizes.SetDiffPairWidth( prevDiffPairWidth );
1238
1239 DP_DIMENSIONS dims( m_sizes.DiffPairWidth(), m_sizes.DiffPairGap(), viaGap(), m_sizes.ViaDiameter(), 0 );
1240 m_currentTrace.SetDimensions( dims );
1241
1242 if( m_currentTrace.EndsWithVias() )
1243 {
1244 m_currentTrace.SetViaDiameter( m_sizes.ViaDiameter() );
1245 m_currentTrace.SetViaDrill( m_sizes.ViaDrill() );
1246 }
1247 }
1248}
1249
1250
1251bool DIFF_PAIR_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
1252{
1253 if( !m_fitOk && !Settings().AllowDRCViolations() )
1254 return false;
1255
1256 if( m_currentTrace.CP().SegmentCount() < 1 || m_currentTrace.CN().SegmentCount() < 1 )
1257 return false;
1258
1259 if( m_currentTrace.CP().SegmentCount() > 1 )
1260 m_initialDiagonal = !DIRECTION_45( m_currentTrace.CP().CSegment( -2 ) ).IsDiagonal();
1261
1262 TOPOLOGY topo( m_lastNode );
1263
1264 if( !m_snapOnTarget && !m_currentTrace.EndsWithVias() && !aForceFinish &&
1265 !Settings().GetFixAllSegments() )
1266 {
1267 SHAPE_LINE_CHAIN newP( m_currentTrace.CP() );
1268 SHAPE_LINE_CHAIN newN( m_currentTrace.CN() );
1269
1270 if( newP.SegmentCount() > 1 && newN.SegmentCount() > 1 )
1271 {
1272 newP.Remove( -1, -1 );
1273 newN.Remove( -1, -1 );
1274 }
1275
1276 m_currentTrace.SetShape( newP, newN );
1277 }
1278
1279 if( m_currentTrace.EndsWithVias() )
1280 {
1281 m_lastNode->Add( Clone( m_currentTrace.PLine().Via() ) );
1282 m_lastNode->Add( Clone( m_currentTrace.NLine().Via() ) );
1283 m_chainedPlacement = false;
1284 }
1285 else
1286 {
1287 m_chainedPlacement = !m_snapOnTarget && !aForceFinish;
1288 }
1289
1290 LINE &lineP = m_currentTrace.PLine();
1291 LINE &lineN = m_currentTrace.NLine();
1292
1293 m_lastNode->Add( lineP );
1294 m_lastNode->Add( lineN );
1295
1296 //topo.SimplifyLine( &lineP );
1297 //topo.SimplifyLine( &lineN );
1298
1299 m_currentTrace.SetLines( lineP, lineN );
1300
1301 m_prevPair = m_currentTrace.EndingPrimitives();
1302
1303 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 ) );
1304
1306
1307 // avoid an use-after-free error (CommitPlacement calls NODE::Commit which will invalidate the shove heads state. Need to rethink the memory management).
1308 if( Settings().Mode() == RM_Shove )
1309 m_shove = std::make_unique<SHOVE>( m_world, Router() );
1310
1312
1313 m_currentTrace.Clear();
1314 m_currentTrace.ClearLinks();
1315
1316 m_placingVia = false;
1317 m_lastFixNode = nullptr;
1318
1319 if( m_snapOnTarget || aForceFinish )
1320 {
1321 m_idle = true;
1322 if( m_prevPair )
1323 m_prevPair->Unlink();
1324
1325 if( m_target )
1326 m_target->Unlink();
1327
1328 return true;
1329 }
1330 else
1331 {
1332 m_hasFixedAnything = true;
1334
1335 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 ) );
1336
1337 initPlacement();
1338 return false;
1339 }
1340}
1341
1342
1344{
1345 m_world->KillChildren();
1346 m_lastNode = nullptr;
1347 return true;
1348}
1349
1350
1352{
1353 return m_currentTrace.CP().SegmentCount() > 0 || m_currentTrace.CN().SegmentCount() > 0;
1354}
1355
1356
1358{
1359 if( m_target )
1360 m_target->Unlink();
1361
1363
1364 if( m_lastFixNode )
1366 else if( m_prevPair )
1367 m_prevPair->Unlink();
1368
1369 m_lastFixNode = nullptr;
1370 m_lastNode = nullptr;
1371 m_currentNode = nullptr;
1372 return true;
1373}
1374
1375
1376void DIFF_PAIR_PLACER::GetModifiedNets( std::vector<NET_HANDLE> &aNets ) const
1377{
1378 aNets.push_back( m_netP );
1379 aNets.push_back( m_netN );
1380}
1381
1382
1384{
1385 SHAPE_LINE_CHAIN ratLineN, ratLineP;
1386 TOPOLOGY topo( m_lastNode, m_router->GetInterface() );
1387
1388 if( topo.LeadingRatLine( &m_currentTrace.PLine(), ratLineP ) )
1389 m_router->GetInterface()->DisplayRatline( ratLineP, m_netP );
1390
1391 if( topo.LeadingRatLine ( &m_currentTrace.NLine(), ratLineN ) )
1392 m_router->GetInterface()->DisplayRatline( ratLineN, m_netN );
1393}
1394
1395
1396const std::vector<NET_HANDLE> DIFF_PAIR_PLACER::CurrentNets() const
1397{
1398 std::vector<NET_HANDLE> rv;
1399 rv.push_back( m_netP );
1400 rv.push_back( m_netN );
1401 return rv;
1402}
1403
1404}
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:41
void SetOpt(T v)
Definition minoptmax.h:43
void SetMax(T v)
Definition minoptmax.h:42
void SetDebugDecorator(DEBUG_DECORATOR *aDecorator)
Assign a debug decorator allowing this algo to draw extra graphics for visual debugging.
ROUTER * Router() const
Return the instance of our router.
ROUTER * m_router
ROUTING_SETTINGS & Settings() const
Return current router settings.
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
Force the router to place a straight 90/45 degree trace (with the end as near to the cursor as possib...
bool HasPlacedAnything() const override
NODE * m_world
pointer to world to search colliding items
bool tryWalkDp(NODE *aNode, DIFF_PAIR &aPair, bool aSolidsOnly)
bool propagateDpHeadForces(const VECTOR2I &aP, VECTOR2I &aNewP)
bool rhMarkObstacles(const VECTOR2I &aP)
route step, mark obstacles mode
int m_currentWidth
current track width
int m_viaDiameter
current via diameter
static constexpr int DP_DEFAULT_GAP_EPSILON
std::optional< DP_PRIMITIVE_PAIR > m_prevPair
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
current algorithm iteration
bool rhShoveOnly(const VECTOR2I &aP)
route step, shove 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
Return the nets of all currently routed trace(s)
int m_viaDrill
current via drill
static constexpr double DP_CPR_SWITCH_POSTURE_RATIO
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
Current world state.
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, walk around 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...
NODE * m_lastNode
Postprocessed world state (including marked collisions & removed loops)
bool m_placingVia
Are we placing a via?
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)
bool SetLayer(int aLayer) override
Set the current routing layer.
std::unique_ptr< SHOVE > m_shove
The shove engine.
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:272
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:322
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
Return the aIdx-th point of the line.
Definition pns_line.h:154
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
int PointCount() const
Definition pns_line.h:149
Keep the router "world" - i.e.
Definition pns_node.h:244
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:254
int Depth() const
Return the number of nodes in the inheritance chain (wrs to the root node).
Definition pns_node.h:305
RULE_RESOLVER * GetRuleResolver() const
Definition pns_node.h:293
void AllItemsInNet(NET_HANDLE aNet, std::set< ITEM * > &aItems, int aKindMask=-1)
void KillChildren()
Destroy all child nodes. Applicable only to the root node.
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))
A quick shortcut to optimize a line without creating and setting up an optimizer.
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:151
virtual RULE_RESOLVER * GetRuleResolver()=0
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:261
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:256
void CommitRouting()
NODE * GetWorld() const
Definition pns_router.h:207
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:651
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:312
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:335
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
@ 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:348
@ 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:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707