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