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 "pns_diff_pair.h"
54
55#include "router_preview_item.h"
56
57namespace PNS {
58
59// an ugly singleton for drawing debug items within the router context.
60// To be fixed sometime in the future.
62
64{
65 theRouter = this;
66
67 m_state = IDLE;
69
70 m_logger = nullptr;
71
72 if( ADVANCED_CFG::GetCfg().m_EnableRouterDump )
73 m_logger = new LOGGER;
74
75 // Initialize all other variables:
76 m_lastNode = nullptr;
77 m_iterLimit = 0;
78 m_settings = nullptr;
79 m_iface = nullptr;
80 m_visibleViewArea.SetMaximum();
81}
82
83
85{
86 return theRouter;
87}
88
89
91{
92 ClearWorld();
93 theRouter = nullptr;
94 delete m_logger;
95}
96
97
99{
100 ClearWorld();
101
102 m_world = std::make_unique<NODE>();
103 m_world->BeginBulkAdd();
104 m_iface->SyncWorld( m_world.get() );
105 m_world->FinalizeBulkAdd();
106 m_world->FixupVirtualVias();
107}
108
109
111{
112 if( m_world )
113 {
114 m_world->SetRuleResolver( nullptr );
115 m_world->KillChildren();
116 m_world.reset();
117 }
118
119 m_placer.reset();
120}
121
122
124{
125 return m_state != IDLE;
126}
127
128
129const ITEM_SET ROUTER::QueryHoverItems( const VECTOR2I& aP, int aSlopRadius )
130{
131 NODE* node = m_placer ? m_placer->CurrentNode() : m_world.get();
132 PNS::ITEM_SET ret;
133
134 wxCHECK( node, ret );
135
137 {
138 NODE::OBSTACLES obs;
139 SEGMENT test( SEG( aP, aP ), nullptr );
141
142 test.SetWidth( Sizes().DiffPairGap() + 2 * Sizes().DiffPairWidth() );
143 test.SetLayers( PNS_LAYER_RANGE::All() );
144
145 opts.m_differentNetsOnly = false;
146 node->QueryColliding( &test, obs, opts );
147
148 DIFF_PAIR_PLACER* dpPlacer = static_cast<DIFF_PAIR_PLACER*>( Placer() );
149 auto currentDP = dpPlacer->CurrentTrace();
150
151 int distP = std::numeric_limits<int>::max();
152 int distN = std::numeric_limits<int>::max();
153
154 ITEM* best = nullptr;
155
156 for( const OBSTACLE& obstacle : obs )
157 {
158 NET_HANDLE netP, netN;
159
160 if( m_iface->GetRuleResolver()->DpNetPair( obstacle.m_item, netP, netN ) )
161 {
162 int dist = obstacle.m_item->Shape( obstacle.m_item->Layer() )->Distance( aP );
163
164 if( dist <= 0 )
165 {
166 ret.Add( obstacle.m_item, false );
167 }
168
169 int polarity = m_iface->GetRuleResolver()->DpNetPolarity( obstacle.m_item->Net() );
170
171 if( polarity > 0 )
172 {
173 if( dist < distP )
174 {
175 distP = dist;
176 best = obstacle.m_item;
177 }
178 }
179 else
180 {
181 if( dist < distN )
182 {
183 distN = dist;
184 if( distN <= distP )
185 best = obstacle.m_item;
186 }
187 }
188 }
189 }
190
191 if( distP < Sizes().DiffPairGap() && distN < Sizes().DiffPairGap() )
192 {
193 ret.Add( best, false );
194 return ret;
195 }
196
197 return ret;
198 }
199
200 if( aSlopRadius > 0 )
201 {
202 NODE::OBSTACLES obs;
203 SEGMENT test( SEG( aP, aP ), nullptr );
205
206 test.SetWidth( 1 );
207 test.SetLayers( PNS_LAYER_RANGE::All() );
208
209 opts.m_differentNetsOnly = false;
210 opts.m_overrideClearance = aSlopRadius;
211
212 node->QueryColliding( &test, obs, opts );
213
214 for( const OBSTACLE& obstacle : obs )
215 ret.Add( obstacle.m_item, false );
216
217 return ret;
218 }
219 else
220 {
221 return node->HitTest( aP );
222 }
223}
224
225
226bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM* aItem, int aDragMode )
227{
228 m_leaderSegments.clear();
229 return StartDragging( aP, ITEM_SET( aItem ), aDragMode );
230}
231
232bool ROUTER::hasDiffPairMembers( const ITEM_SET& aItems ) const
233{
234 auto rr = GetRuleResolver();
235
236 for( auto item : aItems.CItems() )
237 {
238
239 if( item->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
240 {
241 NET_HANDLE coupledNet = rr->DpCoupledNet( item->Net() );
242 if( coupledNet )
243 {
244 PNS_DBG( dbg(), Message, wxString::Format( wxT("found DP in itemset: %s/%s"),
245 GetInterface()->GetNetName( item->Net() ),
246 GetInterface()->GetNetName( coupledNet ) ) );
247 return true;
248 }
249 }
250 }
251
252 return false;
253}
254
255
256bool ROUTER::StartDragging( const VECTOR2I& aP, ITEM_SET aStartItems, int aDragMode )
257{
258 m_leaderSegments.clear();
259 SetFailureReason( wxEmptyString );
261 if( aStartItems.Empty() )
262 return false;
263
264 auto rr = GetRuleResolver();
265 rr->ClearCaches();
266
267 bool useMultidragger = aStartItems.Count( ITEM::SEGMENT_T | ITEM::ARC_T ) > 1;
268
269 PNS_DBG( dbg, Message, wxString::Format(wxT("start-drag items %d usemulti %d"),
270 aStartItems.Count( ITEM::SEGMENT_T | ITEM::ARC_T ), useMultidragger?1:0 ));
271
272 // we use MULTI_DRAGGER also for differential pairs (with somewhat different optimization strategy)
273 if( !useMultidragger && Settings().GetKeepDPCouplingWhenDragging() )
274 {
275 useMultidragger = hasDiffPairMembers( aStartItems );
276 }
277
278 if( aStartItems.Count( ITEM::SOLID_T ) > 0 )
279 {
280 m_dragger = std::make_unique<COMPONENT_DRAGGER>( this );
282 }
283 // more than 1 track segment or arc to drag? launch the multisegment dragger
284 else if( useMultidragger )
285 {
286 m_dragger = std::make_unique<MULTI_DRAGGER>( this );
288 }
289 else
290 {
291 m_dragger = std::make_unique<DRAGGER>( this );
293 }
294
295 m_dragger->SetMode( static_cast<PNS::DRAG_MODE>( aDragMode ) );
296 m_dragger->SetWorld( m_world.get() );
297 m_dragger->SetLogger( m_logger );
298 m_dragger->SetDebugDecorator( m_iface->GetDebugDecorator() );
299
300 if( m_logger )
301 m_logger->Clear();
302
303 if( m_logger )
304 {
305 if( aStartItems.Size() == 1 )
306 m_logger->Log( LOGGER::EVT_START_DRAG, aP, aStartItems[0] );
307 else if( aStartItems.Size() > 1 )
308 m_logger->LogM( LOGGER::EVT_START_MULTIDRAG, aP, aStartItems.Items() ); // fixme default args
309 }
310
311 if( m_dragger->Start( aP, aStartItems ) )
312 {
313 return true;
314 }
315 else
316 {
317 m_dragger.reset();
318 m_state = IDLE;
319 return false;
320 }
321}
322
323
324bool ROUTER::isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aStartItem, int aLayer )
325{
326 if( Settings().AllowDRCViolations() )
327 return true;
328
330 {
331 if( m_sizes.DiffPairGap() < m_sizes.MinClearance() )
332 {
333 SetFailureReason( _( "Diff pair gap is less than board minimum clearance." ) );
334 return false;
335 }
336 }
337
339 wxString failureReason;
340
341 for( ITEM* item : candidates.Items() )
342 {
343 // Edge cuts are put on all layers, but they're not *really* on all layers
344 if( item->BoardItem() && item->BoardItem()->GetLayer() == Edge_Cuts )
345 continue;
346
347 if( !item->Layers().Overlaps( aLayer ) )
348 continue;
349
350 if( item->IsRoutable() )
351 {
352 failureReason = wxEmptyString;
353 break;
354 }
355 else
356 {
357 BOARD_ITEM* parent = item->BoardItem();
358
359 switch( parent->Type() )
360 {
361 case PCB_PAD_T:
362 {
363 PAD* pad = static_cast<PAD*>( parent );
364
365 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
366 failureReason = _( "Cannot start routing from a non-plated hole." );
367 }
368 break;
369
370 case PCB_ZONE_T:
371 {
372 ZONE* zone = static_cast<ZONE*>( parent );
373
374 if( !zone->HasKeepoutParametersSet() )
375 break;
376
377 if( !zone->GetZoneName().IsEmpty() )
378 {
379 failureReason = wxString::Format( _( "Rule area '%s' disallows tracks." ),
380 zone->GetZoneName() );
381 }
382 else
383 {
384 failureReason = _( "Rule area disallows tracks." );
385 }
386 }
387 break;
388
389 case PCB_FIELD_T:
390 case PCB_TEXT_T:
391 case PCB_TEXTBOX_T:
392 failureReason = _( "Cannot start routing from a text item." );
393 break;
394
395 default:
396 break;
397 }
398 }
399 }
400
401 if( !failureReason.IsEmpty() )
402 {
403 SetFailureReason( failureReason );
404 return false;
405 }
406
407 VECTOR2I startPoint = aWhere;
408
410 {
411 SHAPE_LINE_CHAIN dummyStartSeg;
412 LINE dummyStartLine;
413
414 dummyStartSeg.Append( startPoint );
415 dummyStartSeg.Append( startPoint, true );
416
417 dummyStartLine.SetShape( dummyStartSeg );
418 dummyStartLine.SetLayer( aLayer );
419 dummyStartLine.SetNet( aStartItem ? aStartItem->Net() : 0 );
420 dummyStartLine.SetWidth( m_sizes.TrackWidth() );
421
422 if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
423 {
424 // If the only reason we collide is track width; it's better to allow the user to start
425 // anyway and just highlight the resulting collisions, so they can change width later.
426 dummyStartLine.SetWidth( m_sizes.BoardMinTrackWidth() );
427
428 if( m_world->CheckColliding( &dummyStartLine, ITEM::ANY_T ) )
429 {
430 ITEM_SET dummyStartSet( &dummyStartLine );
431 NODE::ITEM_VECTOR highlightedItems;
432
433 markViolations( m_world.get(), dummyStartSet, highlightedItems );
434
435 for( ITEM* item : highlightedItems )
436 m_iface->HideItem( item );
437
438 SetFailureReason( _( "The routing start point violates DRC." ) );
439 return false;
440 }
441 }
442 }
443 else if( m_mode == PNS_MODE_ROUTE_DIFF_PAIR )
444 {
445 if( !aStartItem )
446 {
447 SetFailureReason( _( "Cannot start a differential pair in the middle of nowhere." ) );
448 return false;
449 }
450
451 DP_PRIMITIVE_PAIR dpPair;
452 DIFF_PAIR_PLACER dpPlacer( this );
453 wxString errorMsg;
454
455
456 if( !dpPlacer.FindDpPrimitivePair( m_world.get(), startPoint, aStartItem, dpPair,
457 &errorMsg ) )
458 {
459 SetFailureReason( errorMsg );
460 return false;
461 }
462
463 }
464
465 return true;
466}
467
468
469bool ROUTER::StartRouting( const VECTOR2I& aP, ITEM* aStartItem, int aLayer )
470{
472
473 if( !isStartingPointRoutable( aP, aStartItem, aLayer ) )
474 return false;
475
476 switch( m_mode )
477 {
479 m_placer = std::make_unique<LINE_PLACER>( this );
480 break;
481
483 m_placer = std::make_unique<DIFF_PAIR_PLACER>( this );
484 break;
485
487 m_placer = std::make_unique<MEANDER_PLACER>( this );
488 break;
489
491 m_placer = std::make_unique<DP_MEANDER_PLACER>( this );
492 break;
493
495 m_placer = std::make_unique<MEANDER_SKEW_PLACER>( this );
496 break;
497
498 default:
499 return false;
500 }
501
502 m_placer->UpdateSizes( m_sizes );
503 m_placer->SetLayer( aLayer );
504 m_placer->SetDebugDecorator( m_iface->GetDebugDecorator() );
505 m_placer->SetLogger( m_logger );
506
507 if( m_placer->Start( aP, aStartItem ) )
508 {
510
511 if( m_logger )
512 {
513 m_logger->Clear();
514 m_logger->Log( LOGGER::EVT_START_ROUTE, aP, aStartItem, &m_sizes, m_placer->CurrentLayer() );
515 }
516
517 return true;
518 }
519 else
520 {
521 m_state = IDLE;
522 m_placer.reset();
523
524 return false;
525 }
526}
527
528
529bool ROUTER::Move( const VECTOR2I& aP, ITEM* endItem )
530{
531 if( m_logger )
532 m_logger->Log( LOGGER::EVT_MOVE, aP, endItem );
533
534 switch( m_state )
535 {
536 case ROUTE_TRACK:
537 return movePlacing( aP, endItem );
538
539 case DRAG_SEGMENT:
540 case DRAG_COMPONENT:
541 return moveDragging( aP, endItem );
542
543 default:
544 break;
545 }
546
548
549 return false;
550}
551
552
554 ITEM*& aOtherEndItem )
555{
556 // Can't finish something with no connections
557 if( GetCurrentNets().empty() )
558 return false;
559
560 PLACEMENT_ALGO* placer = Placer();
561
562 if( placer == nullptr || placer->Traces().Size() == 0 )
563 return false;
564
565 LINE* trace = dynamic_cast<LINE*>( placer->Traces()[0] );
566
567 if( trace == nullptr )
568 return false;
569
570 PNS::NODE* lastNode = placer->CurrentNode( true );
571 PNS::TOPOLOGY topo( lastNode, m_iface );
572
573 // If the user has drawn a line, get the anchor nearest to the line end
574 if( trace->SegmentCount() > 0 )
575 {
576 return topo.NearestUnconnectedAnchorPoint( trace, aOtherEnd, aOtherEndLayers,
577 aOtherEndItem );
578 }
579
580 // Otherwise, find the closest anchor to our start point
581
582 // Get joint from placer start item
583 const JOINT* jt = lastNode->FindJoint( placer->CurrentStart(), placer->CurrentLayer(),
584 placer->CurrentNets()[0] );
585
586 if( !jt )
587 return false;
588
589 // Get unconnected item from joint
590 int anchor;
591 PNS::ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
592
593 if( !it )
594 return false;
595
596 aOtherEnd = it->Anchor( anchor );
597 aOtherEndLayers = it->Layers();
598 aOtherEndItem = it;
599
600 return true;
601}
602
603
605{
606 if( m_state != ROUTE_TRACK )
607 return false;
608
609 PLACEMENT_ALGO* placer = Placer();
610
611 if( placer == nullptr || placer->Traces().Size() == 0 )
612 return false;
613
614 LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
615
616 if( current == nullptr )
617 return false;
618
619 // Get our current line and position and nearest ratsnest to them if it exists
620 VECTOR2I otherEnd;
621 PNS_LAYER_RANGE otherEndLayers;
622 ITEM* otherEndItem = nullptr;
623
624 // Get the anchor nearest to the end of the trace the user is routing
625 if( !GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
626 return false;
627
628 // Keep moving until we don't change position or hit the limit
629 int triesLeft = 5;
630 VECTOR2I moveResultPoint;
631
632 do
633 {
634 moveResultPoint = placer->CurrentEnd();
635 Move( otherEnd, otherEndItem );
636 triesLeft--;
637 } while( placer->CurrentEnd() != moveResultPoint && triesLeft );
638
639 // If we've made it, fix the route and we're done
640 if( placer->CurrentEnd() == otherEnd && otherEndLayers.Overlaps( GetCurrentLayer() ) )
641 {
642 bool forceFinish = false;
643 bool allowViolations = false;
644
645 return FixRoute( otherEnd, otherEndItem, forceFinish, allowViolations );
646 }
647
648 return false;
649}
650
651
652bool ROUTER::ContinueFromEnd( ITEM** aNewStartItem )
653{
654 PLACEMENT_ALGO* placer = Placer();
655
656 if( placer == nullptr || placer->Traces().Size() == 0 )
657 return false;
658
659 LINE* current = dynamic_cast<LINE*>( placer->Traces()[0] );
660
661 if( current == nullptr )
662 return false;
663
664 int currentLayer = GetCurrentLayer();
665 VECTOR2I currentEnd = placer->CurrentEnd();
666 VECTOR2I otherEnd;
667 PNS_LAYER_RANGE otherEndLayers;
668 ITEM* otherEndItem = nullptr;
669
670 // Get the anchor nearest to the end of the trace the user is routing
671 if( !GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
672 return false;
673
675
676 // Commit whatever we've fixed and restart routing from the other end
677 int nextLayer = otherEndLayers.Overlaps( currentLayer ) ? currentLayer : otherEndLayers.Start();
678
679 if( !StartRouting( otherEnd, otherEndItem, nextLayer ) )
680 return false;
681
682 // Attempt to route to our current position
683 Move( currentEnd, nullptr );
684
685 *aNewStartItem = otherEndItem;
686
687 return true;
688}
689
690
691bool ROUTER::moveDragging( const VECTOR2I& aP, ITEM* aEndItem )
692{
693 m_iface->EraseView();
694
695 bool ret = m_dragger->Drag( aP );
696 ITEM_SET dragged = m_dragger->Traces();
697
698 m_leaderSegments = m_dragger->GetLastCommittedLeaderSegments();
699
700 updateView( m_dragger->CurrentNode(), dragged, true );
701 return ret;
702}
703
704
705void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR& aRemoved )
706{
707 auto updateItem =
708 [&]( ITEM* currentItem, ITEM* itemToMark )
709 {
710 std::unique_ptr<ITEM> tmp( itemToMark->Clone() );
711
712 int clearance;
713 bool removeOriginal = true;
714
715 clearance = aNode->GetClearance( currentItem, itemToMark );
716
717 if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer()
718 && !itemToMark->HasUniqueShapeLayers() )
719 {
720 tmp->SetLayer( currentItem->Layer() );
721 }
722
723 if( itemToMark->IsCompoundShapePrimitive() )
724 {
725 // We're only highlighting one (or more) of several primitives so we don't
726 // want all the other parts of the object to disappear
727 removeOriginal = false;
728 }
729
730 m_iface->DisplayItem( tmp.get(), clearance );
731
732 if( removeOriginal )
733 aRemoved.push_back( itemToMark );
734 };
735
736 for( ITEM* item : aCurrent.Items() )
737 {
738 NODE::OBSTACLES obstacles;
739
740 aNode->QueryColliding( item, obstacles );
741
742 if( item->OfKind( ITEM::LINE_T ) )
743 {
744 LINE* l = static_cast<LINE*>( item );
745
746 if( l->EndsWithVia() )
747 {
748 VIA v( l->Via() );
749 aNode->QueryColliding( &v, obstacles );
750 }
751 }
752
753 ITEM_SET draggedItems;
754
755 if( GetDragger() )
756 draggedItems = GetDragger()->Traces();
757
758 for( const OBSTACLE& obs : obstacles )
759 {
760 // Don't mark items being dragged; only board items they collide with
761 if( draggedItems.Contains( obs.m_item ) )
762 continue;
763
764 obs.m_item->Mark( obs.m_item->Marker() | MK_VIOLATION );
765 updateItem( item, obs.m_item );
766 }
767
768 if( item->Kind() == ITEM::LINE_T )
769 {
770 LINE* line = static_cast<LINE*>( item );
771
772 // Show clearance on any blocking obstacles
773 if( line->GetBlockingObstacle() )
774 updateItem( item, line->GetBlockingObstacle() );
775 }
776 }
777}
778
779
781{
782 if( m_state != ROUTE_TRACK )
783 return false;
784
786 return true;
787
788 return false;
789}
790
791
792void ROUTER::updateView( NODE* aNode, ITEM_SET& aCurrent, bool aDragging )
793{
794 NODE::ITEM_VECTOR removed, added;
795 NODE::OBSTACLES obstacles;
796
797 if( !aNode )
798 return;
799
800 // hack: we only mark violations when routing, not when length tuning - as the length tuner
801 // by design can never generate clearance violations. Since markViolations() calls multiple
802 // collision/clearance queries, it can be extremely expensive with certain custom DRC rules
803 // (rule area/courtyard-based, see issue #24052 for examples)
804 if( !areWeLengthTuning() )
805 {
806 markViolations( aNode, aCurrent, removed );
807 }
808
809 aNode->GetUpdatedItems( removed, added );
810
811 std::vector<const PNS::ITEM*> cacheCheckItems( added.begin(), added.end() );
812 GetRuleResolver()->ClearCacheForItems( cacheCheckItems );
813
814 for( ITEM* item : added )
815 {
816 int clearance = GetRuleResolver()->Clearance( item, nullptr );
817 m_iface->DisplayItem( item, clearance, aDragging );
818 }
819
820 for( ITEM* item : removed )
821 m_iface->HideItem( item );
822}
823
824
826{
827 m_sizes = aSizes;
828
829 // Change track/via size settings
830 if( m_state == ROUTE_TRACK )
831 m_placer->UpdateSizes( m_sizes );
832}
833
834
835bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
836{
837 m_iface->EraseView();
838
839 bool ret = m_placer->Move( aP, aEndItem );
840 ITEM_SET current = m_placer->Traces();
841
842 for( const ITEM* item : current.CItems() )
843 {
844 if( !item->OfKind( ITEM::LINE_T ) )
845 continue;
846
847 const LINE* l = static_cast<const LINE*>( item );
848 int clearance = GetRuleResolver()->Clearance( item, nullptr );
849
850 m_iface->DisplayItem( l, clearance, false, PNS_HEAD_TRACE );
851
852 if( l->EndsWithVia() )
853 {
854 const VIA& via = l->Via();
855 clearance = GetRuleResolver()->Clearance( &via, nullptr );
856
857 if( via.HasHole() )
858 {
859 int holeClearance = GetRuleResolver()->Clearance( via.Hole(), nullptr );
860 int annularWidth = std::max( 0, via.Diameter( l->Layer() ) - via.Drill() ) / 2;
861 int excessHoleClearance = holeClearance - annularWidth;
862
863 if( excessHoleClearance > clearance )
864 clearance = excessHoleClearance;
865 }
866
867 m_iface->DisplayItem( &l->Via(), clearance, false, PNS_HEAD_TRACE );
868 }
869 }
870
871 //ITEM_SET tmp( &current );
872
873 updateView( m_placer->CurrentNode( true ), current );
874
875 return ret;
876}
877
878
879void ROUTER::GetUpdatedItems( std::vector<PNS::ITEM*>& aRemoved, std::vector<PNS::ITEM*>& aAdded,
880 std::vector<PNS::ITEM*>& aHeads )
881{
882 NODE *node = nullptr;
883 ITEM_SET current;
884
885 if( m_state == ROUTE_TRACK )
886 {
887 node = m_placer->CurrentNode( true );
888 current = m_placer->Traces();
889 }
890 else if ( m_state == DRAG_SEGMENT )
891 {
892 node = m_dragger->CurrentNode();
893 current = m_dragger->Traces();
894 }
895
896 // There probably should be a debugging assertion and possibly a PNS_LOGGER call here but
897 // I'm not sure how to be proceed WLS.
898 if( !node )
899 return;
900
901 node->GetUpdatedItems( aRemoved, aAdded );
902
903 for( const ITEM* item : current.CItems() )
904 aHeads.push_back( item->Clone() );
905}
906
907
909{
910 if( m_state == ROUTE_TRACK && !m_placer->HasPlacedAnything() )
911 return;
912
913 NODE::ITEM_VECTOR removed;
914 NODE::ITEM_VECTOR added;
915 NODE::ITEM_VECTOR changed;
916
917 aNode->GetUpdatedItems( removed, added );
918
919 for( ITEM* item : removed )
920 {
921 bool is_changed = false;
922
923 // Items in remove/add that share the same parent are just updated versions
924 // We move them to the updated vector to preserve attributes such as UUID and pad data
925 if( item->Parent() )
926 {
927 for( NODE::ITEM_VECTOR::iterator added_it = added.begin();
928 added_it != added.end(); ++added_it )
929 {
930 if( ( *added_it )->Parent() && ( *added_it )->Parent() == item->Parent() )
931 {
932 changed.push_back( *added_it );
933 added.erase( added_it );
934 is_changed = true;
935 break;
936 }
937 }
938 }
939
940 if( !is_changed && !item->IsVirtual() )
941 m_iface->RemoveItem( item );
942 }
943
944 for( ITEM* item : added )
945 {
946 if( !item->IsVirtual() )
947 m_iface->AddItem( item );
948 }
949
950 for( ITEM* item : changed )
951 {
952 if( !item->IsVirtual() )
953 m_iface->UpdateItem( item );
954 }
955
956 m_iface->Commit();
957 m_world->Commit( aNode );
958}
959
960
961bool ROUTER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish, bool aForceCommit )
962{
963 bool rv = false;
964
965 if( m_logger )
966 m_logger->Log( LOGGER::EVT_FIX, aP, aEndItem );
967
968 switch( m_state )
969 {
970 case ROUTE_TRACK:
971 rv = m_placer->FixRoute( aP, aEndItem, aForceFinish );
972 break;
973
974 case DRAG_SEGMENT:
975 case DRAG_COMPONENT:
976 rv = m_dragger->FixRoute( aForceCommit );
977 break;
978
979 default:
980 break;
981 }
982
983 return rv;
984}
985
987{
988 return m_leaderSegments;
989};
990
991
992std::optional<VECTOR2I> ROUTER::UndoLastSegment()
993{
994 if( !RoutingInProgress() )
995 return std::nullopt;
996
997 if( m_logger )
999
1000 return m_placer->UnfixRoute();
1001}
1002
1003
1005{
1006 if( m_state == ROUTE_TRACK )
1007 m_placer->CommitPlacement();
1008
1009 StopRouting();
1010}
1011
1012
1014{
1015 // Update the ratsnest with new changes
1016
1017 if( m_placer )
1018 {
1019 std::vector<NET_HANDLE> nets;
1020 m_placer->GetModifiedNets( nets );
1021
1022 // Update the ratsnest with new changes
1023 for( NET_HANDLE n : nets )
1024 m_iface->UpdateNet( n );
1025 }
1026
1027 if( !RoutingInProgress() )
1028 return;
1029
1030 m_placer.reset();
1031 m_dragger.reset();
1032
1033 m_iface->EraseView();
1034
1035 m_state = IDLE;
1036 m_world->KillChildren();
1037 m_world->ClearRanks();
1038}
1039
1040
1042{
1043 m_iface->EraseView();
1044}
1045
1046
1048{
1049 if( m_state == ROUTE_TRACK )
1050 {
1051 m_placer->FlipPosture();
1052 }
1053}
1054
1055
1056bool ROUTER::SwitchLayer( int aLayer )
1057{
1058 if( m_state == ROUTE_TRACK )
1059 return m_placer->SetLayer( aLayer );
1060
1061 return false;
1062}
1063
1064
1066{
1067 if( m_state == ROUTE_TRACK )
1068 {
1069 bool toggle = !m_placer->IsPlacingVia();
1070 m_placer->ToggleVia( toggle );
1071
1072 if( m_logger )
1073 m_logger->Log( LOGGER::EVT_TOGGLE_VIA, VECTOR2I(), nullptr, &m_sizes );
1074 }
1075}
1076
1077
1078const std::vector<NET_HANDLE> ROUTER::GetCurrentNets() const
1079{
1080 if( m_placer )
1081 return m_placer->CurrentNets();
1082 else if( m_dragger )
1083 return m_dragger->CurrentNets();
1084
1085 return std::vector<NET_HANDLE>();
1086}
1087
1088
1090{
1091 if( m_placer )
1092 return m_placer->CurrentLayer();
1093 else if( m_dragger )
1094 return m_dragger->CurrentLayer();
1095
1096 return -1;
1097}
1098
1099
1101{
1102 return m_logger;
1103}
1104
1105
1107{
1108 if( !m_placer )
1109 return false;
1110
1111 return m_placer->IsPlacingVia();
1112}
1113
1114
1129
1130
1131void ROUTER::SetOrthoMode( bool aEnable )
1132{
1133 if( !m_placer )
1134 return;
1135
1136 m_placer->SetOrthoMode( aEnable );
1137}
1138
1139
1141{
1142 m_mode = aMode;
1143}
1144
1145
1147{
1148 m_iface = aIface;
1149}
1150
1151
1152void ROUTER::BreakSegmentOrArc( ITEM *aItem, const VECTOR2I& aP )
1153{
1154 NODE *node = m_world->Branch();
1155
1156 bool ret = false;
1157
1158 if( aItem->OfKind( ITEM::SEGMENT_T ) )
1159 ret = SplitAdjacentSegments( node, aItem, aP );
1160 else if( aItem->OfKind( ITEM::ARC_T ) )
1161 ret = SplitAdjacentArcs( node, aItem, aP );
1162
1163 if( ret )
1164 {
1165 CommitRouting( node );
1166 }
1167 else
1168 {
1169 delete node;
1170 }
1171}
1172
1173
1175{
1176 if( m_placer )
1177 m_placer->AbortPlacement();
1178}
1179
1180}
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:272
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:241
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Assign a shape to the line (a polyline/line chain).
Definition pns_line.h:135
VIA & Via()
Definition pns_line.h:209
int SegmentCount() const
Definition pns_line.h:148
void SetWidth(int aWidth)
Set line width.
Definition pns_line.h:159
bool EndsWithVia() const
Definition pns_line.h:201
@ EVT_START_MULTIDRAG
Definition pns_logger.h:68
Keep the router "world" - i.e.
Definition pns_node.h:244
std::vector< ITEM * > ITEM_VECTOR
Definition pns_node.h:255
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the expected clearance between items a and b.
Definition pns_node.cpp: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:256
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
Abstract class for a P&S placement/dragging algorithm.
virtual NODE * CurrentNode(bool aLoopsRemoved=false) const =0
Return the most recent board state.
virtual const VECTOR2I & CurrentEnd() const =0
Return the current end of the line(s) being placed/tuned.
virtual const ITEM_SET Traces()=0
Return all routed/tuned traces.
virtual const VECTOR2I & CurrentStart() const =0
Return the current start of the line(s) being placed/tuned.
virtual int CurrentLayer() const =0
Return the layer of currently routed track.
virtual const std::vector< NET_HANDLE > CurrentNets() const =0
Returns the net(s) of currently routed track(s).
virtual DEBUG_DECORATOR * GetDebugDecorator()=0
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:303
void ToggleCornerMode()
PLACEMENT_ALGO * Placer()
Definition pns_router.h:259
std::vector< PNS::ITEM * > GetLastCommittedLeaderSegments()
NODE * m_lastNode
Definition pns_router.h:298
void ClearWorld()
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:261
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:256
LOGGER * Logger()
RouterState m_state
Definition pns_router.h:295
void CommitRouting()
std::unique_ptr< DRAG_ALGO > m_dragger
Definition pns_router.h:301
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:300
bool isStartingPointRoutable(const VECTOR2I &aWhere, ITEM *aItem, int aLayer)
ROUTER_IFACE * m_iface
Definition pns_router.h:305
bool IsPlacingVia() const
void FlipPosture()
RULE_RESOLVER * GetRuleResolver() const
Definition pns_router.h:223
SIZES_SETTINGS m_sizes
Definition pns_router.h:310
DEBUG_DECORATOR * dbg() const
Definition pns_router.h:274
ROUTING_SETTINGS & Settings()
Definition pns_router.h:235
DRAG_ALGO * GetDragger()
Definition pns_router.h:182
bool movePlacing(const VECTOR2I &aP, ITEM *aItem)
bool RoutingInProgress() const
BOX2I m_visibleViewArea
Definition pns_router.h:294
static ROUTER * GetInstance()
std::optional< VECTOR2I > UndoLastSegment()
void AbortPlacement()
LOGGER * m_logger
Definition pns_router.h:312
void SetOrthoMode(bool aEnable)
bool StartDragging(const VECTOR2I &aP, ITEM *aItem, int aDragMode=DM_ANY)
bool StartRouting(const VECTOR2I &aP, ITEM *aItem, int aLayer)
bool areWeLengthTuning() const
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:254
bool FixRoute(const VECTOR2I &aP, ITEM *aItem, bool aForceFinish, bool aForceCommit)
std::unique_ptr< NODE > m_world
Definition pns_router.h:297
void ToggleViaPlacement()
ROUTING_SETTINGS * m_settings
Definition pns_router.h:309
bool hasDiffPairMembers(const ITEM_SET &aItems) const
const std::vector< NET_HANDLE > GetCurrentNets() const
ROUTER_MODE m_mode
Definition pns_router.h:311
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:172
virtual void ClearTemporaryCaches()
Definition pns_node.h:174
virtual int Clearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true)=0
virtual void ClearCaches()
Definition pns_node.h:173
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()
Shortcut for comparisons/overlap tests.
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:823
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:68
@ PNS_MODE_ROUTE_SINGLE
Definition pns_router.h:69
@ PNS_MODE_ROUTE_DIFF_PAIR
Definition pns_router.h:70
@ PNS_MODE_TUNE_DIFF_PAIR
Definition pns_router.h:72
@ PNS_MODE_TUNE_SINGLE
Definition pns_router.h:71
@ PNS_MODE_TUNE_DIFF_PAIR_SKEW
Definition pns_router.h:73
DRAG_MODE
Definition pns_router.h:77
@ 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_DBG(dbg, method,...)
#define PNS_HEAD_TRACE
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:89
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
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:708