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