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