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