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