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