KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_topology.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 <wx/log.h>
23
24#include <chrono>
25#include <stack>
26
27#include <advanced_config.h>
28
29#include "pns_line.h"
30#include "pns_segment.h"
31#include "pns_arc.h"
32#include "pns_node.h"
33#include "pns_joint.h"
34#include "pns_solid.h"
35#include "pns_router.h"
36#include "pns_utils.h"
37
38#include "pns_diff_pair.h"
39#include "pns_topology.h"
40
41#include "pcb_track.h"
42
43#include <board.h>
45#include <pad.h>
46
47namespace PNS {
48
50{
51 if( !aLine->IsLinked() || !aLine->SegmentCount() )
52 return false;
53
54 LINKED_ITEM* root = aLine->GetLink( 0 );
55 LINE l = m_world->AssembleLine( root, nullptr, false, false, false );
56 SHAPE_LINE_CHAIN simplified( l.CLine() );
57
58 simplified.Simplify();
59
60 if( simplified.PointCount() != l.PointCount() )
61 {
62 m_world->Remove( l );
63 LINE lnew( l );
64 lnew.SetShape( simplified );
65 m_world->Add( lnew );
66 return true;
67 }
68
69 return false;
70}
71
72
74{
75 std::deque<const JOINT*> searchQueue;
76 JOINT_SET processed;
77
78 searchQueue.push_back( aStart );
79 processed.insert( aStart );
80
81 while( !searchQueue.empty() )
82 {
83 const JOINT* current = searchQueue.front();
84 searchQueue.pop_front();
85
86 for( ITEM* item : current->LinkList() )
87 {
88 if( item->OfKind( ITEM::SEGMENT_T ) )
89 {
90 const JOINT* a = m_world->FindJoint( item->Anchor( 0 ), item );;
91 const JOINT* b = m_world->FindJoint( item->Anchor( 1 ), item );;
92 const JOINT* next = ( *a == *current ) ? b : a;
93
94 if( processed.find( next ) == processed.end() )
95 {
96 processed.insert( next );
97 searchQueue.push_back( next );
98 }
99 }
100 }
101 }
102
103 return processed;
104}
105
106
108 PNS_LAYER_RANGE& aLayers, ITEM*& aItem )
109{
110 LINE track( *aTrack );
112
113 if( !track.PointCount() )
114 return false;
115
116 std::unique_ptr<NODE> tmpNode( m_world->Branch() );
117
118 track.ClearLinks();
119 tmpNode->Add( track );
120
121 const JOINT* jt = tmpNode->FindJoint( track.CLastPoint(), &track );
122
123 if( !jt || m_world->GetRuleResolver()->NetCode( jt->Net() ) <= 0 )
124 return false;
125
126 if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 )
127 || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
128 {
129 end = jt->Pos();
130 aLayers = jt->Layers();
131 aItem = jt->LinkList()[0];
132 }
133 else
134 {
135 int anchor;
136
137 TOPOLOGY topo( tmpNode.get() );
138 ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
139
140 if( !it )
141 return false;
142
143 end = it->Anchor( anchor );
144 aLayers = it->Layers();
145 aItem = it;
146 }
147
148 aPoint = end;
149 return true;
150}
151
152
153bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
154{
156 // Ratline doesn't care about the layer
157 PNS_LAYER_RANGE layers;
158 ITEM* unusedItem;
159
160 if( !NearestUnconnectedAnchorPoint( aTrack, end, layers, unusedItem ) )
161 return false;
162
163 aRatLine.Clear();
164 aRatLine.Append( aTrack->CLastPoint() );
165 aRatLine.Append( end );
166 return true;
167}
168
169
170ITEM* TOPOLOGY::NearestUnconnectedItem( const JOINT* aStart, int* aAnchor, int aKindMask )
171{
172 std::set<ITEM*> disconnected;
173
174 m_world->AllItemsInNet( aStart->Net(), disconnected );
175
176 for( const JOINT* jt : ConnectedJoints( aStart ) )
177 {
178 for( ITEM* link : jt->LinkList() )
179 {
180 if( disconnected.find( link ) != disconnected.end() )
181 disconnected.erase( link );
182 }
183 }
184
185 int best_dist = INT_MAX;
186 ITEM* best = nullptr;
187
188 for( ITEM* item : disconnected )
189 {
190 if( item->OfKind( aKindMask ) )
191 {
192 for( int i = 0; i < item->AnchorCount(); i++ )
193 {
194 VECTOR2I p = item->Anchor( i );
195 int d = ( p - aStart->Pos() ).EuclideanNorm();
196
197 if( d < best_dist )
198 {
199 best_dist = d;
200 best = item;
201
202 if( aAnchor )
203 *aAnchor = i;
204 }
205 }
206 }
207 }
208
209 return best;
210}
211
212
214 std::set<ITEM*>& aVisited,
215 bool aFollowLockedSegments )
216{
217 using clock = std::chrono::steady_clock;
218
219 PATH_RESULT best;
220 best.m_end = aStartJoint;
221
222 const int timeoutMs = ADVANCED_CFG::GetCfg().m_FollowBranchTimeout;
223 auto startTime = clock::now();
224
225 // State for iterative DFS: current joint, previous item, accumulated path items,
226 // accumulated length, and the set of visited joints for this path
227 struct STATE
228 {
229 const JOINT* joint;
230 LINKED_ITEM* prev;
231 ITEM_SET pathItems;
232 int pathLength;
233 std::set<const JOINT*> visitedJoints;
234 ITEM* via;
235 };
236
237 std::stack<STATE> stateStack;
238
239 // Initialize with starting state
240 STATE initial;
241 initial.joint = aStartJoint;
242 initial.prev = aPrev;
243 initial.pathLength = 0;
244 initial.visitedJoints.insert( aStartJoint );
245 initial.via = nullptr;
246
247 stateStack.push( std::move( initial ) );
248
249 while( !stateStack.empty() )
250 {
251 // Check timeout
252 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
253 clock::now() - startTime ).count();
254
255 if( elapsed > timeoutMs )
256 {
257 wxLogTrace( wxT( "PNS_TUNE" ),
258 wxT( "followBranch: timeout after %lld ms, returning best path found" ),
259 elapsed );
260 break;
261 }
262
263 STATE current = std::move( stateStack.top() );
264 stateStack.pop();
265
266 const JOINT* joint = current.joint;
267 ITEM_SET links( joint->CLinks() );
268
269 // Check for via at this joint
270 ITEM* via = nullptr;
271
272 for( ITEM* link : links )
273 {
274 if( link->OfKind( ITEM::VIA_T ) && !aVisited.contains( link ) )
275 {
276 via = link;
277 break;
278 }
279 }
280
281 // Find all unvisited branches from this joint
282 bool foundBranch = false;
283
284 for( ITEM* link : links )
285 {
286 if( !link->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
287 continue;
288
289 if( link == current.prev )
290 continue;
291
292 if( aVisited.contains( link ) )
293 continue;
294
295 LINE l = m_world->AssembleLine( static_cast<LINKED_ITEM*>( link ), nullptr,
296 false, aFollowLockedSegments );
297
298 if( l.CPoint( 0 ) != joint->Pos() )
299 l.Reverse();
300
301 const JOINT* nextJoint = m_world->FindJoint( l.CLastPoint(), &l );
302
303 // Skip if we've already visited this joint in the current path
304 if( current.visitedJoints.count( nextJoint ) )
305 continue;
306
307 foundBranch = true;
308
309 // Build new state for this branch
310 STATE nextState;
311 nextState.joint = nextJoint;
312 nextState.prev = l.Links().back();
313 nextState.pathItems = current.pathItems;
314 nextState.pathLength = current.pathLength + l.CLine().Length();
315 nextState.visitedJoints = current.visitedJoints;
316 nextState.visitedJoints.insert( nextJoint );
317 nextState.via = via;
318
319 // Add via and line to path
320 if( via )
321 nextState.pathItems.Add( via );
322
323 nextState.pathItems.Add( l );
324
325 stateStack.push( std::move( nextState ) );
326 }
327
328 // If no branches found, this is a terminal joint - check if it's the best path
329 if( !foundBranch )
330 {
331 if( current.pathLength > best.m_length )
332 {
333 best.m_length = current.pathLength;
334 best.m_end = joint;
335 best.m_items = current.pathItems;
336 }
337 }
338 }
339
340 wxLogTrace( wxT( "PNS_TUNE" ),
341 wxT( "followBranch: completed with best path length=%d, %d items" ),
342 best.m_length, best.m_items.Size() );
343
344 return best;
345}
346
347
348ITEM_SET TOPOLOGY::followTrivialPath( LINE* aLine2, const JOINT** aTerminalJointA,
349 const JOINT** aTerminalJointB,
350 bool aFollowLockedSegments )
351{
352 assert( aLine2->IsLinked() );
353
354 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath START ===" ) );
355 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: initial line has %d segments, %zu links" ),
356 aLine2->SegmentCount(), aLine2->Links().size() );
357 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: line endpoints: (%d,%d) to (%d,%d)" ),
358 aLine2->CPoint( 0 ).x, aLine2->CPoint( 0 ).y,
359 aLine2->CLastPoint().x, aLine2->CLastPoint().y );
360
362 path.Add( *aLine2 );
363
364 std::set<ITEM*> visited;
365
366 for( LINKED_ITEM* link : aLine2->Links() )
367 visited.insert( link );
368
369 const JOINT* jtA = m_world->FindJoint( aLine2->CPoint( 0 ), aLine2 );
370 const JOINT* jtB = m_world->FindJoint( aLine2->CLastPoint(), aLine2 );
371
372 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: LEFT branch starting from joint at (%d,%d)" ),
373 jtA->Pos().x, jtA->Pos().y );
374 PATH_RESULT left = followBranch( jtA, aLine2->Links().front(), visited, aFollowLockedSegments );
375 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: LEFT branch result: length=%d, %d items" ),
376 left.m_length, left.m_items.Size() );
377
378 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: RIGHT branch starting from joint at (%d,%d)" ),
379 jtB->Pos().x, jtB->Pos().y );
380 PATH_RESULT right = followBranch( jtB, aLine2->Links().back(), visited, aFollowLockedSegments );
381 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: RIGHT branch result: length=%d, %d items" ),
382 right.m_length, right.m_items.Size() );
383
384 if( aTerminalJointA )
385 *aTerminalJointA = left.m_end;
386
387 if( aTerminalJointB )
388 *aTerminalJointB = right.m_end;
389
390 // Count segments as we build the final path
391 int leftSegCount = 0;
392 int rightSegCount = 0;
393 int initialSegCount = 0;
394
395 // Count initial segments
396 for( int i = 0; i < aLine2->SegmentCount(); i++ )
397 initialSegCount++;
398
399 // Add left items
400 for( ITEM* item : left.m_items )
401 {
402 path.Prepend( item );
403 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
404 {
405 LINE* l = dynamic_cast<LINE*>( item );
406 if( l )
407 leftSegCount += l->SegmentCount();
408 else
409 leftSegCount++;
410 }
411 }
412
413 // Add right items
414 for( ITEM* item : right.m_items )
415 {
416 path.Add( item );
417 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
418 {
419 LINE* l = dynamic_cast<LINE*>( item );
420 if( l )
421 rightSegCount += l->SegmentCount();
422 else
423 rightSegCount++;
424 }
425 }
426
427 // Calculate total path length
428 int totalLength = left.m_length + aLine2->CLine().Length() + right.m_length;
429
430 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
431 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath SUMMARY ===" ) );
432 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Starting segment count: %d" ), initialSegCount );
433 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Left branch: %d segments, length=%d" ), leftSegCount, left.m_length );
434 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Initial line: %d segments, length=%lld" ), initialSegCount, aLine2->CLine().Length() );
435 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Right branch: %d segments, length=%d" ), rightSegCount, right.m_length );
436 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total segments in path: %d" ), leftSegCount + initialSegCount + rightSegCount );
437 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total path length: %d" ), totalLength );
438 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total items in result: %d" ), path.Size() );
439 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath END ===" ) );
440 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
441
442 return path;
443}
444
445
447 std::pair<const JOINT*, const JOINT*>* aTerminalJoints,
448 bool aFollowLockedSegments )
449{
450 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "*** AssembleTrivialPath: START ***" ) );
451 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: aStart=%p, kind=%s" ),
452 aStart, aStart->KindStr().c_str() );
453
455 LINKED_ITEM* seg = nullptr;
456
457 if( aStart->Kind() == ITEM::VIA_T )
458 {
459 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: starting from VIA" ) );
460 VIA* via = static_cast<VIA*>( aStart );
461 const JOINT* jt = m_world->FindJoint( via->Pos(), via );
462
463 if( !jt->IsNonFanoutVia() )
464 {
465 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: VIA is fanout, returning empty" ) );
466 return ITEM_SET();
467 }
468
469 ITEM_SET links( jt->CLinks() );
470
471 for( ITEM* item : links )
472 {
473 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
474 {
475 seg = static_cast<LINKED_ITEM*>( item );
476 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: found segment/arc from VIA" ) );
477 break;
478 }
479 }
480 }
481 else if( aStart->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
482 {
483 seg = static_cast<LINKED_ITEM*>( aStart );
484 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: starting from SEGMENT/ARC" ) );
485 }
486
487 if( !seg )
488 {
489 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: no segment found, returning empty" ) );
490 return ITEM_SET();
491 }
492
493 // Assemble a line following through locked segments
494 // TODO: consider if we want to allow tuning lines with different widths in the future
495 LINE l = m_world->AssembleLine( seg, nullptr, false, aFollowLockedSegments );
496
497 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: assembled line with %d segments, length=%lld" ),
498 l.SegmentCount(), l.CLine().Length() );
499
500 const JOINT* jointA = nullptr;
501 const JOINT* jointB = nullptr;
502
503 path = followTrivialPath( &l, &jointA, &jointB, aFollowLockedSegments );
504
505 if( aTerminalJoints )
506 {
507 wxASSERT( jointA && jointB );
508 *aTerminalJoints = std::make_pair( jointA, jointB );
509 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: terminal joints at (%d,%d) and (%d,%d)" ),
510 jointA->Pos().x, jointA->Pos().y, jointB->Pos().x, jointB->Pos().y );
511 }
512
513 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: returning path with %d items" ), path.Size() );
514 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "*** AssembleTrivialPath: END ***" ) );
515 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
516
517 return path;
518}
519
520
521std::vector<LINE> TOPOLOGY::findLinesFromVia( ROUTER_IFACE* aRouterIface, VIA* aVia, const std::set<ITEM*>& aVisited )
522{
523 std::vector<LINE> result;
524 NODE::OBSTACLES obstacles;
526
527 opts.m_differentNetsOnly = false;
528 opts.m_overrideClearance = 0;
530
531 m_world->QueryColliding( aVia, obstacles, opts );
532
533 NET_HANDLE net = aVia->Net();
534 std::set<LINKED_ITEM*> assembled;
535
536 const PCB_VIA* pcbVia = ( aVia->Parent() && aVia->Parent()->Type() == PCB_VIA_T )
537 ? static_cast<const PCB_VIA*>( aVia->Parent() )
538 : nullptr;
539
540
541 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "findLinesFromVia: VIA at (%d,%d), net=%p, %zu obstacles" ), aVia->Pos().x,
542 aVia->Pos().y, net, obstacles.size() );
543
544 for( const OBSTACLE& obs : obstacles )
545 {
546 if( obs.m_item->Net() != net )
547 continue;
548
549 LINKED_ITEM* linked = static_cast<LINKED_ITEM*>( obs.m_item );
550
551 if( aVisited.contains( linked ) )
552 continue;
553
554 if( assembled.contains( linked ) )
555 continue;
556
557 // Make sure at least one anchor is inside the via pad
558 VECTOR2I anchor0 = linked->Anchor( 0 );
559 VECTOR2I anchor1 = linked->Anchor( 1 );
560
561 bool anchor0Inside, anchor1Inside;
562
563 if( pcbVia )
564 {
565 PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( linked->Layer() );
566 anchor0Inside = LENGTH_DELAY_CALCULATION::IsPointInsideViaPad( pcbVia, anchor0, pcbLayer );
567 anchor1Inside = LENGTH_DELAY_CALCULATION::IsPointInsideViaPad( pcbVia, anchor1, pcbLayer );
568 }
569 else
570 {
571 // Fallback to PNS shape collision
572 const SHAPE* shape = aVia->Shape( aVia->Layer() );
573 anchor0Inside = shape && shape->Collide( anchor0, 0 );
574 anchor1Inside = shape && shape->Collide( anchor1, 0 );
575 }
576
577 if( !anchor0Inside && !anchor1Inside )
578 {
579 wxLogTrace( wxT( "PNS_TUNE" ), wxT( " skip collision: layer=%d anchor0=(%d,%d) anchor1=(%d,%d)" ),
580 linked->Layer(), anchor0.x, anchor0.y, anchor1.x, anchor1.y );
581 continue;
582 }
583
584 LINE l = m_world->AssembleLine( linked, nullptr, false, true );
585
586 for( LINKED_ITEM* link : l.Links() )
587 assembled.insert( link );
588
589 result.push_back( l );
590 }
591
592 return result;
593}
594
595
596TOPOLOGY::WALK_RESULT TOPOLOGY::walkTuningPath( ROUTER_IFACE* aRouterIface, LINE& aStartLine, bool aStartFromBack,
597 const std::set<ITEM*>& aVisited )
598{
599 using clock = std::chrono::steady_clock;
600
601 WALK_RESULT best;
602
603 NET_HANDLE net = aStartLine.Net();
604 const int timeoutMs = ADVANCED_CFG::GetCfg().m_FollowBranchTimeout;
605 auto startTime = clock::now();
606
607 struct STATE
608 {
609 VECTOR2I endpoint;
610 ITEM_SET pathItems;
611 int64_t pathLength;
612 std::set<ITEM*> visited;
613 };
614
615 std::stack<STATE> stateStack;
616
617 STATE initial;
618 initial.endpoint = aStartFromBack ? aStartLine.CLastPoint() : aStartLine.CPoint( 0 );
619 initial.pathLength = 0;
620 initial.visited = aVisited;
621 stateStack.push( std::move( initial ) );
622
623 while( !stateStack.empty() )
624 {
625 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( clock::now() - startTime ).count();
626
627 if( elapsed > timeoutMs )
628 {
629 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "walkTuningPath: timeout after %lld ms" ), elapsed );
630 break;
631 }
632
633 STATE current = std::move( stateStack.top() );
634 stateStack.pop();
635
636 ITEM_SET hits = m_world->HitTest( current.endpoint );
637
638 SOLID* pad = nullptr;
639
640 for( ITEM* item : hits )
641 {
642 if( item->OfKind( ITEM::SOLID_T ) && item->Net() == net && !current.visited.contains( item ) )
643 {
644 pad = static_cast<SOLID*>( item );
645 break;
646 }
647 }
648
649 if( pad )
650 {
651 if( current.pathLength > best.m_length )
652 {
653 best.m_length = current.pathLength;
654 best.m_items = current.pathItems;
655 best.m_endPad = pad;
656 }
657
658 // Continue through an in-line pad so tuning spans the whole net.
659 current.visited.insert( pad );
660
661 for( ITEM* item : hits )
662 {
663 if( !item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
664 continue;
665
666 if( item->Net() != net || current.visited.contains( item ) )
667 continue;
668
669 LINE contLine = m_world->AssembleLine( static_cast<LINKED_ITEM*>( item ), nullptr, false, true );
670
671 VECTOR2I ep = current.endpoint;
672 bool startNear = ( contLine.CPoint( 0 ) - ep ).SquaredEuclideanNorm()
673 <= ( contLine.CLastPoint() - ep ).SquaredEuclideanNorm();
674
675 STATE nextState;
676 nextState.endpoint = startNear ? contLine.CLastPoint() : contLine.CPoint( 0 );
677 nextState.pathItems = current.pathItems;
678 nextState.pathItems.Add( contLine );
679 nextState.pathLength = current.pathLength + contLine.CLine().Length();
680 nextState.visited = current.visited;
681
682 for( LINKED_ITEM* link : contLine.Links() )
683 nextState.visited.insert( link );
684
685 stateStack.push( std::move( nextState ) );
686 }
687
688 continue;
689 }
690
691 VIA* via = nullptr;
692
693 for( ITEM* item : hits )
694 {
695 if( item->OfKind( ITEM::VIA_T ) && item->Net() == net && !item->IsVirtual()
696 && !current.visited.contains( item ) )
697 {
698 via = static_cast<VIA*>( item );
699 break;
700 }
701 }
702
703 if( via )
704 {
705 current.visited.insert( via );
706
707 std::vector<LINE> continuations = findLinesFromVia( aRouterIface, via, current.visited );
708
709 for( LINE& contLine : continuations )
710 {
711 VECTOR2I ep = current.endpoint;
712 bool startNearVia = ( contLine.CPoint( 0 ) - ep ).SquaredEuclideanNorm()
713 <= ( contLine.CLastPoint() - ep ).SquaredEuclideanNorm();
714
715 VECTOR2I forwardEndpoint = startNearVia ? contLine.CLastPoint() : contLine.CPoint( 0 );
716
717 int64_t contLength = contLine.CLine().Length();
718
719 if( const BOARD_ITEM* parent = via->Parent(); parent && parent->Type() == PCB_VIA_T )
720 {
721 const PCB_VIA* pcbVia = static_cast<const PCB_VIA*>( parent );
722 SHAPE_LINE_CHAIN clipped = contLine.Line();
723 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( contLine.Layer() );
724
725 LENGTH_DELAY_CALCULATION::OptimiseTraceInVia( clipped, pcbVia, pcbLayer );
726 contLength = clipped.Length();
727 }
728
729 STATE nextState;
730 nextState.endpoint = forwardEndpoint;
731 nextState.pathItems = current.pathItems;
732 nextState.pathItems.Add( via );
733 nextState.pathItems.Add( contLine );
734 nextState.pathLength = current.pathLength + contLength;
735 nextState.visited = current.visited;
736
737 for( LINKED_ITEM* link : contLine.Links() )
738 nextState.visited.insert( link );
739
740 stateStack.push( std::move( nextState ) );
741 }
742
743 if( continuations.empty() )
744 {
745 if( current.pathLength > best.m_length )
746 {
747 best.m_length = current.pathLength;
748 best.m_items = current.pathItems;
749 best.m_items.Add( via );
750 best.m_endPad = nullptr;
751 }
752 }
753 }
754 else
755 {
756 if( current.pathLength > best.m_length )
757 {
758 best.m_length = current.pathLength;
759 best.m_items = current.pathItems;
760 best.m_endPad = nullptr;
761 }
762 }
763 }
764
765 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "walkTuningPath: completed, best length=%lld, %d items, pad=%p" ),
766 best.m_length, best.m_items.Size(), best.m_endPad );
767
768 return best;
769}
770
771
772const ITEM_SET TOPOLOGY::AssembleTuningPath( ROUTER_IFACE* aRouterIface, ITEM* aStart, SOLID** aStartPad,
773 SOLID** aEndPad )
774{
775 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
776 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: START ##########" ) );
777 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: aStart=%p, kind=%s" ),
778 aStart, aStart->KindStr().c_str() );
779
780 LINKED_ITEM* seg = nullptr;
781
782 if( aStart->Kind() == ITEM::VIA_T )
783 {
784 VIA* via = static_cast<VIA*>( aStart );
785
786 const JOINT* jt = m_world->FindJoint( via->Pos(), via );
787
788 if( jt && jt->IsNonFanoutVia() )
789 {
790 ITEM_SET links( jt->CLinks() );
791
792 for( ITEM* item : links )
793 {
794 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
795 {
796 seg = static_cast<LINKED_ITEM*>( item );
797 break;
798 }
799 }
800 }
801
802 if( !seg )
803 {
804 std::vector<LINE> continuations = findLinesFromVia( aRouterIface, via, {} );
805
806 if( continuations.empty() )
807 {
808 wxLogTrace( wxT( "PNS_TUNE" ),
809 wxT( "AssembleTuningPath: no via continuation found, returning empty" ) );
810 return ITEM_SET();
811 }
812
813 for( LINKED_ITEM* link : continuations.front().Links() )
814 {
815 if( link->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
816 {
817 seg = link;
818 break;
819 }
820 }
821 }
822 }
823 else if( aStart->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
824 {
825 seg = static_cast<LINKED_ITEM*>( aStart );
826 }
827
828 if( !seg )
829 {
830 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: no segment found, returning empty" ) );
831 return ITEM_SET();
832 }
833
834 LINE l = m_world->AssembleLine( seg, nullptr, false, true );
835
836 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: initial line %d segments, length=%lld" ), l.SegmentCount(),
837 l.CLine().Length() );
838
839 std::set<ITEM*> visited;
840
841 for( LINKED_ITEM* link : l.Links() )
842 visited.insert( link );
843
844 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: walking LEFT from (%d,%d)" ), l.CPoint( 0 ).x,
845 l.CPoint( 0 ).y );
846 WALK_RESULT left = walkTuningPath( aRouterIface, l, false, visited );
847
848 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: walking RIGHT from (%d,%d)" ), l.CLastPoint().x,
849 l.CLastPoint().y );
850 WALK_RESULT right = walkTuningPath( aRouterIface, l, true, visited );
851
853
854 for( ITEM* item : left.m_items )
855 path.Prepend( item );
856
857 path.Add( l );
858
859 for( ITEM* item : right.m_items )
860 path.Add( item );
861
862 PAD* padA = nullptr;
863 PAD* padB = nullptr;
864
865 if( left.m_endPad )
866 {
867 BOARD_ITEM* bi = left.m_endPad->Parent();
868
869 if( bi && bi->Type() == PCB_PAD_T )
870 {
871 padA = static_cast<PAD*>( bi );
872
873 if( aStartPad )
874 *aStartPad = left.m_endPad;
875
876 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found start pad" ) );
877 }
878 }
879
880 if( right.m_endPad )
881 {
882 BOARD_ITEM* bi = right.m_endPad->Parent();
883
884 if( bi && bi->Type() == PCB_PAD_T )
885 {
886 padB = static_cast<PAD*>( bi );
887
888 if( aEndPad )
889 *aEndPad = right.m_endPad;
890
891 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found end pad" ) );
892 }
893 }
894
895 if( !padA && !padB )
896 {
897 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: no pads found, returning path" ) );
898 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
899 return path;
900 }
901
902 auto processPad = [&]( PAD* aPad )
903 {
904 for( int idx = 0; idx < path.Size(); idx++ )
905 {
906 if( path[idx]->Kind() != ITEM::LINE_T )
907 continue;
908
909 LINE* line = static_cast<LINE*>( path[idx] );
910 SHAPE_LINE_CHAIN& slc = line->Line();
911 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
912
914 }
915 };
916
917 if( padA )
918 processPad( padA );
919
920 if( padB )
921 processPad( padB );
922
923 std::set<PAD*> processedPads;
924
925 if( padA )
926 processedPads.insert( padA );
927
928 if( padB )
929 processedPads.insert( padB );
930
931 for( int idx = 0; idx < path.Size(); idx++ )
932 {
933 if( path[idx]->Kind() != ITEM::LINE_T )
934 continue;
935
936 LINE* line = static_cast<LINE*>( path[idx] );
937
938 for( const VECTOR2I& pt : { line->CPoint( 0 ), line->CLastPoint() } )
939 {
940 ITEM_SET hits = m_world->HitTest( pt );
941
942 for( ITEM* item : hits )
943 {
944 if( item->OfKind( ITEM::SOLID_T ) && item->Net() == line->Net() )
945 {
946 SOLID* solid = static_cast<SOLID*>( item );
947 BOARD_ITEM* bi = solid->Parent();
948
949 if( bi && bi->Type() == PCB_PAD_T )
950 {
951 PAD* intermediatePad = static_cast<PAD*>( bi );
952
953 if( processedPads.find( intermediatePad ) == processedPads.end() )
954 {
955 wxLogTrace( wxT( "PNS_TUNE" ),
956 wxT( "AssembleTuningPath: processing intermediate"
957 " pad at (%d,%d)" ),
958 pt.x, pt.y );
959 processPad( intermediatePad );
960 processedPads.insert( intermediatePad );
961 }
962 }
963
964 break;
965 }
966 }
967 }
968 }
969
970 // Clip in-VIA portions and add residual path to VIA centre.
971 for( int idx = 0; idx < path.Size(); idx++ )
972 {
973 if( path[idx]->Kind() != ITEM::VIA_T )
974 continue;
975
976 VIA* pnsVia = static_cast<VIA*>( path[idx] );
977 BOARD_ITEM* parent = pnsVia->Parent();
978
979 if( !parent || parent->Type() != PCB_VIA_T )
980 continue;
981
982 const PCB_VIA* pcbVia = static_cast<const PCB_VIA*>( parent );
983
984 for( int delta : { -1, 1 } )
985 {
986 int j = idx + delta;
987
988 if( j < 0 || j >= path.Size() || path[j]->Kind() != ITEM::LINE_T )
989 continue;
990
991 LINE* line = static_cast<LINE*>( path[j] );
992 SHAPE_LINE_CHAIN& slc = line->Line();
993 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
994
995 LENGTH_DELAY_CALCULATION::OptimiseTraceInVia( slc, pcbVia, pcbLayer );
996 }
997 }
998
999 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: final path has %d items" ), path.Size() );
1000 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
1001
1002 return path;
1003}
1004
1005
1006const ITEM_SET TOPOLOGY::ConnectedItems( const JOINT* aStart, int aKindMask )
1007{
1008 return ITEM_SET();
1009}
1010
1011
1012const ITEM_SET TOPOLOGY::ConnectedItems( ITEM* aStart, int aKindMask )
1013{
1014 return ITEM_SET();
1015}
1016
1017
1018bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip );
1019
1020
1022{
1023 NET_HANDLE refNet = aStart->Net();
1024 NET_HANDLE coupledNet = m_world->GetRuleResolver()->DpCoupledNet( refNet );
1025 LINKED_ITEM* startItem = dynamic_cast<LINKED_ITEM*>( aStart );
1026
1027 if( !coupledNet || !startItem )
1028 return false;
1029
1030 LINE lp = m_world->AssembleLine( startItem );
1031
1032 std::vector<ITEM*> pItems;
1033 std::vector<ITEM*> nItems;
1034
1035 for( ITEM* item : lp.Links() )
1036 {
1037 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
1038 pItems.push_back( item );
1039 }
1040
1041 std::set<ITEM*> coupledItems;
1042 m_world->AllItemsInNet( coupledNet, coupledItems );
1043
1044 for( ITEM* item : coupledItems )
1045 {
1046 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
1047 nItems.push_back( item );
1048 }
1049
1050 LINKED_ITEM* refItem = nullptr;
1051 LINKED_ITEM* coupledItem = nullptr;
1052 SEG::ecoord minDist_sq = std::numeric_limits<SEG::ecoord>::max();
1053 SEG::ecoord minDistTarget_sq = std::numeric_limits<SEG::ecoord>::max();
1054 VECTOR2I targetPoint = aStart->Shape( -1 )->Centre();
1055
1056 auto findNItem = [&]( ITEM* p_item )
1057 {
1058 for( ITEM* n_item : nItems )
1059 {
1060 SEG::ecoord dist_sq = std::numeric_limits<SEG::ecoord>::max();
1061
1062 if( n_item->Kind() != p_item->Kind() )
1063 continue;
1064
1065 if( p_item->Kind() == ITEM::SEGMENT_T )
1066 {
1067 const SEGMENT* p_seg = static_cast<const SEGMENT*>( p_item );
1068 const SEGMENT* n_seg = static_cast<const SEGMENT*>( n_item );
1069
1070 if( n_seg->Width() != p_seg->Width() )
1071 continue;
1072
1073 if( !p_seg->Seg().ApproxParallel( n_seg->Seg(), DP_PARALLELITY_THRESHOLD ) )
1074 continue;
1075
1076 SEG p_clip, n_clip;
1077
1078 if( !commonParallelProjection( p_seg->Seg(), n_seg->Seg(), p_clip, n_clip ) )
1079 continue;
1080
1081 dist_sq = n_seg->Seg().SquaredDistance( p_seg->Seg() );
1082 }
1083 else if( p_item->Kind() == ITEM::ARC_T )
1084 {
1085 const ARC* p_arc = static_cast<const ARC*>( p_item );
1086 const ARC* n_arc = static_cast<const ARC*>( n_item );
1087
1088 if( n_arc->Width() != p_arc->Width() )
1089 continue;
1090
1091 VECTOR2I centerDiff = n_arc->CArc().GetCenter() - p_arc->CArc().GetCenter();
1092 SEG::ecoord centerDist_sq = centerDiff.SquaredEuclideanNorm();
1093
1094 if( centerDist_sq > SEG::Square( DP_PARALLELITY_THRESHOLD ) )
1095 continue;
1096
1097 dist_sq = SEG::Square( p_arc->CArc().GetRadius() - n_arc->CArc().GetRadius() );
1098 }
1099
1100 if( dist_sq <= minDist_sq )
1101 {
1102 SEG::ecoord distTarget_sq = n_item->Shape( -1 )->SquaredDistance( targetPoint );
1103 if( distTarget_sq < minDistTarget_sq )
1104 {
1105 minDistTarget_sq = distTarget_sq;
1106 minDist_sq = dist_sq;
1107
1108 refItem = static_cast<LINKED_ITEM*>( p_item );
1109 coupledItem = static_cast<LINKED_ITEM*>( n_item );
1110 }
1111 }
1112 }
1113 };
1114
1115 findNItem( startItem );
1116
1117 if( !coupledItem )
1118 {
1119 LINKED_ITEM* linked = static_cast<LINKED_ITEM*>( startItem );
1120 std::set<ITEM*> linksToTest;
1121
1122 for( int i = 0; i < linked->AnchorCount(); i++ )
1123 {
1124 const JOINT* jt = m_world->FindJoint( linked->Anchor( i ), linked );
1125
1126 if( !jt )
1127 continue;
1128
1129 for( ITEM* link : jt->LinkList() )
1130 {
1131 if( link != linked )
1132 linksToTest.emplace( link );
1133 }
1134 }
1135
1136 for( ITEM* link : linksToTest )
1137 findNItem( link );
1138 }
1139
1140 if( !coupledItem )
1141 return false;
1142
1143 LINE ln = m_world->AssembleLine( coupledItem );
1144
1145 if( m_world->GetRuleResolver()->DpNetPolarity( refNet ) < 0 )
1146 std::swap( lp, ln );
1147
1148 int gap = -1;
1149
1150 if( refItem && refItem->Kind() == ITEM::SEGMENT_T )
1151 {
1152 // Segments are parallel -> compute pair gap
1153 const VECTOR2I refDir = refItem->Anchor( 1 ) - refItem->Anchor( 0 );
1154 const VECTOR2I displacement = refItem->Anchor( 1 ) - coupledItem->Anchor( 1 );
1155 gap = (int) std::abs( refDir.Cross( displacement ) / refDir.EuclideanNorm() ) - lp.Width();
1156 }
1157 else if( refItem && refItem->Kind() == ITEM::ARC_T )
1158 {
1159 const ARC* refArc = static_cast<ARC*>( refItem );
1160 const ARC* coupledArc = static_cast<ARC*>( coupledItem );
1161 gap = (int) std::abs( refArc->CArc().GetRadius() - coupledArc->CArc().GetRadius() ) - lp.Width();
1162 }
1163
1164 aPair = DIFF_PAIR( lp, ln );
1165 aPair.SetWidth( lp.Width() );
1166 aPair.SetLayers( lp.Layers() );
1167 aPair.SetGap( gap );
1168
1169 return true;
1170}
1171
1172const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, double aAreaExpansionLimit, NET_HANDLE aExcludedNet )
1173{
1174 CLUSTER cluster;
1175 std::deque<ITEM*> pending;
1176
1178
1179 opts.m_differentNetsOnly = false;
1180 opts.m_overrideClearance = 0;
1181
1182 pending.push_back( aStart );
1183
1184 BOX2I clusterBBox = aStart->Shape( aLayer )->BBox();
1185 int64_t initialArea = clusterBBox.GetArea();
1186 std::unordered_set<ITEM*> processed;
1187
1188 while( !pending.empty() )
1189 {
1190 NODE::OBSTACLES obstacles;
1191 ITEM* top = pending.front();
1192
1193 pending.pop_front();
1194
1195 if( processed.find( top ) == processed.end() )
1196 {
1197 cluster.m_items.push_back( top );
1198 }
1199
1200 processed.insert( top );
1201
1202 m_world->QueryColliding( top, obstacles, opts ); // only query touching objects
1203
1204 for( const OBSTACLE& obs : obstacles )
1205 {
1206 bool trackOnTrack = ( obs.m_item->Net() != top->Net() ) && obs.m_item->OfKind( ITEM::SEGMENT_T ) && top->OfKind( ITEM::SEGMENT_T );
1207
1208 if( trackOnTrack )
1209 continue;
1210
1211 if( aExcludedNet && obs.m_item->Net() == aExcludedNet )
1212 continue;
1213
1214 if( obs.m_item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && obs.m_item->Layers().Overlaps( aLayer ) )
1215 {
1216 auto line = m_world->AssembleLine( static_cast<LINKED_ITEM*>(obs.m_item) );
1217 clusterBBox.Merge( line.CLine().BBox() );
1218 }
1219 else
1220 {
1221 clusterBBox.Merge( obs.m_item->Shape( aLayer )->BBox() );
1222 }
1223
1224 const int64_t currentArea = clusterBBox.GetArea();
1225 const double areaRatio = (double) currentArea / (double) ( initialArea + 1 );
1226
1227 if( aAreaExpansionLimit > 0.0 && areaRatio > aAreaExpansionLimit )
1228 break;
1229
1230 if( processed.find( obs.m_item ) == processed.end() &&
1231 obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
1232 {
1233 processed.insert( obs.m_item );
1234 cluster.m_items.push_back( obs.m_item );
1235 pending.push_back( obs.m_item );
1236 }
1237 }
1238 }
1239
1240 return cluster;
1241}
1242
1243}
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr ecoord_type GetArea() const
Return the area of the rectangle.
Definition box2.h:757
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
static void OptimiseTraceInVia(SHAPE_LINE_CHAIN &aLine, const PCB_VIA *aVia, PCB_LAYER_ID aLayer)
Clips trace portions inside a VIA pad and replaces them with a straight-line segment from the VIA edg...
static bool IsPointInsideViaPad(const PCB_VIA *aVia, const VECTOR2I &aPoint, PCB_LAYER_ID aLayer)
Returns true if the given point falls inside VIA pad shape on the given layer.
static void OptimiseTraceInPad(SHAPE_LINE_CHAIN &aLine, const PAD *aPad, PCB_LAYER_ID aPcbLayer)
Optimises the given trace / line to minimise the electrical path length within the given pad.
Definition pad.h:61
int Width() const override
Definition pns_arc.h:88
const SHAPE_ARC & CArc() const
Definition pns_arc.h:116
Basic class for a differential pair.
void SetGap(int aGap)
void SetWidth(int aWidth)
int Size() const
void Add(const LINE &aLine)
Base class for PNS router board items.
Definition pns_item.h:98
BOARD_ITEM * Parent() const
Definition pns_item.h:199
void SetLayers(const PNS_LAYER_RANGE &aLayers)
Definition pns_item.h:213
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition pns_item.h:242
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
virtual int Layer() const
Definition pns_item.h:216
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:268
std::string KindStr() const
Definition pns_item.cpp:315
virtual int AnchorCount() const
Definition pns_item.h:273
A 2D point on a given set of layers and belonging to a certain net, that links together a number of b...
Definition pns_joint.h:43
const std::vector< ITEM * > & LinkList() const
Definition pns_joint.h:303
NET_HANDLE Net() const override
Definition pns_joint.h:298
int LinkCount(int aMask=-1) const
Definition pns_joint.h:318
bool IsNonFanoutVia() const
Definition pns_joint.h:149
const ITEM_SET & CLinks() const
Definition pns_joint.h:308
const VECTOR2I & Pos() const
Definition pns_joint.h:293
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:150
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition pns_line.h:131
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:142
const VECTOR2I & CLastPoint() const
Definition pns_line.h:151
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
int SegmentCount() const
Definition pns_line.h:144
int PointCount() const
Definition pns_line.h:145
bool EndsWithVia() const
Definition pns_line.h:195
void Reverse()
Clip the line to the nearest obstacle, traversing from the line's start vertex (0).
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition pns_line.h:162
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:254
virtual PCB_LAYER_ID GetBoardLayerFromPNSLayer(int aLayer) const =0
const SEG & Seg() const
int Width() const override
Definition pns_segment.h:96
ITEM * NearestUnconnectedItem(const JOINT *aStart, int *aAnchor=nullptr, int aKindMask=ITEM::ANY_T)
std::set< const JOINT * > JOINT_SET
bool LeadingRatLine(const LINE *aTrack, SHAPE_LINE_CHAIN &aRatLine)
std::vector< LINE > findLinesFromVia(ROUTER_IFACE *aRouterIface, VIA *aVia, const std::set< ITEM * > &aVisited)
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
WALK_RESULT walkTuningPath(ROUTER_IFACE *aRouterIface, LINE &aStartLine, bool aStartFromBack, const std::set< ITEM * > &aVisited)
ITEM_SET followTrivialPath(LINE *aLine, const JOINT **aTerminalJointA, const JOINT **aTerminalJointB, bool aFollowLockedSegments=false)
const ITEM_SET ConnectedItems(const JOINT *aStart, int aKindMask=ITEM::ANY_T)
bool NearestUnconnectedAnchorPoint(const LINE *aTrack, VECTOR2I &aPoint, PNS_LAYER_RANGE &aLayers, ITEM *&aItem)
const CLUSTER AssembleCluster(ITEM *aStart, int aLayer, double aAreaExpansionLimit=0.0, NET_HANDLE aExcludedNet=nullptr)
const JOINT_SET ConnectedJoints(const JOINT *aStart)
const ITEM_SET AssembleTuningPath(ROUTER_IFACE *aRouterIface, ITEM *aStart, SOLID **aStartPad=nullptr, SOLID **aEndPad=nullptr)
Like AssembleTrivialPath, but follows the track length algorithm, which discards segments that are fu...
const int DP_PARALLELITY_THRESHOLD
TOPOLOGY(NODE *aNode)
PATH_RESULT followBranch(const JOINT *aStartJoint, LINKED_ITEM *aPrev, std::set< ITEM * > &aVisited, bool aFollowLockedSegments)
const ITEM_SET AssembleTrivialPath(ITEM *aStart, std::pair< const JOINT *, const JOINT * > *aTerminalJoints=nullptr, bool aFollowLockedSegments=false)
Assemble a trivial path between two joints given a starting item.
bool SimplifyLine(LINE *aLine)
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
Represent a contiguous set of PCB layers.
Definition seg.h:38
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:76
VECTOR2I::extended_type ecoord
Definition seg.h:40
static SEG::ecoord Square(int a)
Definition seg.h:119
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:803
double GetRadius() const
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
void Clear()
Remove all points from the line chain.
void Simplify(int aTolerance=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
long long int Length() const
Return length of the line chain in Euclidean metric.
An abstract shape on 2D plane.
Definition shape.h:124
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:179
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Definition shape.h:230
virtual const BOX2I BBox(int aClearance=0) const =0
Compute a bounding box of the shape, with a margin of aClearance a collision.
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:534
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
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
int m_FollowBranchTimeout
Timeout for the PNS router's followBranch path search, in milliseconds.
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
Push and Shove diff pair dimensions (gap) settings dialog.
bool commonParallelProjection(SEG p, SEG n, SEG &pClip, SEG &nClip)
void * NET_HANDLE
Definition pns_item.h:55
@ MK_HEAD
Definition pns_item.h:43
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ DIFF_PAIR
CITER next(CITER it)
Definition ptree.cpp:120
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
std::vector< ITEM * > m_items
std::string path
KIBIS top(path, &reporter)
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
int delta
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683