KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_router.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <cstdio>
23#include <memory>
24#include <vector>
25
27
28#include <advanced_config.h>
30
31#include <pcb_painter.h>
32#include <pad.h>
33#include <zone.h>
34
35#include <geometry/shape.h>
36
37#include "pns_node.h"
38#include "pns_line_placer.h"
39#include "pns_line.h"
40#include "pns_solid.h"
41#include "pns_utils.h"
42#include "pns_router.h"
43#include "pns_shove.h"
44#include "pns_dragger.h"
45#include "pns_multi_dragger.h"
47#include "pns_topology.h"
49#include "pns_meander_placer.h"
52#include "pns_utils.h"
53#include "router_preview_item.h"
54
55namespace PNS {
56
57// an ugly singleton for drawing debug items within the router context.
58// To be fixed sometime in the future.
60
62{
63 theRouter = this;
64
65 m_state = IDLE;
67
68 m_logger = nullptr;
69
70 if( ADVANCED_CFG::GetCfg().m_EnableRouterDump )
71 m_logger = new LOGGER;
72
73 // Initialize all other variables:
74 m_lastNode = nullptr;
75 m_iterLimit = 0;
76 m_settings = nullptr;
77 m_iface = nullptr;
78 m_visibleViewArea.SetMaximum();
79}
80
81
83{
84 return theRouter;
85}
86
87
89{
90 ClearWorld();
91 theRouter = nullptr;
92 delete m_logger;
93}
94
95
97{
98 ClearWorld();
99
100 m_world = std::make_unique<NODE>();
101 m_world->BeginBulkAdd();
102 m_iface->SyncWorld( m_world.get() );
103 m_world->FinalizeBulkAdd();
104 m_world->FixupVirtualVias();
105}
106
107
109{
110 if( m_world )
111 {
112 m_world->SetRuleResolver( nullptr );
113 m_world->KillChildren();
114 m_world.reset();
115 }
116
117 m_placer.reset();
118}
119
120
122{
123 return m_state != IDLE;
124}
125
126
127const ITEM_SET ROUTER::QueryHoverItems( const VECTOR2I& aP, int aSlopRadius )
128{
129 NODE* node = m_placer ? m_placer->CurrentNode() : m_world.get();
130 PNS::ITEM_SET ret;
131
132 wxCHECK( node, ret );
133
135 {
136 NODE::OBSTACLES obs;
137 SEGMENT test( SEG( aP, aP ), nullptr );
139
140 test.SetWidth( Sizes().DiffPairGap() + 2 * Sizes().DiffPairWidth() );
141 test.SetLayers( PNS_LAYER_RANGE::All() );
142
143 opts.m_differentNetsOnly = false;
144 node->QueryColliding( &test, obs, opts );
145
146 DIFF_PAIR_PLACER* dpPlacer = static_cast<DIFF_PAIR_PLACER*>( Placer() );
147 auto currentDP = dpPlacer->CurrentTrace();
148
149 int distP = std::numeric_limits<int>::max();
150 int distN = std::numeric_limits<int>::max();
151
152 ITEM* best = nullptr;
153
154 for( const OBSTACLE& obstacle : obs )
155 {
156 NET_HANDLE netP, netN;
157
158 if( m_iface->GetRuleResolver()->DpNetPair( obstacle.m_item, netP, netN ) )
159 {
160 int dist = obstacle.m_item->Shape( obstacle.m_item->Layer() )->Distance( aP );
161
162 if( dist <= 0 )
163 {
164 ret.Add( obstacle.m_item, false );
165 }
166
167 int polarity = m_iface->GetRuleResolver()->DpNetPolarity( obstacle.m_item->Net() );
168
169 if( polarity > 0 )
170 {
171 if( dist < distP )
172 {
173 distP = dist;
174 best = obstacle.m_item;
175 }
176 }
177 else
178 {
179 if( dist < distN )
180 {
181 distN = dist;
182 if( distN <= distP )
183 best = obstacle.m_item;
184 }
185 }
186 }
187 }
188
189 if( distP < Sizes().DiffPairGap() && distN < Sizes().DiffPairGap() )
190 {
191 ret.Add( best, false );
192 return ret;
193 }
194
195 return ret;
196 }
197
198 if( aSlopRadius > 0 )
199 {
200 NODE::OBSTACLES obs;
201 SEGMENT test( SEG( aP, aP ), nullptr );
203
204 test.SetWidth( 1 );
205 test.SetLayers( PNS_LAYER_RANGE::All() );
206
207 opts.m_differentNetsOnly = false;
208 opts.m_overrideClearance = aSlopRadius;
209
210 node->QueryColliding( &test, obs, opts );
211
212 for( const OBSTACLE& obstacle : obs )
213 ret.Add( obstacle.m_item, false );
214
215 return ret;
216 }
217 else
218 {
219 return node->HitTest( aP );
220 }
221}
222
223
224bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM* aItem, int aDragMode )
225{
226 m_leaderSegments.clear();
227 return StartDragging( aP, ITEM_SET( aItem ), aDragMode );
228}
229
230
231bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM_SET aStartItems, int aDragMode )
232{
233 m_leaderSegments.clear();
234 SetFailureReason( wxEmptyString );
235
236 if( aStartItems.Empty() )
237 return false;
238
240
241 if( aStartItems.Count( ITEM::SOLID_T ) == aStartItems.Size() )
242 {
243 m_dragger = std::make_unique<COMPONENT_DRAGGER>( this );
245 }
246 // more than 1 track segment or arc to drag? launch the multisegment dragger
247 else if( aStartItems.Count( ITEM::SEGMENT_T | ITEM::ARC_T ) > 1 )
248 {
249 m_dragger = std::make_unique<MULTI_DRAGGER>( this );
251 }
252 else
253 {
254 m_dragger = std::make_unique<DRAGGER>( this );
256 }
257
258 m_dragger->SetMode( static_cast<PNS::DRAG_MODE>( aDragMode ) );
259 m_dragger->SetWorld( m_world.get() );
260 m_dragger->SetLogger( m_logger );
261 m_dragger->SetDebugDecorator( m_iface->GetDebugDecorator() );
262
263 if( m_logger )
264 m_logger->Clear();
265
266 if( m_logger )
267 {
268 if( aStartItems.Size() == 1 )
269 m_logger->Log( LOGGER::EVT_START_DRAG, aP, aStartItems[0] );
270 else if( aStartItems.Size() > 1 )
271 m_logger->LogM( LOGGER::EVT_START_MULTIDRAG, aP, aStartItems.Items() ); // fixme default args
272 }
273
274 if( m_dragger->Start( aP, aStartItems ) )
275 {
276 return true;
277 }
278 else
279 {
280 m_dragger.reset();
281 m_state = IDLE;
282 return false;
283 }
284}
285
286
287bool ROUTER::isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aStartItem, int aLayer )
288{
289 if( Settings().AllowDRCViolations() )
290 return true;
291
293 {
294 if( m_sizes.DiffPairGap() < m_sizes.MinClearance() )
295 {
296 SetFailureReason( _( "Diff pair gap is less than board minimum clearance." ) );
297 return false;
298 }
299 }
300
301 ITEM_SET candidates = QueryHoverItems( aWhere );
302 wxString failureReason;
303
304 for( ITEM* item : candidates.Items() )
305 {
306 // Edge cuts are put on all layers, but they're not *really* on all layers
307 if( item->BoardItem() && item->BoardItem()->GetLayer() == Edge_Cuts )
308 continue;
309
310 if( !item->Layers().Overlaps( aLayer ) )
311 continue;
312
313 if( item->IsRoutable() )
314 {
315 failureReason = wxEmptyString;
316 break;
317 }
318 else
319 {
320 BOARD_ITEM* parent = item->BoardItem();
321
322 switch( parent->Type() )
323 {
324 case PCB_PAD_T:
325 {
326 PAD* pad = static_cast<PAD*>( parent );
327
328 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
329 failureReason = _( "Cannot start routing from a non-plated hole." );
330 }
331 break;
332
333 case PCB_ZONE_T:
334 {
335 ZONE* zone = static_cast<ZONE*>( parent );
336
337 if( !zone->HasKeepoutParametersSet() )
338 break;
339
340 if( !zone->GetZoneName().IsEmpty() )
341 {
342 failureReason = wxString::Format( _( "Rule area '%s' disallows tracks." ),
343 zone->GetZoneName() );
344 }
345 else
346 {
347 failureReason = _( "Rule area disallows tracks." );
348 }
349 }
350 break;
351
352 case PCB_FIELD_T:
353 case PCB_TEXT_T:
354 case PCB_TEXTBOX_T:
355 failureReason = _( "Cannot start routing from a text item." );
356 break;
357
358 default:
359 break;
360 }
361 }
362 }
363
364 if( !failureReason.IsEmpty() )
365 {
366 SetFailureReason( failureReason );
367 return false;
368 }
369
370 VECTOR2I startPoint = aWhere;
371
373 {
374 SHAPE_LINE_CHAIN dummyStartSeg;
375 LINE dummyStartLine;
376
377 dummyStartSeg.Append( startPoint );
378 dummyStartSeg.Append( startPoint, true );
379
380 dummyStartLine.SetShape( dummyStartSeg );
381 dummyStartLine.SetLayer( aLayer );
382 dummyStartLine.SetNet( aStartItem ? aStartItem->Net() : 0 );
383 dummyStartLine.SetWidth( m_sizes.TrackWidth() );
384
385 if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
386 {
387 // If the only reason we collide is track width; it's better to allow the user to start
388 // anyway and just highlight the resulting collisions, so they can change width later.
389 dummyStartLine.SetWidth( m_sizes.BoardMinTrackWidth() );
390
391 if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
392 {
393 ITEM_SET dummyStartSet( &dummyStartLine );
394 NODE::ITEM_VECTOR highlightedItems;
395
396 markViolations( m_world.get(), dummyStartSet, highlightedItems );
397
398 for( ITEM* item : highlightedItems )
399 m_iface->HideItem( item );
400
401 SetFailureReason( _( "The routing start point violates DRC." ) );
402 return false;
403 }
404 }
405 }
406 else if( m_mode == PNS_MODE_ROUTE_DIFF_PAIR )
407 {
408 if( !aStartItem )
409 {
410 SetFailureReason( _( "Cannot start a differential pair in the middle of nowhere." ) );
411 return false;
412 }
413
414 DP_PRIMITIVE_PAIR dpPair;
415 DIFF_PAIR_PLACER dpPlacer( this );
416 wxString errorMsg;
417
418
419 if( !dpPlacer.FindDpPrimitivePair( m_world.get(), startPoint, aStartItem, dpPair,
420 &errorMsg ) )
421 {
422 SetFailureReason( errorMsg );
423 return false;
424 }
425
426 }
427
428 return true;
429}
430
431
432bool ROUTER::StartRouting( const VECTOR2I& aP, ITEM* aStartItem, int aLayer )
433{
435
436 if( !isStartingPointRoutable( aP, aStartItem, aLayer ) )
437 return false;
438
439 switch( m_mode )
440 {
442 m_placer = std::make_unique<LINE_PLACER>( this );
443 break;
444
446 m_placer = std::make_unique<DIFF_PAIR_PLACER>( this );
447 break;
448
450 m_placer = std::make_unique<MEANDER_PLACER>( this );
451 break;
452
454 m_placer = std::make_unique<DP_MEANDER_PLACER>( this );
455 break;
456
458 m_placer = std::make_unique<MEANDER_SKEW_PLACER>( this );
459 break;
460
461 default:
462 return false;
463 }
464
465 m_placer->UpdateSizes( m_sizes );
466 m_placer->SetLayer( aLayer );
467 m_placer->SetDebugDecorator( m_iface->GetDebugDecorator() );
468 m_placer->SetLogger( m_logger );
469
470 if( m_placer->Start( aP, aStartItem ) )
471 {
473
474 if( m_logger )
475 {
476 m_logger->Clear();
477 m_logger->Log( LOGGER::EVT_START_ROUTE, aP, aStartItem, &m_sizes, m_placer->CurrentLayer() );
478 }
479
480 return true;
481 }
482 else
483 {
484 m_state = IDLE;
485 m_placer.reset();
486
487 return false;
488 }
489}
490
491
492bool ROUTER::Move( const VECTOR2I& aP, ITEM* endItem )
493{
494 if( m_logger )
495 m_logger->Log( LOGGER::EVT_MOVE, aP, endItem );
496
497 switch( m_state )
498 {
499 case ROUTE_TRACK:
500 return movePlacing( aP, endItem );
501
502 case DRAG_SEGMENT:
503 case DRAG_COMPONENT:
504 return moveDragging( aP, endItem );
505
506 default:
507 break;
508 }
509
511
512 return false;
513}
514
515
517 ITEM*& aOtherEndItem )
518{
519 // Can't finish something with no connections
520 if( GetCurrentNets().empty() )
521 return false;
522
523 PLACEMENT_ALGO* placer = Placer();
524
525 if( placer == nullptr || placer->Traces().Size() == 0 )
526 return false;
527
528 LINE* trace = dynamic_cast<LINE*>( placer->Traces()[0] );
529
530 if( trace == nullptr )
531 return false;
532
533 PNS::NODE* lastNode = placer->CurrentNode( true );
534 PNS::TOPOLOGY topo( lastNode );
535
536 // If the user has drawn a line, get the anchor nearest to the line end
537 if( trace->SegmentCount() > 0 )
538 {
539 return topo.NearestUnconnectedAnchorPoint( trace, aOtherEnd, aOtherEndLayers,
540 aOtherEndItem );
541 }
542
543 // Otherwise, find the closest anchor to our start point
544
545 // Get joint from placer start item
546 const JOINT* jt = lastNode->FindJoint( placer->CurrentStart(), placer->CurrentLayer(),
547 placer->CurrentNets()[0] );
548
549 if( !jt )
550 return false;
551
552 // Get unconnected item from joint
553 int anchor;
554 PNS::ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
555
556 if( !it )
557 return false;
558
559 aOtherEnd = it->Anchor( anchor );
560 aOtherEndLayers = it->Layers();
561 aOtherEndItem = it;
562
563 return true;
564}
565
566
568{
569 if( m_state != ROUTE_TRACK )
570 return false;
571
572 PLACEMENT_ALGO* placer = Placer();
573
574 if( placer == nullptr || placer->Traces().Size() == 0 )
575 return false;
576
577 LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
578
579 if( current == nullptr )
580 return false;
581
582 // Get our current line and position and nearest ratsnest to them if it exists
583 VECTOR2I otherEnd;
584 PNS_LAYER_RANGE otherEndLayers;
585 ITEM* otherEndItem = nullptr;
586
587 // Get the anchor nearest to the end of the trace the user is routing
588 if( !GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
589 return false;
590
591 // Keep moving until we don't change position or hit the limit
592 int triesLeft = 5;
593 VECTOR2I moveResultPoint;
594
595 do
596 {
597 moveResultPoint = placer->CurrentEnd();
598 Move( otherEnd, otherEndItem );
599 triesLeft--;
600 } while( placer->CurrentEnd() != moveResultPoint && triesLeft );
601
602 // If we've made it, fix the route and we're done
603 if( moveResultPoint == otherEnd && otherEndLayers.Overlaps( GetCurrentLayer() ) )
604 {
605 bool forceFinish = false;
606 bool allowViolations = false;
607
608 return FixRoute( otherEnd, otherEndItem, forceFinish, allowViolations );
609 }
610
611 return false;
612}
613
614
615bool ROUTER::ContinueFromEnd( ITEM** aNewStartItem )
616{
617 PLACEMENT_ALGO* placer = Placer();
618
619 if( placer == nullptr || placer->Traces().Size() == 0 )
620 return false;
621
622 LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
623
624 if( current == nullptr )
625 return false;
626
627 int currentLayer = GetCurrentLayer();
628 VECTOR2I currentEnd = placer->CurrentEnd();
629 VECTOR2I otherEnd;
630 PNS_LAYER_RANGE otherEndLayers;
631 ITEM* otherEndItem = nullptr;
632
633 // Get the anchor nearest to the end of the trace the user is routing
634 if( !GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
635 return false;
636
638
639 // Commit whatever we've fixed and restart routing from the other end
640 int nextLayer = otherEndLayers.Overlaps( currentLayer ) ? currentLayer : otherEndLayers.Start();
641
642 if( !StartRouting( otherEnd, otherEndItem, nextLayer ) )
643 return false;
644
645 // Attempt to route to our current position
646 Move( currentEnd, nullptr );
647
648 *aNewStartItem = otherEndItem;
649
650 return true;
651}
652
653
654bool ROUTER::moveDragging( const VECTOR2I& aP, ITEM* aEndItem )
655{
656 m_iface->EraseView();
657
658 bool ret = m_dragger->Drag( aP );
659 ITEM_SET dragged = m_dragger->Traces();
660
661 m_leaderSegments = m_dragger->GetLastCommittedLeaderSegments();
662
663 updateView( m_dragger->CurrentNode(), dragged, true );
664 return ret;
665}
666
667
668void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR& aRemoved )
669{
670 auto updateItem =
671 [&]( ITEM* currentItem, ITEM* itemToMark )
672 {
673 std::unique_ptr<ITEM> tmp( itemToMark->Clone() );
674
675 int clearance;
676 bool removeOriginal = true;
677
678 clearance = aNode->GetClearance( currentItem, itemToMark );
679
680 if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer()
681 && !itemToMark->HasUniqueShapeLayers() )
682 {
683 tmp->SetLayer( currentItem->Layer() );
684 }
685
686 if( itemToMark->IsCompoundShapePrimitive() )
687 {
688 // We're only highlighting one (or more) of several primitives so we don't
689 // want all the other parts of the object to disappear
690 removeOriginal = false;
691 }
692
693 m_iface->DisplayItem( tmp.get(), clearance );
694
695 if( removeOriginal )
696 aRemoved.push_back( itemToMark );
697 };
698
699 for( ITEM* item : aCurrent.Items() )
700 {
701 NODE::OBSTACLES obstacles;
702
703 aNode->QueryColliding( item, obstacles );
704
705 if( item->OfKind( ITEM::LINE_T ) )
706 {
707 LINE* l = static_cast<LINE*>( item );
708
709 if( l->EndsWithVia() )
710 {
711 VIA v( l->Via() );
712 aNode->QueryColliding( &v, obstacles );
713 }
714 }
715
716 ITEM_SET draggedItems;
717
718 if( GetDragger() )
719 draggedItems = GetDragger()->Traces();
720
721 for( const OBSTACLE& obs : obstacles )
722 {
723 // Don't mark items being dragged; only board items they collide with
724 if( draggedItems.Contains( obs.m_item ) )
725 continue;
726
727 obs.m_item->Mark( obs.m_item->Marker() | MK_VIOLATION );
728 updateItem( item, obs.m_item );
729 }
730
731 if( item->Kind() == ITEM::LINE_T )
732 {
733 LINE* line = static_cast<LINE*>( item );
734
735 // Show clearance on any blocking obstacles
736 if( line->GetBlockingObstacle() )
737 updateItem( item, line->GetBlockingObstacle() );
738 }
739 }
740}
741
742
743void ROUTER::updateView( NODE* aNode, ITEM_SET& aCurrent, bool aDragging )
744{
745 NODE::ITEM_VECTOR removed, added;
746 NODE::OBSTACLES obstacles;
747
748 if( !aNode )
749 return;
750
751 // hack: we only mark violations when routing, not when length tuning - as the length tuner
752 // by design can never generate clearance violations. Since markViolations() calls multiple
753 // collision/clearance queries, it can be extremely expensive with certain custom DRC rules
754 // (rule area/courtyard-based, see issue #24052 for examples)
757 {
758 markViolations( aNode, aCurrent, removed );
759 }
760
761 aNode->GetUpdatedItems( removed, added );
762
763 std::vector<const PNS::ITEM*> cacheCheckItems( added.begin(), added.end() );
764 GetRuleResolver()->ClearCacheForItems( cacheCheckItems );
765
766 for( ITEM* item : added )
767 {
768 int clearance = GetRuleResolver()->Clearance( item, nullptr );
769 m_iface->DisplayItem( item, clearance, aDragging );
770 }
771
772 for( ITEM* item : removed )
773 m_iface->HideItem( item );
774}
775
776
778{
779 m_sizes = aSizes;
780
781 // Change track/via size settings
782 if( m_state == ROUTE_TRACK )
783 m_placer->UpdateSizes( m_sizes );
784}
785
786
787bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
788{
789 m_iface->EraseView();
790
791 bool ret = m_placer->Move( aP, aEndItem );
792 ITEM_SET current = m_placer->Traces();
793
794 for( const ITEM* item : current.CItems() )
795 {
796 if( !item->OfKind( ITEM::LINE_T ) )
797 continue;
798
799 const LINE* l = static_cast<const LINE*>( item );
800 int clearance = GetRuleResolver()->Clearance( item, nullptr );
801
802 m_iface->DisplayItem( l, clearance, false, PNS_HEAD_TRACE );
803
804 if( l->EndsWithVia() )
805 {
806 const VIA& via = l->Via();
807 clearance = GetRuleResolver()->Clearance( &via, nullptr );
808
809 if( via.HasHole() )
810 {
811 int holeClearance = GetRuleResolver()->Clearance( via.Hole(), nullptr );
812 int annularWidth = std::max( 0, via.Diameter( l->Layer() ) - via.Drill() ) / 2;
813 int excessHoleClearance = holeClearance - annularWidth;
814
815 if( excessHoleClearance > clearance )
816 clearance = excessHoleClearance;
817 }
818
819 m_iface->DisplayItem( &l->Via(), clearance, false, PNS_HEAD_TRACE );
820 }
821 }
822
823 //ITEM_SET tmp( &current );
824
825 updateView( m_placer->CurrentNode( true ), current );
826
827 return ret;
828}
829
830
831void ROUTER::GetUpdatedItems( std::vector<PNS::ITEM*>& aRemoved, std::vector<PNS::ITEM*>& aAdded,
832 std::vector<PNS::ITEM*>& aHeads )
833{
834 NODE *node = nullptr;
835 ITEM_SET current;
836
837 if( m_state == ROUTE_TRACK )
838 {
839 node = m_placer->CurrentNode( true );
840 current = m_placer->Traces();
841 }
842 else if ( m_state == DRAG_SEGMENT )
843 {
844 node = m_dragger->CurrentNode();
845 current = m_dragger->Traces();
846 }
847
848 // There probably should be a debugging assertion and possibly a PNS_LOGGER call here but
849 // I'm not sure how to be proceed WLS.
850 if( !node )
851 return;
852
853 node->GetUpdatedItems( aRemoved, aAdded );
854
855 for( const ITEM* item : current.CItems() )
856 aHeads.push_back( item->Clone() );
857}
858
859
861{
862 if( m_state == ROUTE_TRACK && !m_placer->HasPlacedAnything() )
863 return;
864
865 NODE::ITEM_VECTOR removed;
866 NODE::ITEM_VECTOR added;
867 NODE::ITEM_VECTOR changed;
868
869 aNode->GetUpdatedItems( removed, added );
870
871 for( ITEM* item : removed )
872 {
873 bool is_changed = false;
874
875 // Items in remove/add that share the same parent are just updated versions
876 // We move them to the updated vector to preserve attributes such as UUID and pad data
877 if( item->Parent() )
878 {
879 for( NODE::ITEM_VECTOR::iterator added_it = added.begin();
880 added_it != added.end(); ++added_it )
881 {
882 if( ( *added_it )->Parent() && ( *added_it )->Parent() == item->Parent() )
883 {
884 changed.push_back( *added_it );
885 added.erase( added_it );
886 is_changed = true;
887 break;
888 }
889 }
890 }
891
892 if( !is_changed && !item->IsVirtual() )
893 m_iface->RemoveItem( item );
894 }
895
896 for( ITEM* item : added )
897 {
898 if( !item->IsVirtual() )
899 m_iface->AddItem( item );
900 }
901
902 for( ITEM* item : changed )
903 {
904 if( !item->IsVirtual() )
905 m_iface->UpdateItem( item );
906 }
907
908 m_iface->Commit();
909 m_world->Commit( aNode );
910}
911
912
913bool ROUTER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish, bool aForceCommit )
914{
915 bool rv = false;
916
917 if( m_logger )
918 m_logger->Log( LOGGER::EVT_FIX, aP, aEndItem );
919
920 switch( m_state )
921 {
922 case ROUTE_TRACK:
923 rv = m_placer->FixRoute( aP, aEndItem, aForceFinish );
924 break;
925
926 case DRAG_SEGMENT:
927 case DRAG_COMPONENT:
928 rv = m_dragger->FixRoute( aForceCommit );
929 break;
930
931 default:
932 break;
933 }
934
935 return rv;
936}
937
939{
940 return m_leaderSegments;
941};
942
943
944std::optional<VECTOR2I> ROUTER::UndoLastSegment()
945{
946 if( !RoutingInProgress() )
947 return std::nullopt;
948
949 if( m_logger )
951
952 return m_placer->UnfixRoute();
953}
954
955
957{
958 if( m_state == ROUTE_TRACK )
959 m_placer->CommitPlacement();
960
961 StopRouting();
962}
963
964
966{
967 // Update the ratsnest with new changes
968
969 if( m_placer )
970 {
971 std::vector<NET_HANDLE> nets;
972 m_placer->GetModifiedNets( nets );
973
974 // Update the ratsnest with new changes
975 for( NET_HANDLE n : nets )
976 m_iface->UpdateNet( n );
977 }
978
979 if( !RoutingInProgress() )
980 return;
981
982 m_placer.reset();
983 m_dragger.reset();
984
985 m_iface->EraseView();
986
987 m_state = IDLE;
988 m_world->KillChildren();
989 m_world->ClearRanks();
990}
991
992
994{
995 m_iface->EraseView();
996}
997
998
1000{
1001 if( m_state == ROUTE_TRACK )
1002 {
1003 m_placer->FlipPosture();
1004 }
1005}
1006
1007
1008bool ROUTER::SwitchLayer( int aLayer )
1009{
1010 if( m_state == ROUTE_TRACK )
1011 return m_placer->SetLayer( aLayer );
1012
1013 return false;
1014}
1015
1016
1018{
1019 if( m_state == ROUTE_TRACK )
1020 {
1021 bool toggle = !m_placer->IsPlacingVia();
1022 m_placer->ToggleVia( toggle );
1023
1024 if( m_logger )
1025 m_logger->Log( LOGGER::EVT_TOGGLE_VIA, VECTOR2I(), nullptr, &m_sizes );
1026 }
1027}
1028
1029
1030const std::vector<NET_HANDLE> ROUTER::GetCurrentNets() const
1031{
1032 if( m_placer )
1033 return m_placer->CurrentNets();
1034 else if( m_dragger )
1035 return m_dragger->CurrentNets();
1036
1037 return std::vector<NET_HANDLE>();
1038}
1039
1040
1042{
1043 if( m_placer )
1044 return m_placer->CurrentLayer();
1045 else if( m_dragger )
1046 return m_dragger->CurrentLayer();
1047
1048 return -1;
1049}
1050
1051
1053{
1054 return m_logger;
1055}
1056
1057
1059{
1060 if( !m_placer )
1061 return false;
1062
1063 return m_placer->IsPlacingVia();
1064}
1065
1066
1081
1082
1083void ROUTER::SetOrthoMode( bool aEnable )
1084{
1085 if( !m_placer )
1086 return;
1087
1088 m_placer->SetOrthoMode( aEnable );
1089}
1090
1091
1093{
1094 m_mode = aMode;
1095}
1096
1097
1099{
1100 m_iface = aIface;
1101}
1102
1103
1104void ROUTER::BreakSegmentOrArc( ITEM *aItem, const VECTOR2I& aP )
1105{
1106 NODE *node = m_world->Branch();
1107
1108 bool ret = false;
1109
1110 if( aItem->OfKind( ITEM::SEGMENT_T ) )
1111 ret = SplitAdjacentSegments( node, aItem, aP );
1112 else if( aItem->OfKind( ITEM::ARC_T ) )
1113 ret = SplitAdjacentArcs( node, aItem, aP );
1114
1115 if( ret )
1116 {
1117 CommitRouting( node );
1118 }
1119 else
1120 {
1121 delete node;
1122 }
1123}
1124
1125
1127{
1128 if( m_placer )
1129 m_placer->AbortPlacement();
1130}
1131
1132}
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
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
@ ROUNDED_45
H/V/45 with filleted corners.
Definition direction45.h:69
@ MITERED_45
H/V/45 with mitered corners (default)
Definition direction45.h:68
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Definition pad.h:61
Single track placement algorithm.
const DIFF_PAIR CurrentTrace() const
bool FindDpPrimitivePair(NODE *aWorld, const VECTOR2I &aP, ITEM *aItem, DP_PRIMITIVE_PAIR &aPair, wxString *aErrorMsg=nullptr)
Store starting/ending primitives (pads, vias or segments) for a differential pair.
virtual const ITEM_SET Traces()=0
Function Traces()
bool Empty() const
Definition pns_itemset.h:90
int Size() const
int Count(int aKindMask=-1) const
Definition pns_itemset.h:74
void Add(const LINE &aLine)
bool Contains(ITEM *aItem) const
std::vector< ITEM * > & Items()
Definition pns_itemset.h:95
const std::vector< ITEM * > & CItems() const
Definition pns_itemset.h:96
Base class for PNS router board items.
Definition pns_item.h:98
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
void SetNet(NET_HANDLE aNet)
Definition pns_item.h:209
virtual int Layer() const
Definition pns_item.h:216
void SetLayer(int aLayer)
Definition pns_item.h:215
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:268
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
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
ITEM * GetBlockingObstacle() const
Definition pns_line.h:239
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition pns_line.h:135
VIA & Via()
Definition pns_line.h:207
int SegmentCount() const
Definition pns_line.h:148
void SetWidth(int aWidth)
Return line width.
Definition pns_line.h:159
bool EndsWithVia() const
Definition pns_line.h:199
@ EVT_START_MULTIDRAG
Definition pns_logger.h:68
Keep the router "world" - i.e.
Definition pns_node.h:243
std::vector< ITEM * > ITEM_VECTOR
Definition pns_node.h:254
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 GetUpdatedItems(ITEM_VECTOR &aRemoved, ITEM_VECTOR &aAdded)
Return the list of items removed and added in this branch with respect to the root branch.
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.
std::set< OBSTACLE > OBSTACLES
Definition pns_node.h:255
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
const ITEM_SET HitTest(const VECTOR2I &aPoint) const
Find all items that contain the point aPoint.
Definition pns_node.cpp:572
virtual NODE * CurrentNode(bool aLoopsRemoved=false) const =0
Function CurrentNode()
virtual const VECTOR2I & CurrentEnd() const =0
Function CurrentEnd()
virtual const ITEM_SET Traces()=0
Function Traces()
virtual const VECTOR2I & CurrentStart() const =0
Function CurrentStart()
virtual int CurrentLayer() const =0
Function CurrentLayer()
virtual const std::vector< NET_HANDLE > CurrentNets() const =0
Function CurrentNets()
void updateView(NODE *aNode, ITEM_SET &aCurrent, bool aDragging=false)
void SetMode(ROUTER_MODE aMode)
bool moveDragging(const VECTOR2I &aP, ITEM *aItem)
bool SwitchLayer(int layer)
void StopRouting()
void ClearViewDecorations()
std::vector< PNS::ITEM * > m_leaderSegments
Definition pns_router.h:285
void ToggleCornerMode()
PLACEMENT_ALGO * Placer()
Definition pns_router.h:252
std::vector< PNS::ITEM * > GetLastCommittedLeaderSegments()
NODE * m_lastNode
Definition pns_router.h:280
void ClearWorld()
void BreakSegmentOrArc(ITEM *aItem, const VECTOR2I &aP)
bool ContinueFromEnd(ITEM **aNewStartItem)
void UpdateSizes(const SIZES_SETTINGS &aSizes)
Applies stored settings.
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:249
LOGGER * Logger()
RouterState m_state
Definition pns_router.h:277
void CommitRouting()
std::unique_ptr< DRAG_ALGO > m_dragger
Definition pns_router.h:283
const ITEM_SET QueryHoverItems(const VECTOR2I &aP, int aSlopRadius=0)
void SetInterface(ROUTER_IFACE *aIface)
void markViolations(NODE *aNode, ITEM_SET &aCurrent, NODE::ITEM_VECTOR &aRemoved)
void SyncWorld()
std::unique_ptr< PLACEMENT_ALGO > m_placer
Definition pns_router.h:282
bool isStartingPointRoutable(const VECTOR2I &aWhere, ITEM *aItem, int aLayer)
ROUTER_IFACE * m_iface
Definition pns_router.h:287
bool IsPlacingVia() const
void FlipPosture()
RULE_RESOLVER * GetRuleResolver() const
Definition pns_router.h:216
SIZES_SETTINGS m_sizes
Definition pns_router.h:292
ROUTING_SETTINGS & Settings()
Definition pns_router.h:228
DRAG_ALGO * GetDragger()
Definition pns_router.h:175
bool movePlacing(const VECTOR2I &aP, ITEM *aItem)
bool RoutingInProgress() const
BOX2I m_visibleViewArea
Definition pns_router.h:276
static ROUTER * GetInstance()
std::optional< VECTOR2I > UndoLastSegment()
void AbortPlacement()
LOGGER * m_logger
Definition pns_router.h:294
void SetOrthoMode(bool aEnable)
bool StartDragging(const VECTOR2I &aP, ITEM *aItem, int aDragMode=DM_ANY)
bool StartRouting(const VECTOR2I &aP, ITEM *aItem, int aLayer)
int GetCurrentLayer() const
void GetUpdatedItems(std::vector< PNS::ITEM * > &aRemoved, std::vector< PNS::ITEM * > &aAdded, std::vector< PNS::ITEM * > &aHeads)
SIZES_SETTINGS & Sizes()
Definition pns_router.h:247
bool FixRoute(const VECTOR2I &aP, ITEM *aItem, bool aForceFinish, bool aForceCommit)
std::unique_ptr< NODE > m_world
Definition pns_router.h:279
void ToggleViaPlacement()
ROUTING_SETTINGS * m_settings
Definition pns_router.h:291
const std::vector< NET_HANDLE > GetCurrentNets() const
ROUTER_MODE m_mode
Definition pns_router.h:293
bool GetNearestRatnestAnchor(VECTOR2I &aOtherEnd, PNS_LAYER_RANGE &aOtherEndLayers, ITEM *&aOtherEndItem)
bool Move(const VECTOR2I &aP, ITEM *aItem)
virtual void ClearCacheForItems(std::vector< const ITEM * > &aItems)
Definition pns_node.h:171
virtual void ClearTemporaryCaches()
Definition pns_node.h:173
virtual int Clearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true)=0
virtual void ClearCaches()
Definition pns_node.h:172
ITEM * NearestUnconnectedItem(const JOINT *aStart, int *aAnchor=nullptr, int aKindMask=ITEM::ANY_T)
bool NearestUnconnectedAnchorPoint(const LINE *aTrack, VECTOR2I &aPoint, PNS_LAYER_RANGE &aLayers, ITEM *&aItem)
Represent a contiguous set of PCB layers.
int Start() const
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
static PNS_LAYER_RANGE All()
bool IsMultilayer() const
Definition seg.h:38
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
const wxString & GetZoneName() const
Definition zone.h:160
bool HasKeepoutParametersSet() const
Accessor to determine if any keepout parameters are set.
Definition zone.h:798
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
@ Edge_Cuts
Definition layer_ids.h:108
Push and Shove diff pair dimensions (gap) settings dialog.
bool SplitAdjacentSegments(NODE *aNode, ITEM *aSeg, const VECTOR2I &aP)
Snaps the point aP to segment aSeg.
static ROUTER * theRouter
void * NET_HANDLE
Definition pns_item.h:55
bool SplitAdjacentArcs(NODE *aNode, ITEM *aArc, const VECTOR2I &aP)
Snaps the point aP to arc aArc.
ROUTER_MODE
Definition pns_router.h:67
@ PNS_MODE_ROUTE_SINGLE
Definition pns_router.h:68
@ PNS_MODE_ROUTE_DIFF_PAIR
Definition pns_router.h:69
@ PNS_MODE_TUNE_DIFF_PAIR
Definition pns_router.h:71
@ PNS_MODE_TUNE_SINGLE
Definition pns_router.h:70
@ PNS_MODE_TUNE_DIFF_PAIR_SKEW
Definition pns_router.h:72
DRAG_MODE
Definition pns_router.h:76
@ MK_VIOLATION
Definition pns_item.h:44
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
#define PNS_HEAD_TRACE
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
int clearance
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683