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