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 <board.h>
43#include <pad.h>
44
45namespace PNS {
46
48{
49 if( !aLine->IsLinked() || !aLine->SegmentCount() )
50 return false;
51
52 LINKED_ITEM* root = aLine->GetLink( 0 );
53 LINE l = m_world->AssembleLine( root, nullptr, false, false, false );
54 SHAPE_LINE_CHAIN simplified( l.CLine() );
55
56 simplified.Simplify();
57
58 if( simplified.PointCount() != l.PointCount() )
59 {
60 m_world->Remove( l );
61 LINE lnew( l );
62 lnew.SetShape( simplified );
63 m_world->Add( lnew );
64 return true;
65 }
66
67 return false;
68}
69
70
72{
73 std::deque<const JOINT*> searchQueue;
74 JOINT_SET processed;
75
76 searchQueue.push_back( aStart );
77 processed.insert( aStart );
78
79 while( !searchQueue.empty() )
80 {
81 const JOINT* current = searchQueue.front();
82 searchQueue.pop_front();
83
84 for( ITEM* item : current->LinkList() )
85 {
86 if( item->OfKind( ITEM::SEGMENT_T ) )
87 {
88 const JOINT* a = m_world->FindJoint( item->Anchor( 0 ), item );;
89 const JOINT* b = m_world->FindJoint( item->Anchor( 1 ), item );;
90 const JOINT* next = ( *a == *current ) ? b : a;
91
92 if( processed.find( next ) == processed.end() )
93 {
94 processed.insert( next );
95 searchQueue.push_back( next );
96 }
97 }
98 }
99 }
100
101 return processed;
102}
103
104
106 PNS_LAYER_RANGE& aLayers, ITEM*& aItem )
107{
108 LINE track( *aTrack );
110
111 if( !track.PointCount() )
112 return false;
113
114 std::unique_ptr<NODE> tmpNode( m_world->Branch() );
115
116 track.ClearLinks();
117 tmpNode->Add( track );
118
119 const JOINT* jt = tmpNode->FindJoint( track.CLastPoint(), &track );
120
121 if( !jt || m_world->GetRuleResolver()->NetCode( jt->Net() ) <= 0 )
122 return false;
123
124 if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 )
125 || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
126 {
127 end = jt->Pos();
128 aLayers = jt->Layers();
129 aItem = jt->LinkList()[0];
130 }
131 else
132 {
133 int anchor;
134
135 TOPOLOGY topo( tmpNode.get() );
136 ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
137
138 if( !it )
139 return false;
140
141 end = it->Anchor( anchor );
142 aLayers = it->Layers();
143 aItem = it;
144 }
145
146 aPoint = end;
147 return true;
148}
149
150
151bool TOPOLOGY::LeadingRatLine( const LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
152{
154 // Ratline doesn't care about the layer
155 PNS_LAYER_RANGE layers;
156 ITEM* unusedItem;
157
158 if( !NearestUnconnectedAnchorPoint( aTrack, end, layers, unusedItem ) )
159 return false;
160
161 aRatLine.Clear();
162 aRatLine.Append( aTrack->CLastPoint() );
163 aRatLine.Append( end );
164 return true;
165}
166
167
168ITEM* TOPOLOGY::NearestUnconnectedItem( const JOINT* aStart, int* aAnchor, int aKindMask )
169{
170 std::set<ITEM*> disconnected;
171
172 m_world->AllItemsInNet( aStart->Net(), disconnected );
173
174 for( const JOINT* jt : ConnectedJoints( aStart ) )
175 {
176 for( ITEM* link : jt->LinkList() )
177 {
178 if( disconnected.find( link ) != disconnected.end() )
179 disconnected.erase( link );
180 }
181 }
182
183 int best_dist = INT_MAX;
184 ITEM* best = nullptr;
185
186 for( ITEM* item : disconnected )
187 {
188 if( item->OfKind( aKindMask ) )
189 {
190 for( int i = 0; i < item->AnchorCount(); i++ )
191 {
192 VECTOR2I p = item->Anchor( i );
193 int d = ( p - aStart->Pos() ).EuclideanNorm();
194
195 if( d < best_dist )
196 {
197 best_dist = d;
198 best = item;
199
200 if( aAnchor )
201 *aAnchor = i;
202 }
203 }
204 }
205 }
206
207 return best;
208}
209
210
212 std::set<ITEM*>& aVisited,
213 bool aFollowLockedSegments )
214{
215 using clock = std::chrono::steady_clock;
216
217 PATH_RESULT best;
218 best.m_end = aStartJoint;
219
220 const int timeoutMs = ADVANCED_CFG::GetCfg().m_FollowBranchTimeout;
221 auto startTime = clock::now();
222
223 // State for iterative DFS: current joint, previous item, accumulated path items,
224 // accumulated length, and the set of visited joints for this path
225 struct STATE
226 {
227 const JOINT* joint;
228 LINKED_ITEM* prev;
229 ITEM_SET pathItems;
230 int pathLength;
231 std::set<const JOINT*> visitedJoints;
232 ITEM* via;
233 };
234
235 std::stack<STATE> stateStack;
236
237 // Initialize with starting state
238 STATE initial;
239 initial.joint = aStartJoint;
240 initial.prev = aPrev;
241 initial.pathLength = 0;
242 initial.visitedJoints.insert( aStartJoint );
243 initial.via = nullptr;
244
245 stateStack.push( std::move( initial ) );
246
247 while( !stateStack.empty() )
248 {
249 // Check timeout
250 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
251 clock::now() - startTime ).count();
252
253 if( elapsed > timeoutMs )
254 {
255 wxLogTrace( wxT( "PNS_TUNE" ),
256 wxT( "followBranch: timeout after %lld ms, returning best path found" ),
257 elapsed );
258 break;
259 }
260
261 STATE current = std::move( stateStack.top() );
262 stateStack.pop();
263
264 const JOINT* joint = current.joint;
265 ITEM_SET links( joint->CLinks() );
266
267 // Check for via at this joint
268 ITEM* via = nullptr;
269
270 for( ITEM* link : links )
271 {
272 if( link->OfKind( ITEM::VIA_T ) && !aVisited.contains( link ) )
273 {
274 via = link;
275 break;
276 }
277 }
278
279 // Find all unvisited branches from this joint
280 bool foundBranch = false;
281
282 for( ITEM* link : links )
283 {
284 if( !link->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
285 continue;
286
287 if( link == current.prev )
288 continue;
289
290 if( aVisited.contains( link ) )
291 continue;
292
293 LINE l = m_world->AssembleLine( static_cast<LINKED_ITEM*>( link ), nullptr,
294 false, aFollowLockedSegments );
295
296 if( l.CPoint( 0 ) != joint->Pos() )
297 l.Reverse();
298
299 const JOINT* nextJoint = m_world->FindJoint( l.CLastPoint(), &l );
300
301 // Skip if we've already visited this joint in the current path
302 if( current.visitedJoints.count( nextJoint ) )
303 continue;
304
305 foundBranch = true;
306
307 // Build new state for this branch
308 STATE nextState;
309 nextState.joint = nextJoint;
310 nextState.prev = l.Links().back();
311 nextState.pathItems = current.pathItems;
312 nextState.pathLength = current.pathLength + l.CLine().Length();
313 nextState.visitedJoints = current.visitedJoints;
314 nextState.visitedJoints.insert( nextJoint );
315 nextState.via = via;
316
317 // Add via and line to path
318 if( via )
319 nextState.pathItems.Add( via );
320
321 nextState.pathItems.Add( l );
322
323 stateStack.push( std::move( nextState ) );
324 }
325
326 // If no branches found, this is a terminal joint - check if it's the best path
327 if( !foundBranch )
328 {
329 if( current.pathLength > best.m_length )
330 {
331 best.m_length = current.pathLength;
332 best.m_end = joint;
333 best.m_items = current.pathItems;
334 }
335 }
336 }
337
338 wxLogTrace( wxT( "PNS_TUNE" ),
339 wxT( "followBranch: completed with best path length=%d, %d items" ),
340 best.m_length, best.m_items.Size() );
341
342 return best;
343}
344
345
346ITEM_SET TOPOLOGY::followTrivialPath( LINE* aLine2, const JOINT** aTerminalJointA,
347 const JOINT** aTerminalJointB,
348 bool aFollowLockedSegments )
349{
350 assert( aLine2->IsLinked() );
351
352 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath START ===" ) );
353 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: initial line has %d segments, %zu links" ),
354 aLine2->SegmentCount(), aLine2->Links().size() );
355 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: line endpoints: (%d,%d) to (%d,%d)" ),
356 aLine2->CPoint( 0 ).x, aLine2->CPoint( 0 ).y,
357 aLine2->CLastPoint().x, aLine2->CLastPoint().y );
358
360 path.Add( *aLine2 );
361
362 std::set<ITEM*> visited;
363
364 for( LINKED_ITEM* link : aLine2->Links() )
365 visited.insert( link );
366
367 const JOINT* jtA = m_world->FindJoint( aLine2->CPoint( 0 ), aLine2 );
368 const JOINT* jtB = m_world->FindJoint( aLine2->CLastPoint(), aLine2 );
369
370 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: LEFT branch starting from joint at (%d,%d)" ),
371 jtA->Pos().x, jtA->Pos().y );
372 PATH_RESULT left = followBranch( jtA, aLine2->Links().front(), visited, aFollowLockedSegments );
373 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: LEFT branch result: length=%d, %d items" ),
374 left.m_length, left.m_items.Size() );
375
376 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: RIGHT branch starting from joint at (%d,%d)" ),
377 jtB->Pos().x, jtB->Pos().y );
378 PATH_RESULT right = followBranch( jtB, aLine2->Links().back(), visited, aFollowLockedSegments );
379 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "followTrivialPath: RIGHT branch result: length=%d, %d items" ),
380 right.m_length, right.m_items.Size() );
381
382 if( aTerminalJointA )
383 *aTerminalJointA = left.m_end;
384
385 if( aTerminalJointB )
386 *aTerminalJointB = right.m_end;
387
388 // Count segments as we build the final path
389 int leftSegCount = 0;
390 int rightSegCount = 0;
391 int initialSegCount = 0;
392
393 // Count initial segments
394 for( int i = 0; i < aLine2->SegmentCount(); i++ )
395 initialSegCount++;
396
397 // Add left items
398 for( ITEM* item : left.m_items )
399 {
400 path.Prepend( item );
401 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
402 {
403 LINE* l = dynamic_cast<LINE*>( item );
404 if( l )
405 leftSegCount += l->SegmentCount();
406 else
407 leftSegCount++;
408 }
409 }
410
411 // Add right items
412 for( ITEM* item : right.m_items )
413 {
414 path.Add( item );
415 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
416 {
417 LINE* l = dynamic_cast<LINE*>( item );
418 if( l )
419 rightSegCount += l->SegmentCount();
420 else
421 rightSegCount++;
422 }
423 }
424
425 // Calculate total path length
426 int totalLength = left.m_length + aLine2->CLine().Length() + right.m_length;
427
428 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
429 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath SUMMARY ===" ) );
430 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Starting segment count: %d" ), initialSegCount );
431 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Left branch: %d segments, length=%d" ), leftSegCount, left.m_length );
432 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Initial line: %d segments, length=%lld" ), initialSegCount, aLine2->CLine().Length() );
433 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Right branch: %d segments, length=%d" ), rightSegCount, right.m_length );
434 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total segments in path: %d" ), leftSegCount + initialSegCount + rightSegCount );
435 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total path length: %d" ), totalLength );
436 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "Total items in result: %d" ), path.Size() );
437 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "=== followTrivialPath END ===" ) );
438 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
439
440 return path;
441}
442
443
445 std::pair<const JOINT*, const JOINT*>* aTerminalJoints,
446 bool aFollowLockedSegments )
447{
448 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "*** AssembleTrivialPath: START ***" ) );
449 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: aStart=%p, kind=%s" ),
450 aStart, aStart->KindStr().c_str() );
451
453 LINKED_ITEM* seg = nullptr;
454
455 if( aStart->Kind() == ITEM::VIA_T )
456 {
457 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: starting from VIA" ) );
458 VIA* via = static_cast<VIA*>( aStart );
459 const JOINT* jt = m_world->FindJoint( via->Pos(), via );
460
461 if( !jt->IsNonFanoutVia() )
462 {
463 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: VIA is fanout, returning empty" ) );
464 return ITEM_SET();
465 }
466
467 ITEM_SET links( jt->CLinks() );
468
469 for( ITEM* item : links )
470 {
471 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
472 {
473 seg = static_cast<LINKED_ITEM*>( item );
474 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: found segment/arc from VIA" ) );
475 break;
476 }
477 }
478 }
479 else if( aStart->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
480 {
481 seg = static_cast<LINKED_ITEM*>( aStart );
482 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: starting from SEGMENT/ARC" ) );
483 }
484
485 if( !seg )
486 {
487 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: no segment found, returning empty" ) );
488 return ITEM_SET();
489 }
490
491 // Assemble a line following through locked segments
492 // TODO: consider if we want to allow tuning lines with different widths in the future
493 LINE l = m_world->AssembleLine( seg, nullptr, false, aFollowLockedSegments );
494
495 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: assembled line with %d segments, length=%lld" ),
496 l.SegmentCount(), l.CLine().Length() );
497
498 const JOINT* jointA = nullptr;
499 const JOINT* jointB = nullptr;
500
501 path = followTrivialPath( &l, &jointA, &jointB, aFollowLockedSegments );
502
503 if( aTerminalJoints )
504 {
505 wxASSERT( jointA && jointB );
506 *aTerminalJoints = std::make_pair( jointA, jointB );
507 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: terminal joints at (%d,%d) and (%d,%d)" ),
508 jointA->Pos().x, jointA->Pos().y, jointB->Pos().x, jointB->Pos().y );
509 }
510
511 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTrivialPath: returning path with %d items" ), path.Size() );
512 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "*** AssembleTrivialPath: END ***" ) );
513 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
514
515 return path;
516}
517
518
519const ITEM_SET TOPOLOGY::AssembleTuningPath( ROUTER_IFACE* aRouterIface, ITEM* aStart, SOLID** aStartPad,
520 SOLID** aEndPad )
521{
522 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
523 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: START ##########" ) );
524 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: aStart=%p, kind=%s" ),
525 aStart, aStart->KindStr().c_str() );
526
527 std::pair<const JOINT*, const JOINT*> joints;
528 ITEM_SET initialPath = AssembleTrivialPath( aStart, &joints, true );
529
530 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: initial path has %d items" ), initialPath.Size() );
531
532 PAD* padA = nullptr;
533 PAD* padB = nullptr;
534
535 auto getPadFromJoint =
536 []( const JOINT* aJoint, PAD** aTargetPad, SOLID** aTargetSolid )
537 {
538 for( ITEM* item : aJoint->LinkList() )
539 {
540 if( item->OfKind( ITEM::SOLID_T ) )
541 {
542 BOARD_ITEM* bi = static_cast<SOLID*>( item )->Parent();
543
544 if( bi->Type() == PCB_PAD_T )
545 {
546 *aTargetPad = static_cast<PAD*>( bi );
547
548 if( aTargetSolid )
549 *aTargetSolid = static_cast<SOLID*>( item );
550 }
551
552 break;
553 }
554 }
555 };
556
557 if( joints.first )
558 {
559 getPadFromJoint( joints.first, &padA, aStartPad );
560 if( padA )
561 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found start pad at joint (%d,%d)" ),
562 joints.first->Pos().x, joints.first->Pos().y );
563 }
564
565 if( joints.second )
566 {
567 getPadFromJoint( joints.second, &padB, aEndPad );
568 if( padB )
569 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: found end pad at joint (%d,%d)" ),
570 joints.second->Pos().x, joints.second->Pos().y );
571 }
572
573 if( !padA && !padB )
574 {
575 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: no pads found, returning initial path" ) );
576 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
577 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
578 return initialPath;
579 }
580
581 auto processPad = [&]( PAD* aPad, int aLayer )
582 {
583 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: processing pad, optimizing trace in pad" ) );
584 for( int idx = 0; idx < initialPath.Size(); idx++ )
585 {
586 if( initialPath[idx]->Kind() != ITEM::LINE_T )
587 continue;
588
589 LINE* line = static_cast<LINE*>( initialPath[idx] );
590 SHAPE_LINE_CHAIN& slc = line->Line();
591 const PCB_LAYER_ID pcbLayer = aRouterIface->GetBoardLayerFromPNSLayer( line->Layer() );
592
593 int lengthBefore = slc.Length();
595 int lengthAfter = slc.Length();
596
597 if( lengthBefore != lengthAfter )
598 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: optimized line %d, length changed from %d to %d" ),
599 idx, lengthBefore, lengthAfter );
600 }
601 };
602
603 if( padA )
604 processPad( padA, joints.first->Layer() );
605
606 if( padB )
607 processPad( padB, joints.second->Layer() );
608
609 // Calculate total path length
610 int totalLength = 0;
611 int lineCount = 0;
612 for( int idx = 0; idx < initialPath.Size(); idx++ )
613 {
614 if( initialPath[idx]->Kind() == ITEM::LINE_T )
615 {
616 LINE* line = static_cast<LINE*>( initialPath[idx] );
617 totalLength += line->CLine().Length();
618 lineCount++;
619 }
620 }
621
622 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "AssembleTuningPath: final path has %d items, %d lines, total length=%d" ),
623 initialPath.Size(), lineCount, totalLength );
624 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "########## AssembleTuningPath: END ##########" ) );
625 wxLogTrace( wxT( "PNS_TUNE" ), wxT( "" ) );
626
627 return initialPath;
628}
629
630
631const ITEM_SET TOPOLOGY::ConnectedItems( const JOINT* aStart, int aKindMask )
632{
633 return ITEM_SET();
634}
635
636
637const ITEM_SET TOPOLOGY::ConnectedItems( ITEM* aStart, int aKindMask )
638{
639 return ITEM_SET();
640}
641
642
643bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip );
644
645
647{
648 NET_HANDLE refNet = aStart->Net();
649 NET_HANDLE coupledNet = m_world->GetRuleResolver()->DpCoupledNet( refNet );
650 LINKED_ITEM* startItem = dynamic_cast<LINKED_ITEM*>( aStart );
651
652 if( !coupledNet || !startItem )
653 return false;
654
655 LINE lp = m_world->AssembleLine( startItem );
656
657 std::vector<ITEM*> pItems;
658 std::vector<ITEM*> nItems;
659
660 for( ITEM* item : lp.Links() )
661 {
662 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
663 pItems.push_back( item );
664 }
665
666 std::set<ITEM*> coupledItems;
667 m_world->AllItemsInNet( coupledNet, coupledItems );
668
669 for( ITEM* item : coupledItems )
670 {
671 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && item->Layers() == startItem->Layers() )
672 nItems.push_back( item );
673 }
674
675 LINKED_ITEM* refItem = nullptr;
676 LINKED_ITEM* coupledItem = nullptr;
677 SEG::ecoord minDist_sq = std::numeric_limits<SEG::ecoord>::max();
678 SEG::ecoord minDistTarget_sq = std::numeric_limits<SEG::ecoord>::max();
679 VECTOR2I targetPoint = aStart->Shape( -1 )->Centre();
680
681 auto findNItem = [&]( ITEM* p_item )
682 {
683 for( ITEM* n_item : nItems )
684 {
685 SEG::ecoord dist_sq = std::numeric_limits<SEG::ecoord>::max();
686
687 if( n_item->Kind() != p_item->Kind() )
688 continue;
689
690 if( p_item->Kind() == ITEM::SEGMENT_T )
691 {
692 const SEGMENT* p_seg = static_cast<const SEGMENT*>( p_item );
693 const SEGMENT* n_seg = static_cast<const SEGMENT*>( n_item );
694
695 if( n_seg->Width() != p_seg->Width() )
696 continue;
697
698 if( !p_seg->Seg().ApproxParallel( n_seg->Seg(), DP_PARALLELITY_THRESHOLD ) )
699 continue;
700
701 SEG p_clip, n_clip;
702
703 if( !commonParallelProjection( p_seg->Seg(), n_seg->Seg(), p_clip, n_clip ) )
704 continue;
705
706 dist_sq = n_seg->Seg().SquaredDistance( p_seg->Seg() );
707 }
708 else if( p_item->Kind() == ITEM::ARC_T )
709 {
710 const ARC* p_arc = static_cast<const ARC*>( p_item );
711 const ARC* n_arc = static_cast<const ARC*>( n_item );
712
713 if( n_arc->Width() != p_arc->Width() )
714 continue;
715
716 VECTOR2I centerDiff = n_arc->CArc().GetCenter() - p_arc->CArc().GetCenter();
717 SEG::ecoord centerDist_sq = centerDiff.SquaredEuclideanNorm();
718
719 if( centerDist_sq > SEG::Square( DP_PARALLELITY_THRESHOLD ) )
720 continue;
721
722 dist_sq = SEG::Square( p_arc->CArc().GetRadius() - n_arc->CArc().GetRadius() );
723 }
724
725 if( dist_sq <= minDist_sq )
726 {
727 SEG::ecoord distTarget_sq = n_item->Shape( -1 )->SquaredDistance( targetPoint );
728 if( distTarget_sq < minDistTarget_sq )
729 {
730 minDistTarget_sq = distTarget_sq;
731 minDist_sq = dist_sq;
732
733 refItem = static_cast<LINKED_ITEM*>( p_item );
734 coupledItem = static_cast<LINKED_ITEM*>( n_item );
735 }
736 }
737 }
738 };
739
740 findNItem( startItem );
741
742 if( !coupledItem )
743 {
744 LINKED_ITEM* linked = static_cast<LINKED_ITEM*>( startItem );
745 std::set<ITEM*> linksToTest;
746
747 for( int i = 0; i < linked->AnchorCount(); i++ )
748 {
749 const JOINT* jt = m_world->FindJoint( linked->Anchor( i ), linked );
750
751 if( !jt )
752 continue;
753
754 for( ITEM* link : jt->LinkList() )
755 {
756 if( link != linked )
757 linksToTest.emplace( link );
758 }
759 }
760
761 for( ITEM* link : linksToTest )
762 findNItem( link );
763 }
764
765 if( !coupledItem )
766 return false;
767
768 LINE ln = m_world->AssembleLine( coupledItem );
769
770 if( m_world->GetRuleResolver()->DpNetPolarity( refNet ) < 0 )
771 std::swap( lp, ln );
772
773 int gap = -1;
774
775 if( refItem && refItem->Kind() == ITEM::SEGMENT_T )
776 {
777 // Segments are parallel -> compute pair gap
778 const VECTOR2I refDir = refItem->Anchor( 1 ) - refItem->Anchor( 0 );
779 const VECTOR2I displacement = refItem->Anchor( 1 ) - coupledItem->Anchor( 1 );
780 gap = (int) std::abs( refDir.Cross( displacement ) / refDir.EuclideanNorm() ) - lp.Width();
781 }
782 else if( refItem && refItem->Kind() == ITEM::ARC_T )
783 {
784 const ARC* refArc = static_cast<ARC*>( refItem );
785 const ARC* coupledArc = static_cast<ARC*>( coupledItem );
786 gap = (int) std::abs( refArc->CArc().GetRadius() - coupledArc->CArc().GetRadius() ) - lp.Width();
787 }
788
789 aPair = DIFF_PAIR( lp, ln );
790 aPair.SetWidth( lp.Width() );
791 aPair.SetLayers( lp.Layers() );
792 aPair.SetGap( gap );
793
794 return true;
795}
796
797const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, double aAreaExpansionLimit, NET_HANDLE aExcludedNet )
798{
799 CLUSTER cluster;
800 std::deque<ITEM*> pending;
801
803
804 opts.m_differentNetsOnly = false;
805 opts.m_overrideClearance = 0;
806
807 pending.push_back( aStart );
808
809 BOX2I clusterBBox = aStart->Shape( aLayer )->BBox();
810 int64_t initialArea = clusterBBox.GetArea();
811
812 while( !pending.empty() )
813 {
814 NODE::OBSTACLES obstacles;
815 ITEM* top = pending.front();
816
817 pending.pop_front();
818
819 cluster.m_items.insert( top );
820
821 m_world->QueryColliding( top, obstacles, opts ); // only query touching objects
822
823 for( const OBSTACLE& obs : obstacles )
824 {
825 bool trackOnTrack = ( obs.m_item->Net() != top->Net() ) && obs.m_item->OfKind( ITEM::SEGMENT_T ) && top->OfKind( ITEM::SEGMENT_T );
826
827 if( trackOnTrack )
828 continue;
829
830 if( aExcludedNet && obs.m_item->Net() == aExcludedNet )
831 continue;
832
833 if( obs.m_item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && obs.m_item->Layers().Overlaps( aLayer ) )
834 {
835 auto line = m_world->AssembleLine( static_cast<LINKED_ITEM*>(obs.m_item) );
836 clusterBBox.Merge( line.CLine().BBox() );
837 }
838 else
839 {
840 clusterBBox.Merge( obs.m_item->Shape( aLayer )->BBox() );
841 }
842
843 const int64_t currentArea = clusterBBox.GetArea();
844 const double areaRatio = (double) currentArea / (double) ( initialArea + 1 );
845
846 if( aAreaExpansionLimit > 0.0 && areaRatio > aAreaExpansionLimit )
847 break;
848
849 if( cluster.m_items.find( obs.m_item ) == cluster.m_items.end() &&
850 obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
851 {
852 cluster.m_items.insert( obs.m_item );
853 pending.push_back( obs.m_item );
854 }
855 }
856 }
857
858 return cluster;
859}
860
861}
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: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: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:110
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
Base class for PNS router board items.
Definition pns_item.h:98
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:244
virtual PCB_LAYER_ID GetBoardLayerFromPNSLayer(int aLayer) const =0
const SEG & Seg() const
Definition pns_segment.h:93
int Width() const override
Definition pns_segment.h:88
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)
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
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)
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.
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:546
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::set< ITEM * > m_items
std::string path
KIBIS top(path, &reporter)
VECTOR2I end
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695