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 along
19 * with this program. If not, see <http://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;
659 }
660
661 VIA* via = nullptr;
662
663 for( ITEM* item : hits )
664 {
665 if( item->OfKind( ITEM::VIA_T ) && item->Net() == net && !item->IsVirtual()
666 && !current.visited.contains( item ) )
667 {
668 via = static_cast<VIA*>( item );
669 break;
670 }
671 }
672
673 if( via )
674 {
675 current.visited.insert( via );
676
677 std::vector<LINE> continuations = findLinesFromVia( aRouterIface, via, current.visited );
678
679 for( LINE& contLine : continuations )
680 {
681 VECTOR2I ep = current.endpoint;
682 bool startNearVia = ( contLine.CPoint( 0 ) - ep ).SquaredEuclideanNorm()
683 <= ( contLine.CLastPoint() - ep ).SquaredEuclideanNorm();
684
685 VECTOR2I forwardEndpoint = startNearVia ? contLine.CLastPoint() : contLine.CPoint( 0 );
686
687 int64_t contLength = contLine.CLine().Length();
688
689 if( const BOARD_ITEM* parent = via->Parent(); parent && parent->Type() == PCB_VIA_T )
690 {
691 const PCB_VIA* pcbVia = static_cast<const PCB_VIA*>( parent );
692 SHAPE_LINE_CHAIN clipped = contLine.Line();
693 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( contLine.Layer() );
694
695 LENGTH_DELAY_CALCULATION::OptimiseTraceInVia( clipped, pcbVia, pcbLayer );
696 contLength = clipped.Length();
697 }
698
699 STATE nextState;
700 nextState.endpoint = forwardEndpoint;
701 nextState.pathItems = current.pathItems;
702 nextState.pathItems.Add( via );
703 nextState.pathItems.Add( contLine );
704 nextState.pathLength = current.pathLength + contLength;
705 nextState.visited = current.visited;
706
707 for( LINKED_ITEM* link : contLine.Links() )
708 nextState.visited.insert( link );
709
710 stateStack.push( std::move( nextState ) );
711 }
712
713 if( continuations.empty() )
714 {
715 if( current.pathLength > best.m_length )
716 {
717 best.m_length = current.pathLength;
718 best.m_items = current.pathItems;
719 best.m_items.Add( via );
720 best.m_endPad = nullptr;
721 }
722 }
723 }
724 else
725 {
726 if( current.pathLength > best.m_length )
727 {
728 best.m_length = current.pathLength;
729 best.m_items = current.pathItems;
730 best.m_endPad = nullptr;
731 }
732 }
733 }
734
735 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "walkTuningPath: completed, best length=%lld, %d items, pad=%p" ),
736 best.m_length, best.m_items.Size(), best.m_endPad );
737
738 return best;
739}
740
741
742const ITEM_SET TOPOLOGY::AssembleTuningPath( ROUTER_IFACE* aRouterIface, ITEM* aStart, SOLID** aStartPad,
743 SOLID** aEndPad )
744{
745 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
746 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: START ##########" ) );
747 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: aStart=%p, kind=%s" ),
748 aStart, aStart->KindStr().c_str() );
749
750 LINKED_ITEM* seg = nullptr;
751
752 if( aStart->Kind() == ITEM::VIA_T )
753 {
754 VIA* via = static_cast<VIA*>( aStart );
755
756 const JOINT* jt = m_world->FindJoint( via->Pos(), via );
757
758 if( jt && jt->IsNonFanoutVia() )
759 {
760 ITEM_SET links( jt->CLinks() );
761
762 for( ITEM* item : links )
763 {
764 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
765 {
766 seg = static_cast<LINKED_ITEM*>( item );
767 break;
768 }
769 }
770 }
771
772 if( !seg )
773 {
774 std::vector<LINE> continuations = findLinesFromVia( aRouterIface, via, {} );
775
776 if( continuations.empty() )
777 {
778 wxLogTrace( wxT( "PNS_TUNE" ),
779 wxT( "AssembleTuningPath: no via continuation found, returning empty" ) );
780 return ITEM_SET();
781 }
782
783 for( LINKED_ITEM* link : continuations.front().Links() )
784 {
785 if( link->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
786 {
787 seg = link;
788 break;
789 }
790 }
791 }
792 }
793 else if( aStart->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
794 {
795 seg = static_cast<LINKED_ITEM*>( aStart );
796 }
797
798 if( !seg )
799 {
800 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: no segment found, returning empty" ) );
801 return ITEM_SET();
802 }
803
804 LINE l = m_world->AssembleLine( seg, nullptr, false, true );
805
806 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: initial line %d segments, length=%lld" ), l.SegmentCount(),
807 l.CLine().Length() );
808
809 std::set<ITEM*> visited;
810
811 for( LINKED_ITEM* link : l.Links() )
812 visited.insert( link );
813
814 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: walking LEFT from (%d,%d)" ), l.CPoint( 0 ).x,
815 l.CPoint( 0 ).y );
816 WALK_RESULT left = walkTuningPath( aRouterIface, l, false, visited );
817
818 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: walking RIGHT from (%d,%d)" ), l.CLastPoint().x,
819 l.CLastPoint().y );
820 WALK_RESULT right = walkTuningPath( aRouterIface, l, true, visited );
821
823
824 for( ITEM* item : left.m_items )
825 path.Prepend( item );
826
827 path.Add( l );
828
829 for( ITEM* item : right.m_items )
830 path.Add( item );
831
832 PAD* padA = nullptr;
833 PAD* padB = nullptr;
834
835 if( left.m_endPad )
836 {
837 BOARD_ITEM* bi = left.m_endPad->Parent();
838
839 if( bi && bi->Type() == PCB_PAD_T )
840 {
841 padA = static_cast<PAD*>( bi );
842
843 if( aStartPad )
844 *aStartPad = left.m_endPad;
845
846 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found start pad" ) );
847 }
848 }
849
850 if( right.m_endPad )
851 {
852 BOARD_ITEM* bi = right.m_endPad->Parent();
853
854 if( bi && bi->Type() == PCB_PAD_T )
855 {
856 padB = static_cast<PAD*>( bi );
857
858 if( aEndPad )
859 *aEndPad = right.m_endPad;
860
861 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found end pad" ) );
862 }
863 }
864
865 if( !padA && !padB )
866 {
867 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: no pads found, returning path" ) );
868 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
869 return path;
870 }
871
872 auto processPad = [&]( PAD* aPad )
873 {
874 for( int idx = 0; idx < path.Size(); idx++ )
875 {
876 if( path[idx]->Kind() != ITEM::LINE_T )
877 continue;
878
879 LINE* line = static_cast<LINE*>( path[idx] );
880 SHAPE_LINE_CHAIN& slc = line->Line();
881 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
882
884 }
885 };
886
887 if( padA )
888 processPad( padA );
889
890 if( padB )
891 processPad( padB );
892
893 std::set<PAD*> processedPads;
894
895 if( padA )
896 processedPads.insert( padA );
897
898 if( padB )
899 processedPads.insert( padB );
900
901 for( int idx = 0; idx < path.Size(); idx++ )
902 {
903 if( path[idx]->Kind() != ITEM::LINE_T )
904 continue;
905
906 LINE* line = static_cast<LINE*>( path[idx] );
907
908 for( const VECTOR2I& pt : { line->CPoint( 0 ), line->CLastPoint() } )
909 {
910 ITEM_SET hits = m_world->HitTest( pt );
911
912 for( ITEM* item : hits )
913 {
914 if( item->OfKind( ITEM::SOLID_T ) && item->Net() == line->Net() )
915 {
916 SOLID* solid = static_cast<SOLID*>( item );
917 BOARD_ITEM* bi = solid->Parent();
918
919 if( bi && bi->Type() == PCB_PAD_T )
920 {
921 PAD* intermediatePad = static_cast<PAD*>( bi );
922
923 if( processedPads.find( intermediatePad ) == processedPads.end() )
924 {
925 wxLogTrace( wxT( "PNS_TUNE" ),
926 wxT( "AssembleTuningPath: processing intermediate"
927 " pad at (%d,%d)" ),
928 pt.x, pt.y );
929 processPad( intermediatePad );
930 processedPads.insert( intermediatePad );
931 }
932 }
933
934 break;
935 }
936 }
937 }
938 }
939
940 // Clip in-VIA portions and add residual path to VIA centre.
941 for( int idx = 0; idx < path.Size(); idx++ )
942 {
943 if( path[idx]->Kind() != ITEM::VIA_T )
944 continue;
945
946 VIA* pnsVia = static_cast<VIA*>( path[idx] );
947 BOARD_ITEM* parent = pnsVia->Parent();
948
949 if( !parent || parent->Type() != PCB_VIA_T )
950 continue;
951
952 const PCB_VIA* pcbVia = static_cast<const PCB_VIA*>( parent );
953
954 for( int delta : { -1, 1 } )
955 {
956 int j = idx + delta;
957
958 if( j < 0 || j >= path.Size() || path[j]->Kind() != ITEM::LINE_T )
959 continue;
960
961 LINE* line = static_cast<LINE*>( path[j] );
962 SHAPE_LINE_CHAIN& slc = line->Line();
963 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
964
965 LENGTH_DELAY_CALCULATION::OptimiseTraceInVia( slc, pcbVia, pcbLayer );
966 }
967 }
968
969 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: final path has %d items" ), path.Size() );
970 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
971
972 return path;
973}
974
975
976const ITEM_SET TOPOLOGY::ConnectedItems( const JOINT* aStart, int aKindMask )
977{
978 return ITEM_SET();
979}
980
981
982const ITEM_SET TOPOLOGY::ConnectedItems( ITEM* aStart, int aKindMask )
983{
984 return ITEM_SET();
985}
986
987
988bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip );
989
990
992{
993 NET_HANDLE refNet = aStart->Net();
994 NET_HANDLE coupledNet = m_world->GetRuleResolver()->DpCoupledNet( refNet );
995 LINKED_ITEM* startItem = dynamic_cast<LINKED_ITEM*>( aStart );
996
997 if( !coupledNet || !startItem )
998 return false;
999
1000 LINE lp = m_world->AssembleLine( startItem );
1001
1002 std::vector<ITEM*> pItems;
1003 std::vector<ITEM*> nItems;
1004
1005 for( ITEM* item : lp.Links() )
1006 {
1007 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
1008 pItems.push_back( item );
1009 }
1010
1011 std::set<ITEM*> coupledItems;
1012 m_world->AllItemsInNet( coupledNet, coupledItems );
1013
1014 for( ITEM* item : coupledItems )
1015 {
1016 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
1017 nItems.push_back( item );
1018 }
1019
1020 LINKED_ITEM* refItem = nullptr;
1021 LINKED_ITEM* coupledItem = nullptr;
1022 SEG::ecoord minDist_sq = std::numeric_limits<SEG::ecoord>::max();
1023 SEG::ecoord minDistTarget_sq = std::numeric_limits<SEG::ecoord>::max();
1024 VECTOR2I targetPoint = aStart->Shape( -1 )->Centre();
1025
1026 auto findNItem = [&]( ITEM* p_item )
1027 {
1028 for( ITEM* n_item : nItems )
1029 {
1030 SEG::ecoord dist_sq = std::numeric_limits<SEG::ecoord>::max();
1031
1032 if( n_item->Kind() != p_item->Kind() )
1033 continue;
1034
1035 if( p_item->Kind() == ITEM::SEGMENT_T )
1036 {
1037 const SEGMENT* p_seg = static_cast<const SEGMENT*>( p_item );
1038 const SEGMENT* n_seg = static_cast<const SEGMENT*>( n_item );
1039
1040 if( n_seg->Width() != p_seg->Width() )
1041 continue;
1042
1043 if( !p_seg->Seg().ApproxParallel( n_seg->Seg(), DP_PARALLELITY_THRESHOLD ) )
1044 continue;
1045
1046 SEG p_clip, n_clip;
1047
1048 if( !commonParallelProjection( p_seg->Seg(), n_seg->Seg(), p_clip, n_clip ) )
1049 continue;
1050
1051 dist_sq = n_seg->Seg().SquaredDistance( p_seg->Seg() );
1052 }
1053 else if( p_item->Kind() == ITEM::ARC_T )
1054 {
1055 const ARC* p_arc = static_cast<const ARC*>( p_item );
1056 const ARC* n_arc = static_cast<const ARC*>( n_item );
1057
1058 if( n_arc->Width() != p_arc->Width() )
1059 continue;
1060
1061 VECTOR2I centerDiff = n_arc->CArc().GetCenter() - p_arc->CArc().GetCenter();
1062 SEG::ecoord centerDist_sq = centerDiff.SquaredEuclideanNorm();
1063
1064 if( centerDist_sq > SEG::Square( DP_PARALLELITY_THRESHOLD ) )
1065 continue;
1066
1067 dist_sq = SEG::Square( p_arc->CArc().GetRadius() - n_arc->CArc().GetRadius() );
1068 }
1069
1070 if( dist_sq <= minDist_sq )
1071 {
1072 SEG::ecoord distTarget_sq = n_item->Shape( -1 )->SquaredDistance( targetPoint );
1073 if( distTarget_sq < minDistTarget_sq )
1074 {
1075 minDistTarget_sq = distTarget_sq;
1076 minDist_sq = dist_sq;
1077
1078 refItem = static_cast<LINKED_ITEM*>( p_item );
1079 coupledItem = static_cast<LINKED_ITEM*>( n_item );
1080 }
1081 }
1082 }
1083 };
1084
1085 findNItem( startItem );
1086
1087 if( !coupledItem )
1088 {
1089 LINKED_ITEM* linked = static_cast<LINKED_ITEM*>( startItem );
1090 std::set<ITEM*> linksToTest;
1091
1092 for( int i = 0; i < linked->AnchorCount(); i++ )
1093 {
1094 const JOINT* jt = m_world->FindJoint( linked->Anchor( i ), linked );
1095
1096 if( !jt )
1097 continue;
1098
1099 for( ITEM* link : jt->LinkList() )
1100 {
1101 if( link != linked )
1102 linksToTest.emplace( link );
1103 }
1104 }
1105
1106 for( ITEM* link : linksToTest )
1107 findNItem( link );
1108 }
1109
1110 if( !coupledItem )
1111 return false;
1112
1113 LINE ln = m_world->AssembleLine( coupledItem );
1114
1115 if( m_world->GetRuleResolver()->DpNetPolarity( refNet ) < 0 )
1116 std::swap( lp, ln );
1117
1118 int gap = -1;
1119
1120 if( refItem && refItem->Kind() == ITEM::SEGMENT_T )
1121 {
1122 // Segments are parallel -> compute pair gap
1123 const VECTOR2I refDir = refItem->Anchor( 1 ) - refItem->Anchor( 0 );
1124 const VECTOR2I displacement = refItem->Anchor( 1 ) - coupledItem->Anchor( 1 );
1125 gap = (int) std::abs( refDir.Cross( displacement ) / refDir.EuclideanNorm() ) - lp.Width();
1126 }
1127 else if( refItem && refItem->Kind() == ITEM::ARC_T )
1128 {
1129 const ARC* refArc = static_cast<ARC*>( refItem );
1130 const ARC* coupledArc = static_cast<ARC*>( coupledItem );
1131 gap = (int) std::abs( refArc->CArc().GetRadius() - coupledArc->CArc().GetRadius() ) - lp.Width();
1132 }
1133
1134 aPair = DIFF_PAIR( lp, ln );
1135 aPair.SetWidth( lp.Width() );
1136 aPair.SetLayers( lp.Layers() );
1137 aPair.SetGap( gap );
1138
1139 return true;
1140}
1141
1142const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, double aAreaExpansionLimit, NET_HANDLE aExcludedNet )
1143{
1144 CLUSTER cluster;
1145 std::deque<ITEM*> pending;
1146
1148
1149 opts.m_differentNetsOnly = false;
1150 opts.m_overrideClearance = 0;
1151
1152 pending.push_back( aStart );
1153
1154 BOX2I clusterBBox = aStart->Shape( aLayer )->BBox();
1155 int64_t initialArea = clusterBBox.GetArea();
1156 std::unordered_set<ITEM*> processed;
1157
1158 while( !pending.empty() )
1159 {
1160 NODE::OBSTACLES obstacles;
1161 ITEM* top = pending.front();
1162
1163 pending.pop_front();
1164
1165 if( processed.find( top ) == processed.end() )
1166 {
1167 cluster.m_items.push_back( top );
1168 }
1169
1170 processed.insert( top );
1171
1172 m_world->QueryColliding( top, obstacles, opts ); // only query touching objects
1173
1174 for( const OBSTACLE& obs : obstacles )
1175 {
1176 bool trackOnTrack = ( obs.m_item->Net() != top->Net() ) && obs.m_item->OfKind( ITEM::SEGMENT_T ) && top->OfKind( ITEM::SEGMENT_T );
1177
1178 if( trackOnTrack )
1179 continue;
1180
1181 if( aExcludedNet && obs.m_item->Net() == aExcludedNet )
1182 continue;
1183
1184 if( obs.m_item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && obs.m_item->Layers().Overlaps( aLayer ) )
1185 {
1186 auto line = m_world->AssembleLine( static_cast<LINKED_ITEM*>(obs.m_item) );
1187 clusterBBox.Merge( line.CLine().BBox() );
1188 }
1189 else
1190 {
1191 clusterBBox.Merge( obs.m_item->Shape( aLayer )->BBox() );
1192 }
1193
1194 const int64_t currentArea = clusterBBox.GetArea();
1195 const double areaRatio = (double) currentArea / (double) ( initialArea + 1 );
1196
1197 if( aAreaExpansionLimit > 0.0 && areaRatio > aAreaExpansionLimit )
1198 break;
1199
1200 if( processed.find( obs.m_item ) == processed.end() &&
1201 obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
1202 {
1203 processed.insert( obs.m_item );
1204 cluster.m_items.push_back( obs.m_item );
1205 pending.push_back( obs.m_item );
1206 }
1207 }
1208 }
1209
1210 return cluster;
1211}
1212
1213}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
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:84
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr ecoord_type GetArea() const
Return the area of the rectangle.
Definition box2.h:761
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
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:55
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:304
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:252
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:42
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
static SEG::ecoord Square(int a)
Definition seg.h:123
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:807
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:126
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:181
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Definition shape.h:232
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:538
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:307
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
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:60
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:124
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:88
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:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687