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