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