KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_node.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <vector>
24#include <cassert>
25#include <utility>
26
27#include <math/vector2d.h>
28#include <thread_pool.h>
29
30#include <geometry/seg.h>
32#include <pcb_track.h>
33#include <zone.h>
34
35#include <wx/log.h>
36
37#include "pns_arc.h"
38#include "pns_item.h"
39#include "pns_itemset.h"
40#include "pns_line.h"
41#include "pns_node.h"
42#include "pns_via.h"
43#include "pns_solid.h"
44#include "pns_joint.h"
45#include "pns_index.h"
46#include "pns_debug_decorator.h"
47#include "pns_router.h"
48#include "pns_utils.h"
49
50
51namespace PNS {
52
53#ifdef DEBUG
54static std::unordered_set<const NODE*> allocNodes;
55#endif
56
58{
59 m_depth = 0;
60 m_root = this;
61 m_parent = nullptr;
62 m_maxClearance = 800000; // fixme: depends on how thick traces are.
63 m_ruleResolver = nullptr;
64 m_index = new INDEX;
65
66#ifdef DEBUG
67 allocNodes.insert( this );
68#endif
69}
70
71
73{
74 if( !m_children.empty() )
75 {
76 wxLogTrace( wxT( "PNS" ), wxT( "attempting to free a node that has kids." ) );
77 assert( false );
78 }
79
80#ifdef DEBUG
81 if( allocNodes.find( this ) == allocNodes.end() )
82 {
83 wxLogTrace( wxT( "PNS" ), wxT( "attempting to free an already-free'd node." ) );
84 assert( false );
85 }
86
87 allocNodes.erase( this );
88#endif
89
90 m_joints.clear();
91
92 std::vector<const ITEM*> toDelete;
93 std::vector<const ITEM*> parentedHoles;
94
95 toDelete.reserve( m_index->Size() );
96
97 for( ITEM* item : *m_index )
98 {
99 if( item->BelongsTo( this ) )
100 {
101 if ( item->OfKind( ITEM::HOLE_T ) )
102 {
103 HOLE* hole = static_cast<HOLE*>( item );
104 if( hole->ParentPadVia() )
105 {
106 // If a hole is no longer owned by the same NODE as its parent then we're in a
107 // heap of trouble.
108 assert( hole->ParentPadVia()->BelongsTo( this ) );
109
110 // Its parent comes by as a VIA or SOLID, and deletes only a hole it owns
111 if( hole->ParentPadVia()->BelongsTo( this ) )
112 {
113 hole->SetOwner( hole->ParentPadVia() );
114 parentedHoles.push_back( hole );
115 }
116 }
117 else
118 {
119 // freestanding hole
120 toDelete.push_back(item);
121 }
122 }
123 else {
124 // other geometry with no holes
125 toDelete.push_back(item);
126 }
127 }
128 }
129
130 if( m_ruleResolver )
131 {
132 m_ruleResolver->ClearCacheForItems( toDelete );
133 m_ruleResolver->ClearCacheForItems( parentedHoles );
134 }
135
136 for( const ITEM* item : toDelete )
137 {
138 wxLogTrace( wxT( "PNS" ), wxT( "del item %p type %s" ), item, item->KindStr().c_str() );
139 delete item;
140 }
141
143 unlinkParent();
144
145 delete m_index;
146}
147
148
149int NODE::GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
150{
151 if( !m_ruleResolver )
152 return 100000;
153
154 if( aA->IsVirtual() || aB->IsVirtual() )
155 return 0;
156
157 int cl = m_ruleResolver->Clearance( aA, aB, aUseClearanceEpsilon );
158
159 return cl;
160}
161
162
164{
165 NODE* child = new NODE;
166
167 m_children.insert( child );
168
169 child->m_depth = m_depth + 1;
170 child->m_parent = this;
172 child->m_root = isRoot() ? this : m_root;
174
175 // Immediate offspring of the root branch needs not copy anything. For the rest, clone
176 // the spatial index, joints, and overridden item maps.
177 if( !isRoot() )
178 {
179 delete child->m_index;
180 child->m_index = m_index->Clone().release();
181
182 child->m_joints = m_joints;
183 child->m_override = m_override;
184 }
185
186#if 0
187 wxLogTrace( wxT( "PNS" ), wxT( "%d items, %d joints, %d overrides" ),
188 child->m_index->Size(),
189 (int) child->m_joints.size(),
190 (int) child->m_override.size() );
191#endif
192
193 return child;
194}
195
196
198{
199 if( isRoot() )
200 return;
201
202 m_parent->m_children.erase( this );
203}
204
205
207 m_item( aItem ),
208 m_node( nullptr ),
209 m_override( nullptr )
210{
211}
212
213
214void OBSTACLE_VISITOR::SetWorld( const NODE* aNode, const NODE* aOverride )
215{
216 m_node = aNode;
217 m_override = aOverride;
218}
219
220
221bool OBSTACLE_VISITOR::visit( ITEM* aCandidate )
222{
223 // check if there is a more recent branch with a newer (possibly modified) version of this
224 // item.
225 if( m_override && m_override->Overrides( aCandidate ) )
226 return true;
227
228 return false;
229}
230
231
232// function object that visits potential obstacles and performs the actual collision refining
234{
236
238 OBSTACLE_VISITOR( aItem ),
239 m_ctx( aCtx )
240 {
241 }
242
244 {
245 }
246
247 bool operator()( ITEM* aCandidate ) override
248 {
249 if( !aCandidate->OfKind( m_ctx->options.m_kindMask ) )
250 return true;
251
252 // Collisions with self aren't a thing; don't spend time on them.
253 if( m_item == aCandidate )
254 return true;
255
256 if( m_ctx->options.m_filter && !m_ctx->options.m_filter( aCandidate, m_item ) )
257 return true;
258
259 if( visit( aCandidate ) )
260 return true;
261
262 if( !aCandidate->Collide( m_item, m_node, m_layerContext.value_or( -1 ), m_ctx ) )
263 return true;
264
265 if( m_ctx->options.m_limitCount > 0 && (int) m_ctx->obstacles.size() >= m_ctx->options.m_limitCount )
266 return false;
267
268 return true;
269 };
270};
271
272
273int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles,
274 const COLLISION_SEARCH_OPTIONS& aOpts ) const
275{
276 COLLISION_SEARCH_CONTEXT ctx( aObstacles, aOpts );
277
279 if( aItem->IsVirtual() )
280 return 0;
281
282 DEFAULT_OBSTACLE_VISITOR visitor( &ctx, aItem );
283
284#ifdef DEBUG
285 assert( allocNodes.find( this ) != allocNodes.end() );
286#endif
287
288 visitor.SetWorld( this, nullptr );
289
290 // first, look for colliding items in the local index
291 m_index->Query( aItem, m_maxClearance, visitor );
292
293 // if we haven't found enough items, look in the root branch as well.
294 if( !isRoot() && ( (int) ctx.obstacles.size() < aOpts.m_limitCount || aOpts.m_limitCount < 0 ) )
295 {
296 visitor.SetWorld( m_root, this );
297 m_root->m_index->Query( aItem, m_maxClearance, visitor );
298 }
299
300 return aObstacles.size();
301}
302
303
305 const COLLISION_SEARCH_OPTIONS& aOpts )
306{
308 OBSTACLES obstacleSet;
309
310 for( int i = 0; i < aLine->CLine().SegmentCount(); i++ )
311 {
312 // Note: Clearances between &s and other items are cached,
313 // which means they'll be the same for all segments in the line.
314 // Disabling the cache will lead to slowness.
315
316 const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) );
317 QueryColliding( &s, obstacleSet, aOpts );
318 }
319
320 if( aLine->EndsWithVia() )
321 QueryColliding( &aLine->Via(), obstacleSet, aOpts );
322
323 if( obstacleSet.empty() )
324 return OPT_OBSTACLE();
325
326 // Convert to indexed vector for parallel access.
327 std::vector<OBSTACLE> obstacles( obstacleSet.begin(), obstacleSet.end() );
328 const int numObstacles = (int) obstacles.size();
329
330 const int layer = aLine->Layer();
331 RULE_RESOLVER* ruleResolver = GetRuleResolver();
332 const bool simplifyHull = ( cornerMode == DIRECTION_45::MITERED_90
333 || cornerMode == DIRECTION_45::ROUNDED_90 );
334 const bool hasVia = aLine->EndsWithVia();
335
336 auto makeHull = [&]( const SHAPE_LINE_CHAIN& cachedHull ) -> SHAPE_LINE_CHAIN
337 {
338 if( simplifyHull )
339 {
340 BOX2I bbox = cachedHull.BBox();
341 SHAPE_LINE_CHAIN hull;
342 hull.Append( bbox.GetLeft(), bbox.GetTop() );
343 hull.Append( bbox.GetRight(), bbox.GetTop() );
344 hull.Append( bbox.GetRight(), bbox.GetBottom() );
345 hull.Append( bbox.GetLeft(), bbox.GetBottom() );
346 return hull;
347 }
348
349 return cachedHull;
350 };
351
352 // The first step here is sequential since GetClearance() and HullCache() are not thread-safe.
353 // So, we populate all caches first and copy the returned hull references into owned values
354 // before releasing the sequential phase.
355 struct ObstacleHullData
356 {
357 SHAPE_LINE_CHAIN lineHull;
358 SHAPE_LINE_CHAIN viaHull; // only populated when hasVia
359 };
360
361 std::vector<ObstacleHullData> hullData( numObstacles );
362
363 for( int i = 0; i < numObstacles; i++ )
364 {
365 const OBSTACLE& obstacle = obstacles[i];
366
367 int clearance = GetClearance( obstacle.m_item, aLine, aOpts.m_useClearanceEpsilon )
368 + aLine->Width() / 2;
369
370 hullData[i].lineHull = makeHull( ruleResolver->HullCache( obstacle.m_item, clearance,
371 0, layer ) );
372
373 if( hasVia )
374 {
375 const VIA& via = aLine->Via();
376 int viaClearance = GetClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon )
377 + via.Diameter( aLine->Layer() ) / 2;
378
379 hullData[i].viaHull = makeHull( ruleResolver->HullCache( obstacle.m_item,
380 viaClearance, 0, layer ) );
381 }
382 }
383
384 // Run the obstacle finding in parallel and bring the results together afte
385 struct ObstacleResult
386 {
387 int dist = INT_MAX;
388 VECTOR2I ip;
389 };
390
391 std::vector<ObstacleResult> results( numObstacles );
392 const SHAPE_LINE_CHAIN& linePath = aLine->CLine();
393
394 auto processObstacle = [&]( int i )
395 {
396 std::vector<SHAPE_LINE_CHAIN::INTERSECTION> ips;
397 ObstacleResult& result = results[i];
398
399 HullIntersection( hullData[i].lineHull, linePath, ips );
400
401 for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : ips )
402 {
403 if( !ip.valid )
404 continue;
405
406 int dist = linePath.PathLength( ip.p, ip.index_their );
407
408 if( dist < result.dist )
409 {
410 result.dist = dist;
411 result.ip = ip.p;
412 }
413 }
414
415 if( hasVia )
416 {
417 ips.clear();
418 HullIntersection( hullData[i].viaHull, linePath, ips );
419
420 for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : ips )
421 {
422 if( !ip.valid )
423 continue;
424
425 int dist = linePath.PathLength( ip.p, ip.index_their );
426
427 if( dist < result.dist )
428 {
429 result.dist = dist;
430 result.ip = ip.p;
431 }
432 }
433 }
434 };
435
436 // Each task submission locks the thread pool's priority queue mutex, and thread wakeup
437 // latency is ~5-20µs. With too few items per block the synchronization cost exceeds the
438 // geometry work. Use a minimum chunk size so blocks are always worth dispatching, while
439 // still entering the parallel path at a lower obstacle count than a flat threshold allows.
440 constexpr int MIN_OBSTACLES_PER_BLOCK = 8;
441 constexpr int PARALLEL_THRESHOLD = MIN_OBSTACLES_PER_BLOCK;
442
443 if( numObstacles > PARALLEL_THRESHOLD )
444 {
446 std::size_t numBlocks = std::max<std::size_t>( 1, numObstacles / MIN_OBSTACLES_PER_BLOCK );
447
448 auto futures = tp.submit_loop( 0, numObstacles, [&]( int i ) { processObstacle( i ); },
449 numBlocks );
450 futures.wait();
451 }
452 else
453 {
454 for( int i = 0; i < numObstacles; i++ )
455 processObstacle( i );
456 }
457
458 OBSTACLE nearest;
459 nearest.m_head = nullptr;
460 nearest.m_item = nullptr;
461 nearest.m_distFirst = INT_MAX;
462 nearest.m_maxFanoutWidth = 0;
463
464 for( int i = 0; i < numObstacles; i++ )
465 {
466 if( results[i].dist < nearest.m_distFirst )
467 {
468 nearest = obstacles[i];
469 nearest.m_distFirst = results[i].dist;
470 nearest.m_ipFirst = results[i].ip;
471
472 if( results[i].dist == 0 )
473 break;
474 }
475 }
476
477 if( nearest.m_distFirst == INT_MAX )
478 nearest = obstacles[0];
479
480 return nearest;
481}
482
483
485{
486 for( const ITEM* item : aSet.CItems() )
487 {
488 OPT_OBSTACLE obs = CheckColliding( item, aKindMask );
489
490 if( obs )
491 return obs;
492 }
493
494 return OPT_OBSTACLE();
495}
496
497
498NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
499{
501
502 opts.m_kindMask = aKindMask;
503 opts.m_limitCount = 1;
504
505 return CheckColliding( aItemA, opts );
506}
507
509{
510 OBSTACLES obs;
511
512 if( aItemA->Kind() == ITEM::LINE_T )
513 {
514 int n = 0;
515 const LINE* line = static_cast<const LINE*>( aItemA );
516 const SHAPE_LINE_CHAIN& l = line->CLine();
517
518 for( int i = 0; i < l.SegmentCount(); i++ )
519 {
520 // Note: Clearances between &s and other items are cached,
521 // which means they'll be the same for all segments in the line.
522 // Disabling the cache will lead to slowness.
523
524 const SEGMENT s( *line, l.CSegment( i ) );
525 n += QueryColliding( &s, obs, aOpts );
526
527 if( n )
528 return OPT_OBSTACLE( *obs.begin() );
529 }
530
531 if( line->EndsWithVia() )
532 {
533 n += QueryColliding( &line->Via(), obs, aOpts );
534
535 if( n )
536 return OPT_OBSTACLE( *obs.begin() );
537 }
538 }
539 else if( QueryColliding( aItemA, obs, aOpts ) > 0 )
540 {
541 return OPT_OBSTACLE( *obs.begin() );
542 }
543
544 return OPT_OBSTACLE();
545}
546
547
549{
552
553 HIT_VISITOR( ITEM_SET& aTab, const VECTOR2I& aPoint ) :
554 OBSTACLE_VISITOR( nullptr ),
555 m_items( aTab ),
556 m_point( aPoint )
557 {}
558
559 virtual ~HIT_VISITOR()
560 {
561 }
562
563 bool operator()( ITEM* aItem ) override
564 {
565 SHAPE_CIRCLE cp( m_point, 0 );
566
567 int cl = 0;
568
569 // TODO(JE) padstacks -- this may not work
570 if( aItem->Shape( -1 )->Collide( &cp, cl ) )
571 m_items.Add( aItem );
572
573 return true;
574 }
575};
576
577
578const ITEM_SET NODE::HitTest( const VECTOR2I& aPoint ) const
579{
580 ITEM_SET items;
581
582 // fixme: we treat a point as an infinitely small circle - this is inefficient.
583 SHAPE_CIRCLE s( aPoint, 0 );
584 HIT_VISITOR visitor( items, aPoint );
585 visitor.SetWorld( this, nullptr );
586
587 m_index->Query( &s, m_maxClearance, visitor );
588
589 if( !isRoot() ) // fixme: could be made cleaner
590 {
591 ITEM_SET items_root;
592 visitor.SetWorld( m_root, nullptr );
593 HIT_VISITOR visitor_root( items_root, aPoint );
594 m_root->m_index->Query( &s, m_maxClearance, visitor_root );
595
596 for( ITEM* item : items_root.Items() )
597 {
598 if( !Overrides( item ) )
599 items.Add( item );
600 }
601 }
602
603 return items;
604}
605
606
607void NODE::addSolid( SOLID* aSolid )
608{
609 if( aSolid->HasHole() )
610 {
611 assert( aSolid->Hole()->BelongsTo( aSolid ) );
612 addHole( aSolid->Hole() );
613 }
614
615 if( aSolid->IsRoutable() )
616 linkJoint( aSolid->Pos(), aSolid->Layers(), aSolid->Net(), aSolid );
617
618 aSolid->SetOwner( this );
619 m_index->Add( aSolid );
620}
621
622
623void NODE::Add( std::unique_ptr< SOLID > aSolid )
624{
625 aSolid->SetOwner( this );
626 addSolid( aSolid.release() );
627}
628
629
630void NODE::addVia( VIA* aVia )
631{
632 if( aVia->HasHole() )
633 {
634 if( ! aVia->Hole()->BelongsTo( aVia ) )
635 {
636 assert( false );
637 }
638 addHole( aVia->Hole() );
639 }
640
641 linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia );
642 aVia->SetOwner( this );
643
644 m_index->Add( aVia );
645}
646
647
648void NODE::addHole( HOLE* aHole )
649{
650 // do we need holes in the connection graph?
651 //linkJoint( aHole->Pos(), aHole->Layers(), aHole->Net(), aHole );
652
653 aHole->SetOwner( this );
654 m_index->Add( aHole );
655}
656
657
658void NODE::Add( std::unique_ptr< VIA > aVia )
659{
660 addVia( aVia.release() );
661}
662
663
664void NODE::add( ITEM* aItem, bool aAllowRedundant )
665{
666 switch( aItem->Kind() )
667 {
668 case ITEM::ARC_T:
669 addArc( static_cast<ARC*>( aItem ) );
670 break;
671 case ITEM::SEGMENT_T:
672 addSegment( static_cast<SEGMENT*>( aItem ) );
673 break;
674 case ITEM::VIA_T:
675 addVia( static_cast<VIA*>( aItem ) );
676 break;
677 case ITEM::SOLID_T:
678 addSolid( static_cast<SOLID*>( aItem ) );
679 break;
680 case ITEM::HOLE_T:
681 // added by parent VIA_T or SOLID_T (pad)
682 break;
683 default:
684 assert( false );
685 }
686}
687
688
689void NODE::Add( LINE& aLine, bool aAllowRedundant )
690{
691 assert( !aLine.IsLinked() );
692
693 SHAPE_LINE_CHAIN& l = aLine.Line();
694
695 for( size_t i = 0; i < l.ArcCount(); i++ )
696 {
697 auto s = l.Arc( i );
698 ARC* rarc;
699
700 if( !aAllowRedundant && ( rarc = findRedundantArc( s.GetP0(), s.GetP1(), aLine.Layers(),
701 aLine.Net() ) ) )
702 {
703 aLine.Link( rarc );
704 }
705 else
706 {
707 auto newarc = std::make_unique< ARC >( aLine, s );
708 aLine.Link( newarc.get() );
709 Add( std::move( newarc ), true );
710 }
711 }
712
713 for( int i = 0; i < l.SegmentCount(); i++ )
714 {
715 if( l.IsArcSegment( i ) )
716 continue;
717
718 SEG s = l.CSegment( i );
719
720 if( s.A != s.B )
721 {
722 SEGMENT* rseg;
723
724 if( !aAllowRedundant && ( rseg = findRedundantSegment( s.A, s.B, aLine.Layers(),
725 aLine.Net() ) ) )
726 {
727 // another line could be referencing this segment too :(
728 if( !aLine.ContainsLink( rseg ) )
729 aLine.Link( rseg );
730 }
731 else
732 {
733 std::unique_ptr<SEGMENT> newseg = std::make_unique<SEGMENT>( aLine, s );
734 aLine.Link( newseg.get() );
735 Add( std::move( newseg ), true );
736 }
737 }
738 }
739}
740
741
743{
744 aSeg->SetOwner( this );
745
746 linkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
747 linkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
748
749 m_index->Add( aSeg );
750}
751
752
753bool NODE::Add( std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant )
754{
755 if( aSegment->Seg().A == aSegment->Seg().B )
756 {
757 wxLogTrace( wxT( "PNS" ),
758 wxT( "attempting to add a segment with same end coordinates, ignoring." ) );
759 return false;
760 }
761
762 if( !aAllowRedundant && findRedundantSegment( aSegment.get() ) )
763 return false;
764
765 addSegment( aSegment.release() );
766
767 return true;
768}
769
770
771void NODE::addArc( ARC* aArc )
772{
773 aArc->SetOwner( this );
774
775 linkJoint( aArc->Anchor( 0 ), aArc->Layers(), aArc->Net(), aArc );
776 linkJoint( aArc->Anchor( 1 ), aArc->Layers(), aArc->Net(), aArc );
777
778 m_index->Add( aArc );
779}
780
781
782bool NODE::Add( std::unique_ptr< ARC > aArc, bool aAllowRedundant )
783{
784 const SHAPE_ARC& arc = aArc->CArc();
785
786 if( !aAllowRedundant && findRedundantArc( arc.GetP0(), arc.GetP1(), aArc->Layers(),
787 aArc->Net() ) )
788 {
789 return false;
790 }
791
792 addArc( aArc.release() );
793 return true;
794}
795
796
797void NODE::AddEdgeExclusion( std::unique_ptr<SHAPE> aShape )
798{
799 m_edgeExclusions.push_back( std::move( aShape ) );
800}
801
802
803bool NODE::QueryEdgeExclusions( const VECTOR2I& aPos ) const
804{
805 for( const std::unique_ptr<SHAPE>& edgeExclusion : m_edgeExclusions )
806 {
807 if( edgeExclusion->Collide( aPos ) )
808 return true;
809 }
810
811 return false;
812}
813
814
815void NODE::doRemove( ITEM* aItem )
816{
817 bool holeRemoved = false; // fixme: better logic, I don't like this
818
819 // case 1: removing an item that is stored in the root node from any branch:
820 // mark it as overridden, but do not remove
821 if( aItem->BelongsTo( m_root ) && !isRoot() )
822 {
823 m_override.insert( aItem );
824
825 if( aItem->HasHole() )
826 m_override.insert( aItem->Hole() );
827 }
828
829 // case 2: the item belongs to this branch or a parent, non-root branch,
830 // or the root itself and we are the root: remove from the index
831 else if( !aItem->BelongsTo( m_root ) || isRoot() )
832 {
833 m_index->Remove( aItem );
834
835 if( aItem->HasHole() )
836 {
837 m_index->Remove( aItem->Hole() );
838 holeRemoved = true;
839 }
840 }
841
842 // the item belongs to this particular branch: un-reference it
843 if( aItem->BelongsTo( this ) )
844 {
845 aItem->SetOwner( nullptr );
846 m_root->m_garbageItems.insert( aItem );
847 HOLE *hole = aItem->Hole();
848
849 if( hole )
850 {
851 if( ! holeRemoved )
852 {
853 m_index->Remove( hole ); // hole is not directly owned by NODE but by the parent SOLID/VIA.
854 }
855
856 hole->SetOwner( aItem );
857 }
858 }
859}
860
861
863{
864 unlinkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
865 unlinkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
866}
867
868
870{
871 unlinkJoint( aArc->Anchor( 0 ), aArc->Layers(), aArc->Net(), aArc );
872 unlinkJoint( aArc->Anchor( 1 ), aArc->Layers(), aArc->Net(), aArc );
873}
874
875
876void NODE::rebuildJoint( const JOINT* aJoint, const ITEM* aItem )
877{
878 if( !aJoint )
879 return;
880
881 // We have to split a single joint (associated with a via or a pad, binding together multiple
882 // layers) into multiple independent joints. As I'm a lazy bastard, I simply delete the
883 // via/solid and all its links and re-insert them.
884
885 std::vector<ITEM*> links( aJoint->LinkList() );
886 JOINT::HASH_TAG tag;
887 NET_HANDLE net = aItem->Net();
888
889 tag.net = net;
890 tag.pos = aJoint->Pos();
891
892 bool split;
893
894 do
895 {
896 split = false;
897 auto range = m_joints.equal_range( tag );
898
899 if( range.first == m_joints.end() )
900 break;
901
902 // find and remove all joints containing the via to be removed
903
904 for( auto f = range.first; f != range.second; ++f )
905 {
906 if( aItem->LayersOverlap( &f->second ) )
907 {
908 m_joints.erase( f );
909 split = true;
910 break;
911 }
912 }
913 } while( split );
914
915 bool completelyErased = false;
916
917 if( !isRoot() && m_joints.find( tag ) == m_joints.end() )
918 {
919 JOINT jtDummy( tag.pos, PNS_LAYER_RANGE(-1), tag.net );
920
921 m_joints.insert( TagJointPair( tag, jtDummy ) );
922 completelyErased = true;
923 }
924
925
926 // and re-link them, using the former via's link list
927 for( ITEM* link : links )
928 {
929 if( link != aItem )
930 linkJoint( tag.pos, link->Layers(), net, link );
931 else if( !completelyErased )
932 unlinkJoint( tag.pos, link->Layers(), net, link );
933 }
934}
935
936
938{
939 const JOINT* jt = FindJoint( aVia->Pos(), aVia->Layers().Start(), aVia->Net() );
940 assert( jt );
941 rebuildJoint( jt, aVia );
942}
943
944
946{
947 if( !aSolid->IsRoutable() )
948 return;
949
950 // fixme: redundant code
951 const JOINT* jt = FindJoint( aSolid->Pos(), aSolid->Layers().Start(), aSolid->Net() );
952 assert( jt );
953 rebuildJoint( jt, aSolid );
954}
955
956
957void NODE::Replace( ITEM* aOldItem, std::unique_ptr< ITEM > aNewItem )
958{
959 Remove( aOldItem );
960 add( aNewItem.release() );
961}
962
963
964void NODE::Replace( LINE& aOldLine, LINE& aNewLine, bool aAllowRedundantSegments )
965{
966 Remove( aOldLine );
967 Add( aNewLine, aAllowRedundantSegments );
968}
969
970
971void NODE::Remove( SOLID* aSolid )
972{
973 removeSolidIndex( aSolid );
974 doRemove( aSolid );
975}
976
977
978void NODE::Remove( VIA* aVia )
979{
980 removeViaIndex( aVia );
981 doRemove( aVia );
982
983 if( !aVia->Owner() )
984 {
985 assert( aVia->Hole()->BelongsTo( aVia ) );
986 }
987}
988
989
990void NODE::Remove( SEGMENT* aSegment )
991{
992 removeSegmentIndex( aSegment );
993 doRemove( aSegment );
994}
995
996
997void NODE::Remove( ARC* aArc )
998{
999 removeArcIndex( aArc );
1000 doRemove( aArc );
1001}
1002
1003
1004void NODE::Remove( ITEM* aItem )
1005{
1006 switch( aItem->Kind() )
1007 {
1008 case ITEM::ARC_T:
1009 Remove( static_cast<ARC*>( aItem ) );
1010 break;
1011
1012 case ITEM::SOLID_T:
1013 {
1014 SOLID* solid = static_cast<SOLID*>( aItem );
1015
1016 if( solid->HasHole() )
1017 {
1018 Remove( solid->Hole() );
1019 solid->Hole()->SetOwner( solid );
1020 }
1021
1022 Remove( static_cast<SOLID*>( aItem ) );
1023 break;
1024 }
1025
1026 case ITEM::SEGMENT_T:
1027 Remove( static_cast<SEGMENT*>( aItem ) );
1028 break;
1029
1030 case ITEM::LINE_T:
1031 {
1032 LINE* l = static_cast<LINE*>( aItem );
1033
1034 for ( LINKED_ITEM* s : l->Links() )
1035 Remove( s );
1036
1037 break;
1038 }
1039
1040 case ITEM::VIA_T:
1041 {
1042 VIA* via = static_cast<VIA*>( aItem );
1043
1044 if( via->HasHole() )
1045 {
1046 Remove( via->Hole() );
1047 via->Hole()->SetOwner( via );
1048 }
1049
1050 Remove( static_cast<VIA*>( aItem ) );
1051 break;
1052 }
1053
1054 default:
1055 break;
1056 }
1057}
1058
1059
1060void NODE::Remove( LINE& aLine )
1061{
1062 // LINE does not have a separate remover, as LINEs are never truly a member of the tree
1063 std::vector<LINKED_ITEM*>& segRefs = aLine.Links();
1064
1065 for( LINKED_ITEM* li : segRefs )
1066 {
1067 if( li->OfKind( ITEM::SEGMENT_T ) )
1068 Remove( static_cast<SEGMENT*>( li ) );
1069 else if( li->OfKind( ITEM::ARC_T ) )
1070 Remove( static_cast<ARC*>( li ) );
1071 else if( li->OfKind( ITEM::VIA_T ) )
1072 Remove( static_cast<VIA*>( li ) );
1073 }
1074
1075 aLine.SetOwner( nullptr );
1076 aLine.ClearLinks();
1077}
1078
1079
1080void NODE::followLine( LINKED_ITEM* aCurrent, bool aScanDirection, int& aPos, int aLimit,
1081 VECTOR2I* aCorners, LINKED_ITEM** aSegments, bool* aArcReversed,
1082 bool& aGuardHit, bool aStopAtLockedJoints, bool aFollowLockedSegments,
1083 bool aAllowSegmentSizeMismatch )
1084{
1085 bool prevReversed = false;
1086
1087 const VECTOR2I guard = aCurrent->Anchor( aScanDirection );
1088 const int startWidth = aCurrent->Width();
1089
1090 for( int count = 0 ; ; ++count )
1091 {
1092 const VECTOR2I p = aCurrent->Anchor( aScanDirection ^ prevReversed );
1093 const JOINT* jt = FindJoint( p, aCurrent );
1094
1095 if( !jt )
1096 break;
1097
1098 aCorners[aPos] = jt->Pos();
1099 aSegments[aPos] = aCurrent;
1100 aArcReversed[aPos] = false;
1101
1102 if( aCurrent->Kind() == ITEM::ARC_T )
1103 {
1104 if( ( aScanDirection && jt->Pos() == aCurrent->Anchor( 0 ) )
1105 || ( !aScanDirection && jt->Pos() == aCurrent->Anchor( 1 ) ) )
1106 {
1107 aArcReversed[aPos] = true;
1108 }
1109 }
1110
1111 aPos += ( aScanDirection ? 1 : -1 );
1112
1113 if( count && guard == p )
1114 {
1115 if( aPos >= 0 && aPos < aLimit )
1116 aSegments[aPos] = nullptr;
1117
1118 aGuardHit = true;
1119 break;
1120 }
1121
1122 bool locked = aStopAtLockedJoints ? jt->IsLocked() : false;
1123
1124 if( locked || aPos < 0 || aPos == aLimit )
1125 break;
1126
1127 LINKED_ITEM* next = jt->NextSegment( aCurrent, aFollowLockedSegments );
1128
1129 if( !next || ( !aAllowSegmentSizeMismatch && next->Width() != startWidth ) )
1130 break;
1131
1132 aCurrent = next;
1133 prevReversed = ( aCurrent && jt->Pos() == aCurrent->Anchor( aScanDirection ) );
1134 }
1135}
1136
1137
1138const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex, bool aStopAtLockedJoints,
1139 bool aFollowLockedSegments, bool aAllowSegmentSizeMismatch )
1140{
1141 const int MaxVerts = 1024 * 16;
1142
1143 std::array<VECTOR2I, MaxVerts + 1> corners;
1144 std::array<LINKED_ITEM*, MaxVerts + 1> segs;
1145 std::array<bool, MaxVerts + 1> arcReversed;
1146
1147 LINE pl;
1148 bool guardHit = false;
1149
1150 int i_start = MaxVerts / 2;
1151 int i_end = i_start + 1;
1152
1153 pl.SetWidth( aSeg->Width() );
1154 pl.SetLayers( aSeg->Layers() );
1155 pl.SetNet( aSeg->Net() );
1156 pl.SetParent( nullptr );
1157 pl.SetSourceItem( aSeg->GetSourceItem() );
1158 pl.SetOwner( this );
1159
1160 followLine( aSeg, false, i_start, MaxVerts, corners.data(), segs.data(), arcReversed.data(),
1161 guardHit, aStopAtLockedJoints, aFollowLockedSegments, aAllowSegmentSizeMismatch );
1162
1163 if( !guardHit )
1164 {
1165 followLine( aSeg, true, i_end, MaxVerts, corners.data(), segs.data(), arcReversed.data(),
1166 guardHit, aStopAtLockedJoints, aFollowLockedSegments, aAllowSegmentSizeMismatch );
1167 }
1168
1169 int n = 0;
1170
1171 LINKED_ITEM* prev_seg = nullptr;
1172 bool originSet = false;
1173
1174 SHAPE_LINE_CHAIN& line = pl.Line();
1175
1176 for( int i = i_start + 1; i < i_end; i++ )
1177 {
1178 const VECTOR2I& p = corners[i];
1179 LINKED_ITEM* li = segs[i];
1180
1181 if( !li || li->Kind() != ITEM::ARC_T )
1182 line.Append( p );
1183
1184 if( li && prev_seg != li )
1185 {
1186 if( li->Kind() == ITEM::ARC_T )
1187 {
1188 const ARC* arc = static_cast<const ARC*>( li );
1189 const SHAPE_ARC* sa = static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) );
1190
1191 line.Append( arcReversed[i] ? sa->Reversed() : *sa );
1192 }
1193
1194 pl.Link( li );
1195
1196 // latter condition to avoid loops
1197 if( li == aSeg && aOriginSegmentIndex && !originSet )
1198 {
1199 wxASSERT( n < line.SegmentCount() ||
1200 ( n == line.SegmentCount() && li->Kind() == ITEM::SEGMENT_T ) );
1201 *aOriginSegmentIndex = line.PointCount() - 1;
1202 originSet = true;
1203 }
1204 }
1205
1206 prev_seg = li;
1207 }
1208
1209 // Remove duplicate verts, but do NOT remove colinear segments here!
1211
1212 // TODO: maintain actual segment index under simplification system
1213 if( aOriginSegmentIndex && *aOriginSegmentIndex >= pl.SegmentCount() )
1214 *aOriginSegmentIndex = pl.SegmentCount() - 1;
1215
1216 wxASSERT_MSG( pl.SegmentCount() != 0, "assembled line should never be empty" );
1217
1218 return pl;
1219}
1220
1221
1222void NODE::FindLineEnds( const LINE& aLine, JOINT& aA, JOINT& aB )
1223{
1224 aA = *FindJoint( aLine.CPoint( 0 ), &aLine );
1225 aB = *FindJoint( aLine.CLastPoint(), &aLine );
1226}
1227
1228
1229int NODE::FindLinesBetweenJoints( const JOINT& aA, const JOINT& aB, std::vector<LINE>& aLines )
1230{
1231 for( ITEM* item : aA.LinkList() )
1232 {
1233 if( item->Kind() == ITEM::SEGMENT_T || item->Kind() == ITEM::ARC_T )
1234 {
1235 LINKED_ITEM* li = static_cast<LINKED_ITEM*>( item );
1236 LINE line = AssembleLine( li );
1237
1238 if( !line.Layers().Overlaps( aB.Layers() ) )
1239 continue;
1240
1241 JOINT j_start, j_end;
1242
1243 FindLineEnds( line, j_start, j_end );
1244
1245 int id_start = line.CLine().Find( aA.Pos() );
1246 int id_end = line.CLine().Find( aB.Pos() );
1247
1248 if( id_end < id_start )
1249 std::swap( id_end, id_start );
1250
1251 if( id_start >= 0 && id_end >= 0 )
1252 {
1253 line.ClipVertexRange( id_start, id_end );
1254 aLines.push_back( line );
1255 }
1256 }
1257 }
1258
1259 return 0;
1260}
1261
1262
1264{
1265 m_index->SetDeferred( true );
1266}
1267
1268
1270{
1271 m_index->SetDeferred( false );
1272 m_index->BuildSpatialIndex();
1273}
1274
1275
1277{
1278 const SEGMENT* locked_seg = nullptr;
1279 std::vector<VVIA*> vvias;
1280
1281 for( auto& jointPair : m_joints )
1282 {
1283 JOINT joint = jointPair.second;
1284
1285 if( joint.Layers().IsMultilayer() )
1286 continue;
1287
1288 int n_seg = 0;
1289 int n_solid = 0;
1290 int n_vias = 0;
1291 int prev_w = -1;
1292 bool prev_mask = false;
1293 std::optional<int> prev_mask_margin;
1294 int max_w = -1;
1295 bool is_width_change = false;
1296 bool is_locked = false;
1297
1298 for( const ITEM* item : joint.LinkList() )
1299 {
1300 if( item->OfKind( ITEM::VIA_T ) )
1301 {
1302 n_vias++;
1303 }
1304 else if( item->OfKind( ITEM::SOLID_T ) )
1305 {
1306 n_solid++;
1307 }
1308 else if( const auto t = dyn_cast<const PNS::SEGMENT*>( item ) )
1309 {
1310 int w = t->Width();
1311 bool mask = false;
1312 std::optional<int> mask_margin;
1313
1314 if( PCB_TRACK* track = static_cast<PCB_TRACK*>( t->Parent() ) )
1315 {
1316 mask = track->HasSolderMask();
1317 mask_margin = track->GetLocalSolderMaskMargin();
1318 }
1319
1320 if( prev_w < 0 )
1321 {
1322 prev_w = w;
1323 prev_mask = mask;
1324 prev_mask_margin = mask_margin;
1325 }
1326 else if( w != prev_w || mask != prev_mask || mask_margin != prev_mask_margin )
1327 {
1328 is_width_change = true;
1329 }
1330
1331 max_w = std::max( w, max_w );
1332 prev_w = w;
1333
1334 is_locked = t->IsLocked();
1335 locked_seg = t;
1336 }
1337 }
1338
1339 if( ( is_width_change || n_seg >= 3 || is_locked ) && n_solid == 0 && n_vias == 0 )
1340 {
1341 // fixme: the hull margin here is an ugly temporary workaround. The real fix
1342 // is to use octagons for via force propagation.
1343 vvias.push_back( new VVIA( joint.Pos(), joint.Layers().Start(),
1344 max_w + 2 * PNS_HULL_MARGIN, joint.Net() ) );
1345 }
1346
1347 if( is_locked )
1348 {
1349 const VECTOR2I& secondPos = ( locked_seg->Seg().A == joint.Pos() ) ?
1350 locked_seg->Seg().B :
1351 locked_seg->Seg().A;
1352
1353 vvias.push_back( new VVIA( secondPos, joint.Layers().Start(),
1354 max_w + 2 * PNS_HULL_MARGIN, joint.Net() ) );
1355 }
1356 }
1357
1358 for( auto vvia : vvias )
1359 {
1360 Add( ItemCast<VIA>( std::unique_ptr<VVIA>( vvia ) ) );
1361 }
1362}
1363
1364
1365const JOINT* NODE::FindJoint( const VECTOR2I& aPos, int aLayer, NET_HANDLE aNet ) const
1366{
1367 JOINT::HASH_TAG tag;
1368
1369 tag.net = aNet;
1370 tag.pos = aPos;
1371
1372 JOINT_MAP::const_iterator f = m_joints.find( tag ), end = m_joints.end();
1373
1374 if( f == end && !isRoot() )
1375 {
1376 end = m_root->m_joints.end();
1377 f = m_root->m_joints.find( tag ); // m_root->FindJoint(aPos, aLayer, aNet);
1378 }
1379
1380 while( f != end )
1381 {
1382 if( f->second.Pos() == aPos && f->second.Net() == aNet && f->second.Layers().Overlaps( aLayer ) )
1383 return &f->second;
1384
1385 f++;
1386 }
1387
1388 return nullptr;
1389}
1390
1391
1392void NODE::LockJoint( const VECTOR2I& aPos, const ITEM* aItem, bool aLock )
1393{
1394 JOINT& jt = touchJoint( aPos, aItem->Layers(), aItem->Net() );
1395 jt.Lock( aLock );
1396}
1397
1398
1399JOINT& NODE::touchJoint( const VECTOR2I& aPos, const PNS_LAYER_RANGE& aLayers, NET_HANDLE aNet )
1400{
1401 JOINT::HASH_TAG tag;
1402
1403 tag.pos = aPos;
1404 tag.net = aNet;
1405
1406 // try to find the joint in this node.
1407 JOINT_MAP::iterator f = m_joints.find( tag );
1408
1409 std::pair<JOINT_MAP::iterator, JOINT_MAP::iterator> range;
1410
1411 // not found and we are not root? find in the root and copy results here.
1412 if( f == m_joints.end() && !isRoot() )
1413 {
1414 range = m_root->m_joints.equal_range( tag );
1415
1416 for( f = range.first; f != range.second; ++f )
1417 m_joints.insert( *f );
1418 }
1419
1420 // now insert and combine overlapping joints
1421 JOINT jt( aPos, aLayers, aNet );
1422
1423 bool merged;
1424
1425 do
1426 {
1427 merged = false;
1428 range = m_joints.equal_range( tag );
1429
1430 if( range.first == m_joints.end() )
1431 break;
1432
1433 for( f = range.first; f != range.second; ++f )
1434 {
1435 if( aLayers.Overlaps( f->second.Layers() ) )
1436 {
1437 jt.Merge( f->second );
1438 m_joints.erase( f );
1439 merged = true;
1440 break;
1441 }
1442 }
1443 } while( merged );
1444
1445 return m_joints.insert( TagJointPair( tag, jt ) )->second;
1446}
1447
1448
1449void JOINT::Dump() const
1450{
1451 wxLogTrace( wxT( "PNS" ), wxT( "joint layers %d-%d, net %d, pos %s, links: %d" ),
1452 m_layers.Start(),
1453 m_layers.End(),
1454 m_tag.net,
1455 m_tag.pos.Format().c_str(),
1456 LinkCount() );
1457}
1458
1459
1460void NODE::linkJoint( const VECTOR2I& aPos, const PNS_LAYER_RANGE& aLayers, NET_HANDLE aNet,
1461 ITEM* aWhere )
1462{
1463 JOINT& jt = touchJoint( aPos, aLayers, aNet );
1464
1465 jt.Link( aWhere );
1466}
1467
1468
1469void NODE::unlinkJoint( const VECTOR2I& aPos, const PNS_LAYER_RANGE& aLayers, NET_HANDLE aNet,
1470 ITEM* aWhere )
1471{
1472 // fixme: remove dangling joints
1473 JOINT& jt = touchJoint( aPos, aLayers, aNet );
1474
1475 jt.Unlink( aWhere );
1476}
1477
1478
1479void NODE::Dump( bool aLong )
1480{
1481#if 0
1482 std::unordered_set<SEGMENT*> all_segs;
1484
1485 for( i = m_items.begin(); i != m_items.end(); i++ )
1486 {
1487 if( (*i)->GetKind() == ITEM::SEGMENT_T )
1488 all_segs.insert( static_cast<SEGMENT*>( *i ) );
1489 }
1490
1491 if( !isRoot() )
1492 {
1493 for( i = m_root->m_items.begin(); i != m_root->m_items.end(); i++ )
1494 {
1495 if( (*i)->GetKind() == ITEM::SEGMENT_T && !overrides( *i ) )
1496 all_segs.insert( static_cast<SEGMENT*>(*i) );
1497 }
1498 }
1499
1500 JOINT_MAP::iterator j;
1501
1502 if( aLong )
1503 {
1504 for( j = m_joints.begin(); j != m_joints.end(); ++j )
1505 {
1506 wxLogTrace( wxT( "PNS" ), wxT( "joint : %s, links : %d\n" ),
1507 j->second.GetPos().Format().c_str(), j->second.LinkCount() );
1508 JOINT::LINKED_ITEMS::const_iterator k;
1509
1510 for( k = j->second.GetLinkList().begin(); k != j->second.GetLinkList().end(); ++k )
1511 {
1512 const ITEM* m_item = *k;
1513
1514 switch( m_item->GetKind() )
1515 {
1516 case ITEM::SEGMENT_T:
1517 {
1518 const SEGMENT* seg = static_cast<const SEGMENT*>( m_item );
1519 wxLogTrace( wxT( "PNS" ), wxT( " -> seg %s %s\n" ),
1520 seg->GetSeg().A.Format().c_str(),
1521 seg->GetSeg().B.Format().c_str() );
1522 break;
1523 }
1524
1525 default:
1526 break;
1527 }
1528 }
1529 }
1530 }
1531
1532 int lines_count = 0;
1533
1534 while( !all_segs.empty() )
1535 {
1536 SEGMENT* s = *all_segs.begin();
1537 LINE* l = AssembleLine( s );
1538
1539 LINE::LinkedSegments* seg_refs = l->GetLinkedSegments();
1540
1541 if( aLong )
1542 {
1543 wxLogTrace( wxT( "PNS" ), wxT( "Line: %s, net %d " ),
1544 l->GetLine().Format().c_str(), l->GetNet() );
1545 }
1546
1547 for( std::vector<SEGMENT*>::iterator j = seg_refs->begin(); j != seg_refs->end(); ++j )
1548 {
1549 wxLogTrace( wxT( "PNS" ), wxT( "%s " ), (*j)->GetSeg().A.Format().c_str() );
1550
1551 if( j + 1 == seg_refs->end() )
1552 wxLogTrace( wxT( "PNS" ), wxT( "%s\n" ), (*j)->GetSeg().B.Format().c_str() );
1553
1554 all_segs.erase( *j );
1555 }
1556
1557 lines_count++;
1558 }
1559
1560 wxLogTrace( wxT( "PNS" ), wxT( "Local joints: %d, lines : %d \n" ),
1561 m_joints.size(), lines_count );
1562#endif
1563}
1564
1565
1567{
1568 if( isRoot() )
1569 return;
1570
1571 if( m_override.size() )
1572 aRemoved.reserve( m_override.size() );
1573
1574 if( m_index->Size() )
1575 aAdded.reserve( m_index->Size() );
1576
1577 for( ITEM* item : m_override )
1578 aRemoved.push_back( item );
1579
1580 for( ITEM* item : *m_index )
1581 aAdded.push_back( item );
1582}
1583
1584
1586{
1587 // copy the kids as the NODE destructor erases the item from the parent node.
1588 std::set<NODE*> kids = m_children;
1589
1590 for( NODE* node : kids )
1591 {
1592 node->releaseChildren();
1593 delete node;
1594 }
1595}
1596
1597
1599{
1600 if( !isRoot() )
1601 return;
1602
1603 std::vector<const ITEM*> toDelete;
1604 toDelete.reserve( m_garbageItems.size() );
1605
1606 std::vector<const ITEM*> holes;
1607
1608 for( ITEM* item : m_garbageItems )
1609 {
1610 if( !item->BelongsTo( this ) )
1611 {
1612 toDelete.push_back( item );
1613
1614 if( item->HasHole() )
1615 holes.push_back( item->Hole() );
1616 }
1617 }
1618
1619 if( m_ruleResolver )
1620 {
1621 m_ruleResolver->ClearCacheForItems( toDelete );
1622 m_ruleResolver->ClearCacheForItems( holes );
1623 }
1624
1625 for( const ITEM* item : toDelete)
1626 {
1627 delete item;
1628 }
1629
1630 m_garbageItems.clear();
1631}
1632
1633
1634void NODE::Commit( NODE* aNode )
1635{
1636 if( aNode->isRoot() )
1637 return;
1638
1639 for( ITEM* item : aNode->m_override )
1640 Remove( item );
1641
1642 for( ITEM* item : *aNode->m_index )
1643 {
1644 if( item->HasHole() )
1645 {
1646 item->Hole()->SetOwner( item );
1647 }
1648
1649 item->SetRank( -1 );
1650 item->Unmark();
1651 add( item );
1652 }
1653
1656}
1657
1658
1660{
1662}
1663
1664
1665void NODE::AllItemsInNet( NET_HANDLE aNet, std::set<ITEM*>& aItems, int aKindMask )
1666{
1667 INDEX::NET_ITEMS_LIST* l_cur = m_index->GetItemsForNet( aNet );
1668
1669 if( l_cur )
1670 {
1671 for( ITEM* item : *l_cur )
1672 {
1673 if( item->OfKind( aKindMask ) && item->IsRoutable() )
1674 aItems.insert( item );
1675 }
1676 }
1677
1678 if( !isRoot() )
1679 {
1680 INDEX::NET_ITEMS_LIST* l_root = m_root->m_index->GetItemsForNet( aNet );
1681
1682 if( l_root )
1683 {
1684 for( ITEM* item : *l_root )
1685 {
1686 if( !Overrides( item ) && item->OfKind( aKindMask ) && item->IsRoutable() )
1687 aItems.insert( item );
1688 }
1689 }
1690 }
1691}
1692
1693
1694void NODE::ClearRanks( int aMarkerMask )
1695{
1696 for( ITEM* item : *m_index )
1697 {
1698 item->SetRank( -1 );
1699 item->Mark( item->Marker() & ~aMarkerMask );
1700 }
1701}
1702
1703
1704void NODE::RemoveByMarker( int aMarker )
1705{
1706 std::vector<ITEM*> garbage;
1707
1708 for( ITEM* item : *m_index )
1709 {
1710 if( item->Marker() & aMarker )
1711 garbage.emplace_back( item );
1712 }
1713
1714 for( ITEM* item : garbage )
1715 Remove( item );
1716}
1717
1718
1720 NET_HANDLE aNet )
1721{
1722 const JOINT* jtStart = FindJoint( A, lr.Start(), aNet );
1723
1724 if( !jtStart )
1725 return nullptr;
1726
1727 for( ITEM* item : jtStart->LinkList() )
1728 {
1729 if( item->OfKind( ITEM::SEGMENT_T ) )
1730 {
1731 SEGMENT* seg2 = (SEGMENT*)item;
1732
1733 const VECTOR2I a2( seg2->Seg().A );
1734 const VECTOR2I b2( seg2->Seg().B );
1735
1736 if( seg2->Layers().Start() == lr.Start()
1737 && ( ( A == a2 && B == b2 ) || ( A == b2 && B == a2 ) ) )
1738 {
1739 return seg2;
1740 }
1741 }
1742 }
1743
1744 return nullptr;
1745}
1746
1747
1749{
1750 return findRedundantSegment( aSeg->Seg().A, aSeg->Seg().B, aSeg->Layers(), aSeg->Net() );
1751}
1752
1753
1755 NET_HANDLE aNet )
1756{
1757 const JOINT* jtStart = FindJoint( A, lr.Start(), aNet );
1758
1759 if( !jtStart )
1760 return nullptr;
1761
1762 for( ITEM* item : jtStart->LinkList() )
1763 {
1764 if( item->OfKind( ITEM::ARC_T ) )
1765 {
1766 ARC* seg2 = static_cast<ARC*>( item );
1767
1768 const VECTOR2I a2( seg2->Anchor( 0 ) );
1769 const VECTOR2I b2( seg2->Anchor( 1 ) );
1770
1771 if( seg2->Layers().Start() == lr.Start()
1772 && ( ( A == a2 && B == b2 ) || ( A == b2 && B == a2 ) ) )
1773 {
1774 return seg2;
1775 }
1776 }
1777 }
1778
1779 return nullptr;
1780}
1781
1782
1784{
1785 return findRedundantArc( aArc->Anchor( 0 ), aArc->Anchor( 1 ), aArc->Layers(), aArc->Net() );
1786}
1787
1788
1789int NODE::QueryJoints( const BOX2I& aBox, std::vector<JOINT*>& aJoints, PNS_LAYER_RANGE aLayerMask,
1790 int aKindMask )
1791{
1792 int n = 0;
1793
1794 aJoints.clear();
1795
1796 for( JOINT_MAP::value_type& j : m_joints )
1797 {
1798 if( !j.second.Layers().Overlaps( aLayerMask ) )
1799 continue;
1800
1801 if( aBox.Contains( j.second.Pos() ) && j.second.LinkCount( aKindMask ) )
1802 {
1803 aJoints.push_back( &j.second );
1804 n++;
1805 }
1806 }
1807
1808 if( isRoot() )
1809 return n;
1810
1811 for( JOINT_MAP::value_type& j : m_root->m_joints )
1812 {
1813 if( !Overrides( &j.second ) && j.second.Layers().Overlaps( aLayerMask ) )
1814 {
1815 if( aBox.Contains( j.second.Pos() ) && j.second.LinkCount( aKindMask ) )
1816 {
1817 aJoints.push_back( &j.second );
1818 n++;
1819 }
1820 }
1821 }
1822
1823 return n;
1824}
1825
1826
1828{
1829 if( aParent && aParent->IsConnected() )
1830 {
1831 const BOARD_CONNECTED_ITEM* cItem = static_cast<const BOARD_CONNECTED_ITEM*>( aParent );
1832 INDEX::NET_ITEMS_LIST* l_cur = m_index->GetItemsForNet( cItem->GetNet() );
1833
1834 if( l_cur )
1835 {
1836 for( ITEM* item : *l_cur )
1837 {
1838 if( item->Parent() == aParent )
1839 return item;
1840 }
1841 }
1842 }
1843
1844 return nullptr;
1845}
1846
1847
1848std::vector<ITEM*> NODE::FindItemsByParent( const BOARD_ITEM* aParent )
1849{
1850 std::vector<ITEM*> ret;
1851
1852 for( ITEM* item : *m_index )
1853 {
1854 if( item->Parent() == aParent )
1855 ret.push_back( item );
1856 }
1857
1858 return ret;
1859}
1860
1861
1862VIA* NODE::FindViaByHandle ( const VIA_HANDLE& handle ) const
1863{
1864 const JOINT* jt = FindJoint( handle.pos, handle.layers.Start(), handle.net );
1865
1866 if( !jt )
1867 return nullptr;
1868
1869 for( ITEM* item : jt->LinkList() )
1870 {
1871 if( item->OfKind( ITEM::VIA_T ) )
1872 {
1873 if( item->Net() == handle.net && item->Layers().Overlaps(handle.layers) )
1874 return static_cast<VIA*>( item );
1875 }
1876 }
1877
1878 return nullptr;
1879}
1880
1881}
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
NETINFO_ITEM * GetNet() const
Return NETINFO_ITEM object for a given item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:173
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
CORNER_MODE
Corner modes.
Definition direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition direction45.h:70
virtual VECTOR2I Anchor(int n) const override
Definition pns_arc.h:100
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition pns_arc.h:78
ITEM * ParentPadVia() const override
Definition pns_hole.h:72
INDEX.
Definition pns_index.h:47
std::list< ITEM * > NET_ITEMS_LIST
Definition pns_index.h:49
int Size() const
Returns number of items stored in the index.
Definition pns_index.h:144
void Add(const LINE &aLine)
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
const std::vector< ITEM * > & CItems() const
Definition pns_itemset.h:96
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 std::string Format() const
Definition pns_item.cpp:338
virtual void Unmark(int aMarker=-1) const
Definition pns_item.h:266
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition pns_item.h:246
void SetSourceItem(BOARD_ITEM *aSourceItem)
Definition pns_item.h:201
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
PNS_LAYER_RANGE m_layers
Definition pns_item.h:327
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
void SetNet(NET_HANDLE aNet)
Definition pns_item.h:209
BOARD_ITEM * GetSourceItem() const
Definition pns_item.h:202
virtual void SetRank(int aRank)
Definition pns_item.h:269
virtual int Layer() const
Definition pns_item.h:216
void SetParent(BOARD_ITEM *aParent)
Definition pns_item.h:191
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition pns_item.cpp:305
bool OfKind(int aKindMask) const
Definition pns_item.h:181
bool IsVirtual() const
Definition pns_item.h:299
bool IsRoutable() const
Definition pns_item.h:288
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:272
bool LayersOverlap(const ITEM *aOther) const
Return true if the set of layers spanned by aOther overlaps our layers.
Definition pns_item.h:221
virtual HOLE * Hole() const
Definition pns_item.h:308
virtual bool HasHole() const
Definition pns_item.h:307
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:307
NET_HANDLE Net() const override
Definition pns_joint.h:302
int LinkCount(int aMask=-1) const
Definition pns_joint.h:322
void Lock(bool aLock=true)
Definition pns_joint.h:356
void Link(ITEM *aItem)
Link the joint to a given board item (when it's added to the NODE).
Definition pns_joint.h:215
void Dump() const
LINKED_ITEM * NextSegment(LINKED_ITEM *aCurrent, bool aAllowLockedSegs=false) const
For trivial joints, return the segment adjacent to (aCurrent).
Definition pns_joint.h:240
HASH_TAG m_tag
hash tag for unordered_multimap
Definition pns_joint.h:368
bool IsLocked() const
Definition pns_joint.h:361
void Merge(const JOINT &aJoint)
Definition pns_joint.h:334
bool Unlink(ITEM *aItem)
Unlink a given board item from the joint (upon its removal from a NODE)
Definition pns_joint.h:228
const VECTOR2I & Pos() const
Definition pns_joint.h:297
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
void ClipVertexRange(int aStart, int aEnd)
Clip the line to a given range of vertices.
const VECTOR2I & CPoint(int aIdx) const
Return the aIdx-th point of the line.
Definition pns_line.h:157
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:149
const VECTOR2I & CLastPoint() const
Definition pns_line.h:158
SHAPE_LINE_CHAIN & Line()
Modifiable accessor to the underlying shape.
Definition pns_line.h:148
VIA & Via()
Definition pns_line.h:212
int SegmentCount() const
Definition pns_line.h:151
void SetWidth(int aWidth)
Set line width.
Definition pns_line.h:162
bool EndsWithVia() const
Definition pns_line.h:204
int Width() const
Return line width.
Definition pns_line.h:169
virtual int Width() const
Keep the router "world" - i.e.
Definition pns_node.h:244
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:163
void RemoveByMarker(int aMarker)
NODE * m_root
root node of the whole hierarchy
Definition pns_node.h:598
int FindLinesBetweenJoints(const JOINT &aA, const JOINT &aB, std::vector< LINE > &aLines)
Find all lines between a pair of joints. Used by the loop removal procedure.
void BeginBulkAdd()
Defer spatial index insertion during bulk population.
std::vector< ITEM * > ITEM_VECTOR
Definition pns_node.h:255
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the expected clearance between items a and b.
Definition pns_node.cpp:149
void addSolid(SOLID *aSeg)
Helpers for adding/removing items.
Definition pns_node.cpp:607
void Replace(ITEM *aOldItem, std::unique_ptr< ITEM > aNewItem)
Replace an item with another one.
Definition pns_node.cpp:957
bool Overrides(ITEM *aItem) const
Check if this branch contains an updated version of the m_item from the root branch.
Definition pns_node.h:515
void removeSegmentIndex(SEGMENT *aSeg)
Definition pns_node.cpp:862
void rebuildJoint(const JOINT *aJoint, const ITEM *aItem)
Definition pns_node.cpp:876
void GetUpdatedItems(ITEM_VECTOR &aRemoved, ITEM_VECTOR &aAdded)
Return the list of items removed and added in this branch with respect to the root branch.
void addSegment(SEGMENT *aSeg)
Definition pns_node.cpp:742
std::vector< std::unique_ptr< SHAPE > > m_edgeExclusions
Definition pns_node.h:610
ARC * findRedundantArc(const VECTOR2I &A, const VECTOR2I &B, const PNS_LAYER_RANGE &lr, NET_HANDLE aNet)
void releaseChildren()
JOINT_MAP::value_type TagJointPair
Definition pns_node.h:592
void addVia(VIA *aVia)
Definition pns_node.cpp:630
bool QueryEdgeExclusions(const VECTOR2I &aPos) const
Definition pns_node.cpp:803
void doRemove(ITEM *aItem)
Definition pns_node.cpp:815
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition pns_node.cpp:498
const JOINT * FindJoint(const VECTOR2I &aPos, int aLayer, NET_HANDLE aNet) const
Search for a joint at a given position, layer and belonging to given net.
void addHole(HOLE *aHole)
Definition pns_node.cpp:648
std::optional< OBSTACLE > OPT_OBSTACLE
Definition pns_node.h:254
void unlinkJoint(const VECTOR2I &aPos, const PNS_LAYER_RANGE &aLayers, NET_HANDLE aNet, ITEM *aWhere)
Unlink an item from a joint.
std::unordered_set< ITEM * > m_garbageItems
Definition pns_node.h:612
void Dump(bool aLong=false)
Print the contents and joints structure.
void releaseGarbage()
void addArc(ARC *aVia)
Definition pns_node.cpp:771
void FindLineEnds(const LINE &aLine, JOINT &aA, JOINT &aB)
Find the joints corresponding to the ends of line aLine.
RULE_RESOLVER * GetRuleResolver() const
Definition pns_node.h:293
JOINT & touchJoint(const VECTOR2I &aPos, const PNS_LAYER_RANGE &aLayers, NET_HANDLE aNet)
Try to find matching joint and creates a new one if not found.
bool Add(std::unique_ptr< SEGMENT > aSegment, bool aAllowRedundant=false)
Add an item to the current node.
Definition pns_node.cpp:753
void FixupVirtualVias()
int QueryJoints(const BOX2I &aBox, std::vector< JOINT * > &aJoints, PNS_LAYER_RANGE aLayerMask=PNS_LAYER_RANGE::All(), int aKindMask=ITEM::ANY_T)
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:256
const LINE AssembleLine(LINKED_ITEM *aSeg, int *aOriginSegmentIndex=nullptr, bool aStopAtLockedJoints=false, bool aFollowLockedSegments=false, bool aAllowSegmentSizeMismatch=true)
Follow the joint map to assemble a line connecting two non-trivial joints starting from segment aSeg.
INDEX * m_index
Geometric/Net index of the items.
Definition pns_node.h:606
std::unordered_set< ITEM * > m_override
hash of root's items that have been changed in this node
Definition pns_node.h:601
void AllItemsInNet(NET_HANDLE aNet, std::set< ITEM * > &aItems, int aKindMask=-1)
OPT_OBSTACLE NearestObstacle(const LINE *aLine, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS())
Follow the line in search of an obstacle that is nearest to the starting to the line's starting point...
Definition pns_node.cpp:304
void LockJoint(const VECTOR2I &aPos, const ITEM *aItem, bool aLock)
void removeArcIndex(ARC *aVia)
Definition pns_node.cpp:869
void AddEdgeExclusion(std::unique_ptr< SHAPE > aShape)
Definition pns_node.cpp:797
int QueryColliding(const ITEM *aItem, OBSTACLES &aObstacles, const COLLISION_SEARCH_OPTIONS &aOpts=COLLISION_SEARCH_OPTIONS()) const
Find items colliding (closer than clearance) with the item aItem.
Definition pns_node.cpp:273
int m_maxClearance
worst case item-item clearance
Definition pns_node.h:604
bool isRoot() const
Definition pns_node.h:570
VIA * FindViaByHandle(const VIA_HANDLE &handle) const
void removeViaIndex(VIA *aVia)
Definition pns_node.cpp:937
void add(ITEM *aItem, bool aAllowRedundant=false)
Definition pns_node.cpp:664
void followLine(LINKED_ITEM *aCurrent, bool aScanDirection, int &aPos, int aLimit, VECTOR2I *aCorners, LINKED_ITEM **aSegments, bool *aArcReversed, bool &aGuardHit, bool aStopAtLockedJoints, bool aFollowLockedSegments, bool aAllowSegmentSizeMismatch)
Scan the joint map, forming a line starting from segment (current).
void KillChildren()
Destroy all child nodes. Applicable only to the root node.
void removeSolidIndex(SOLID *aSeg)
Definition pns_node.cpp:945
int m_depth
depth of the node (number of parent nodes in the inheritance chain)
Definition pns_node.h:607
std::set< NODE * > m_children
list of nodes branched from this one
Definition pns_node.h:599
ITEM * FindItemByParent(const BOARD_ITEM *aParent)
JOINT_MAP m_joints
hash table with the joints, linking the items.
Definition pns_node.h:594
std::vector< ITEM * > FindItemsByParent(const BOARD_ITEM *aParent)
NODE * m_parent
node this node was branched from
Definition pns_node.h:597
void ClearRanks(int aMarkerMask=MK_HEAD|MK_VIOLATION)
void Remove(ARC *aArc)
Remove an item from this branch.
Definition pns_node.cpp:997
void unlinkParent()
Definition pns_node.cpp:197
void Commit(NODE *aNode)
Apply the changes from a given branch (aNode) to the root branch.
RULE_RESOLVER * m_ruleResolver
Design rules resolver.
Definition pns_node.h:605
SEGMENT * findRedundantSegment(const VECTOR2I &A, const VECTOR2I &B, const PNS_LAYER_RANGE &lr, NET_HANDLE aNet)
void linkJoint(const VECTOR2I &aPos, const PNS_LAYER_RANGE &aLayers, NET_HANDLE aNet, ITEM *aWhere)
Touch a joint and links it to an m_item.
void FinalizeBulkAdd()
Build the spatial index from all items added since BeginBulkAdd().
const ITEM_SET HitTest(const VECTOR2I &aPoint) const
Find all items that contain the point aPoint.
Definition pns_node.cpp:578
OBSTACLE_VISITOR(const ITEM *aItem)
Definition pns_node.cpp:206
const NODE * m_node
node we are searching in (either root or a branch)
Definition pns_node.h:210
const ITEM * m_item
the item we are looking for collisions with
Definition pns_node.h:208
bool visit(ITEM *aCandidate)
Definition pns_node.cpp:221
std::optional< int > m_layerContext
Definition pns_node.h:212
void SetWorld(const NODE *aNode, const NODE *aOverride=nullptr)
Definition pns_node.cpp:214
const NODE * m_override
node that overrides root entries
Definition pns_node.h:211
void SetOwner(const ITEM_OWNER *aOwner)
Set the node that owns this item.
Definition pns_item.h:77
bool BelongsTo(const ITEM_OWNER *aNode) const
Definition pns_item.h:82
const ITEM_OWNER * Owner() const
Return the owner of this item, or NULL if there's none.
Definition pns_item.h:72
ROUTING_SETTINGS & Settings()
Definition pns_router.h:239
static ROUTER * GetInstance()
DIRECTION_45::CORNER_MODE GetCornerMode() const
virtual const SHAPE_LINE_CHAIN & HullCache(const ITEM *aItem, int aClearance, int aWalkaroundThickness, int aLayer)
Definition pns_node.h:178
virtual const std::string Format() const override
const SEG & Seg() const
virtual bool HasHole() const override
Definition pns_solid.h:153
virtual HOLE * Hole() const override
Definition pns_solid.h:154
const VECTOR2I & Pos() const
Definition pns_solid.h:119
const VECTOR2I & Pos() const
Definition pns_via.h:206
virtual HOLE * Hole() const override
Definition pns_via.h:340
virtual bool HasHole() const override
Definition pns_via.h:339
Represent a contiguous set of PCB layers.
int Start() const
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
bool IsMultilayer() const
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
SHAPE_ARC Reversed() const
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
int PathLength(const VECTOR2I &aP, int aIndex=-1) const
Compute the walk path length from the beginning of the line chain and the point aP belonging to our l...
void RemoveDuplicatePoints()
Remove the duplicate points from the line chain.
size_t ArcCount() const
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
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
Push and Shove diff pair dimensions (gap) settings dialog.
std::unique_ptr< T > ItemCast(std::unique_ptr< S > aPtr)
Definition pns_item.h:340
void * NET_HANDLE
Definition pns_item.h:55
void HullIntersection(const SHAPE_LINE_CHAIN &hull, const SHAPE_LINE_CHAIN &line, std::vector< SHAPE_LINE_CHAIN::INTERSECTION > &ips)
#define PNS_HULL_MARGIN
Definition pns_line.h:45
CITER next(CITER it)
Definition ptree.cpp:120
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
std::set< OBSTACLE > & obstacles
Definition pns_node.h:136
bool operator()(ITEM *aItem) override
Definition pns_node.cpp:563
const VECTOR2I & m_point
Definition pns_node.cpp:551
HIT_VISITOR(ITEM_SET &aTab, const VECTOR2I &aPoint)
Definition pns_node.cpp:553
virtual ~HIT_VISITOR()
Definition pns_node.cpp:559
ITEM_SET & m_items
Definition pns_node.cpp:550
Joints are hashed by their position, layers and net.
Definition pns_joint.h:48
bool operator()(ITEM *aCandidate) override
Definition pns_node.cpp:247
COLLISION_SEARCH_CONTEXT * m_ctx
Definition pns_node.cpp:235
DEFAULT_OBSTACLE_VISITOR(COLLISION_SEARCH_CONTEXT *aCtx, const ITEM *aItem)
Definition pns_node.cpp:237
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
int m_distFirst
... and the distance thereof
Definition pns_node.h:95
int m_maxFanoutWidth
worst case (largest) width of the tracks connected to the item
Definition pns_node.h:96
ITEM * m_head
Line we search collisions against.
Definition pns_node.h:90
VECTOR2I m_ipFirst
First intersection between m_head and m_hull.
Definition pns_node.h:92
ITEM * m_item
Item found to be colliding with m_head.
Definition pns_node.h:91
VECTOR2I pos
Definition pns_via.h:55
NET_HANDLE net
Definition pns_via.h:57
PNS_LAYER_RANGE layers
Definition pns_via.h:56
Represent an intersection between two line segments.
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:27
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708