KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_via_stack.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <algorithm>
21#include <cmath>
22#include <map>
23#include <set>
24#include <tuple>
25#include <vector>
26
28#include <math/util.h>
29
30#include <board.h>
31#include <board_commit.h>
33#include <footprint.h>
34#include <layer_range.h>
35#include <netinfo.h>
36#include <netclass.h>
37#include <pad.h>
38#include <zone.h>
39#include <lset.h>
40#include <padstack.h>
41#include <pcb_base_edit_frame.h>
42#include <pcb_track.h>
43#include <pcbnew_settings.h>
44#include <scoped_set_reset.h>
45#include <wx/choicdlg.h>
46#include <tool/actions.h>
47#include <tool/tool_manager.h>
48#include <view/view.h>
50#include <trigo.h>
51#include <view/view_controls.h>
52
53#include <properties/property.h>
55
58#include <generators_mgr.h>
59#include <drc/drc_engine.h>
60#include <router/router_tool.h>
61#include <router/pns_router.h>
63#include <widgets/wx_infobar.h>
64#include <tools/drawing_tool.h>
66#include <tools/pcb_actions.h>
68#include <tools/pcb_selection.h>
69
70
71const wxString PCB_VIA_STACK::DISPLAY_NAME = _HKI( "Microvia Stack" );
72const wxString PCB_VIA_STACK::GENERATOR_TYPE = wxS( "via_stack" );
73
74
75static wxString styleToString( VIA_STACK_STYLE aStyle )
76{
77 return aStyle == VIA_STACK_STYLE::STAGGERED ? wxS( "staggered" ) : wxS( "stacked" );
78}
79
80
81static VIA_STACK_STYLE styleFromString( const wxString& aStr )
82{
83 return aStr == wxS( "staggered" ) ? VIA_STACK_STYLE::STAGGERED : VIA_STACK_STYLE::STACKED;
84}
85
86
87// Resolve a canonical copper layer name back to its id without needing a board.
88static PCB_LAYER_ID layerFromName( const wxString& aName, PCB_LAYER_ID aFallback )
89{
90 wxString name = aName;
91 int layer = LSET::NameToLayer( name );
92
93 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT && !( layer & 1 ) && IsCopperLayer( layer ) )
94 return ToLAYER_ID( layer );
95
96 return aFallback;
97}
98
99
101 PCB_GENERATOR( aParent, aLayer ),
102 m_startLayer( aLayer ),
105 m_pitch( 0 ),
106 m_filled( true ),
107 m_capped( false ),
108 m_viaSize( 0 ),
109 m_viaDrill( 0 ),
110 m_useNetclass( false ),
111 m_netCode( 0 )
112{
115}
116
117
119{
120 for( BOARD_ITEM* item : GetBoardItems() )
121 {
122 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
123 return bci->GetNetCode();
124 }
125
126 return m_netCode;
127}
128
129
131{
132 m_netCode = aNet;
133
134 for( BOARD_ITEM* item : GetBoardItems() )
135 {
136 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
137 bci->SetNetCode( aNet );
138 }
139}
140
141
143{
144 LSET enabled = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
145
146 auto present = [&]( PCB_LAYER_ID aLayer )
147 {
148 return aLayer >= 0 && aLayer < PCB_LAYER_ID_COUNT && enabled.test( aLayer );
149 };
150
151 return present( aStart ) && present( aEnd ) && aStart != aEnd;
152}
153
154
155void PCB_VIA_STACK::Move( const VECTOR2I& aMoveVector )
156{
157 PCB_GENERATOR::Move( aMoveVector );
158
159 if( m_hops )
160 m_hops->Move( aMoveVector );
161}
162
163
164void PCB_VIA_STACK::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
165{
166 PCB_GENERATOR::Rotate( aRotCentre, aAngle );
167
168 if( m_hops )
169 m_hops->Rotate( aAngle, aRotCentre );
170}
171
172
173void PCB_VIA_STACK::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
174{
175 PCB_GENERATOR::Flip( aCentre, aFlipDirection );
176
177 if( m_hops )
178 m_hops->Mirror( aCentre, aFlipDirection );
179
182}
183
184
185void PCB_VIA_STACK::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
186{
187 PCB_GENERATOR::Mirror( aCentre, aFlipDirection );
188
189 if( m_hops )
190 m_hops->Mirror( aCentre, aFlipDirection );
191}
192
193
194void PCB_VIA_STACK::addMember( BOARD* aBoard, BOARD_COMMIT* aCommit, BOARD_ITEM* aItem )
195{
196 if( aCommit )
197 aCommit->Add( aItem );
198 else
199 aBoard->Add( aItem, ADD_MODE::APPEND, false );
200
201 AddItem( aItem );
202}
203
204
205std::vector<BOARD_ITEM*> PCB_VIA_STACK::BuildMembers( BOARD* aBoard, int aNetCode ) const
206{
207 std::vector<BOARD_ITEM*> members;
208
209 if( !IsSpanValid( aBoard, m_startLayer, m_endLayer ) )
210 return members;
211
212 // Copper layers spanned, in physical stackup order.
213 std::vector<PCB_LAYER_ID> layers;
214
216 layers.push_back( layer );
217
218 if( layers.size() < 2 )
219 return members;
220
221 const int nHops = (int) layers.size() - 1;
222
224 int viaSize;
225 int viaDrill;
226
227 if( m_useNetclass )
228 {
229 NETINFO_ITEM* net = aBoard->FindNet( aNetCode );
230 NETCLASS* nc = net ? net->GetNetClass() : nullptr;
231
232 viaSize = ( nc && nc->HasuViaDiameter() ) ? nc->GetuViaDiameter() : bds.GetCurrentViaSize();
233 viaDrill = ( nc && nc->HasuViaDrill() ) ? nc->GetuViaDrill() : bds.GetCurrentViaDrill();
234 }
235 else
236 {
237 viaSize = m_viaSize > 0 ? m_viaSize : bds.GetCurrentViaSize();
238 viaDrill = m_viaDrill > 0 ? m_viaDrill : bds.GetCurrentViaDrill();
239 }
240
241 auto hopPos = [&]( int aIndex ) -> VECTOR2I
242 {
244 {
245 if( m_hops && aIndex < (int) m_hops->PointCount() )
246 return m_hops->CPoint( aIndex );
247
248 if( m_hops && m_hops->PointCount() > 1 )
249 {
250 int last = m_hops->PointCount() - 1;
251 VECTOR2I step = ( m_hops->CPoint( last ) - m_hops->CPoint( last - 1 ) ).Resize( m_pitch );
252
253 if( step != VECTOR2I( 0, 0 ) )
254 return m_hops->CPoint( last ) + step * ( aIndex - last );
255 }
256
257 return GetPosition() + VECTOR2I( (long long int) m_pitch * aIndex, 0 );
258 }
259
260 return GetPosition(); // stacked hops are coaxial
261 };
262
263 // One microvia per adjacent copper pair.
264 for( int i = 0; i < nHops; ++i )
265 {
266 PCB_VIA* via = new PCB_VIA( aBoard );
267 via->SetNetCode( aNetCode );
268 via->SetViaType( VIATYPE::MICROVIA );
269 via->SetLayerPair( layers[i], layers[i + 1] );
270 via->SetWidth( PADSTACK::ALL_LAYERS, viaSize );
271 via->SetDrill( viaDrill );
272 via->SetPosition( hopPos( i ) );
273
274 // Only a hop reaching an outer layer has a face to cap.
275 bool capped = m_capped && ( IsExternalCopperLayer( layers[i] ) || IsExternalCopperLayer( layers[i + 1] ) );
276
277 // Stacked hops land on filled copper, and capping needs a filled via under it.
278 bool filled = ( m_style == VIA_STACK_STYLE::STACKED ) || m_filled || capped;
279
280 via->Padstack().Drill().is_filled = filled;
281 via->Padstack().Drill().is_capped = capped;
282
283 members.push_back( via );
284 }
285
286 // Staggered stacks need a short trace on each shared landing layer.
288 {
289 // The trace takes the net's width, not whatever the toolbar shows.
290 NETINFO_ITEM* net = aBoard->FindNet( aNetCode );
291 NETCLASS* nc = net ? net->GetNetClass() : nullptr;
292
293 int trackWidth = ( nc && nc->HasTrackWidth() ) ? nc->GetTrackWidth() : bds.GetCurrentTrackWidth();
294
295 for( int i = 0; i < nHops - 1; ++i )
296 {
297 VECTOR2I a = hopPos( i );
298 VECTOR2I b = hopPos( i + 1 );
299
300 if( a == b )
301 continue;
302
303 PCB_TRACK* trace = new PCB_TRACK( aBoard );
304 trace->SetNetCode( aNetCode );
305 trace->SetStart( a );
306 trace->SetEnd( b );
307 trace->SetLayer( layers[i + 1] );
308 trace->SetWidth( trackWidth );
309
310 members.push_back( trace );
311 }
312 }
313
314 return members;
315}
316
317
318// Identifies the hop a member implements. Two members with the same key are interchangeable.
319static std::optional<std::tuple<int, int, int>> memberKey( BOARD_ITEM* aItem )
320{
321 if( aItem->Type() == PCB_VIA_T )
322 {
323 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
324
325 return std::make_tuple( (int) PCB_VIA_T, (int) via->TopLayer(), (int) via->BottomLayer() );
326 }
327
328 if( aItem->Type() == PCB_TRACE_T )
329 return std::make_tuple( (int) PCB_TRACE_T, (int) aItem->GetLayer(), 0 );
330
331 return std::nullopt;
332}
333
334
335bool PCB_VIA_STACK::reuseMembers( BOARD_COMMIT* aCommit, const std::vector<BOARD_ITEM*>& aRebuilt )
336{
337 if( aRebuilt.empty() || aRebuilt.size() != GetItems().size() )
338 return false;
339
340 std::map<std::tuple<int, int, int>, BOARD_ITEM*> live;
341
342 for( BOARD_ITEM* item : GetBoardItems() )
343 {
344 std::optional<std::tuple<int, int, int>> key = memberKey( item );
345
346 if( !key || !live.emplace( *key, item ).second )
347 return false;
348 }
349
350 std::vector<std::pair<BOARD_ITEM*, BOARD_ITEM*>> matched;
351
352 for( BOARD_ITEM* fresh : aRebuilt )
353 {
354 std::optional<std::tuple<int, int, int>> key = memberKey( fresh );
355
356 if( !key )
357 return false;
358
359 auto it = live.find( *key );
360
361 if( it == live.end() )
362 return false;
363
364 matched.emplace_back( it->second, fresh );
365 live.erase( it );
366 }
367
368 for( const auto& [item, fresh] : matched )
369 {
370 if( aCommit )
371 aCommit->Modify( item );
372
373 if( item->Type() == PCB_VIA_T )
374 {
375 PCB_VIA* via = static_cast<PCB_VIA*>( item );
376 PCB_VIA* src = static_cast<PCB_VIA*>( fresh );
377
378 via->SetNetCode( src->GetNetCode() );
379 via->SetPosition( src->GetPosition() );
381 via->SetDrill( src->GetDrillValue() );
382 via->Padstack().Drill().is_filled = src->Padstack().Drill().is_filled;
383 via->Padstack().Drill().is_capped = src->Padstack().Drill().is_capped;
384 }
385 else
386 {
387 PCB_TRACK* trace = static_cast<PCB_TRACK*>( item );
388 PCB_TRACK* src = static_cast<PCB_TRACK*>( fresh );
389
390 trace->SetNetCode( src->GetNetCode() );
391 trace->SetStart( src->GetStart() );
392 trace->SetEnd( src->GetEnd() );
393 trace->SetWidth( src->GetWidth() );
394 }
395 }
396
397 return true;
398}
399
400
402{
403 int net = GetNetCode();
404 std::vector<BOARD_ITEM*> rebuilt = BuildMembers( aBoard, net );
405
406 if( rebuilt.empty() && !GetItems().empty() )
407 return;
408
409 // A drag rebuilds on every motion event. Replacing the members each time would stage an
410 // add and a remove per generation in one commit, and undo brings all of them back.
411 if( reuseMembers( aCommit, rebuilt ) )
412 {
413 for( BOARD_ITEM* item : rebuilt )
414 delete item;
415
416 return;
417 }
418
419 // Remove any existing members. For the commit path the item must be snapshotted for undo
420 // (aCommit->Remove) while it is STILL in this group, otherwise the undo image is ungrouped
421 // and undoing an edit leaves the vias loose. RemoveItem() unlinks it afterwards.
422 for( BOARD_ITEM* item : GetBoardItems() )
423 {
424 if( aCommit )
425 {
426 aCommit->Unmodify( item, nullptr );
427 aCommit->Remove( item );
428 RemoveItem( item );
429 }
430 else
431 {
432 RemoveItem( item );
433 aBoard->Remove( item );
434 delete item;
435 }
436 }
437
438 for( BOARD_ITEM* member : rebuilt )
439 addMember( aBoard, aCommit, member );
440}
441
442
444{
445 if( aCommit )
446 {
447 if( IsNew() )
448 {
449 aCommit->Add( this );
450 }
451 else
452 {
453 // Members must be staged before the move, not after.
454 aCommit->Modify( this, nullptr, RECURSE_MODE::RECURSE );
455 }
456 }
457
458 SetFlags( IN_EDIT );
459}
460
461
463{
464 if( !( GetFlags() & IN_EDIT ) )
465 return false;
466
467 if( !aBoard || !aCommit )
468 return false;
469
470 Regenerate( aBoard, aCommit );
471 return true;
472}
473
474
479
480
485
486
488{
489 if( !aCommit )
490 return;
491
492 for( BOARD_ITEM* item : GetBoardItems() )
493 {
494 aCommit->Unmodify( item, nullptr );
495 aCommit->Remove( item );
496 }
497
498 aCommit->Remove( this );
499}
500
501
502int PCB_VIA_STACK::FindNetAtPosition( BOARD* aBoard, const VECTOR2I& aPosition, PCB_LAYER_ID aLayer )
503{
504 for( FOOTPRINT* footprint : aBoard->Footprints() )
505 {
506 for( PAD* pad : footprint->Pads() )
507 {
508 // A mechanical hole carries no net, and must not hide copper under it.
509 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
510 continue;
511
512 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition ) )
513 return pad->GetNetCode();
514 }
515 }
516
517 for( PCB_TRACK* track : aBoard->Tracks() )
518 {
519 if( track->IsOnLayer( aLayer ) && track->HitTest( aPosition ) )
520 return track->GetNetCode();
521 }
522
523 for( ZONE* zone : aBoard->Zones() )
524 {
525 if( !zone->GetIsRuleArea() && zone->IsOnLayer( aLayer ) && zone->HitTestFilledArea( aLayer, aPosition ) )
526 {
527 return zone->GetNetCode();
528 }
529 }
530
531 return 0;
532}
533
534
535int PCB_VIA_STACK::FindNetAtPosition( BOARD* aBoard, const VECTOR2I& aPosition, const LSET& aLayers )
536{
537 for( PCB_LAYER_ID layer : aLayers.CuStack() )
538 {
539 if( int net = FindNetAtPosition( aBoard, aPosition, layer ) )
540 return net;
541 }
542
543 return 0;
544}
545
546
548{
549 SetStartLayer( aPreset.m_StartLayer );
550 m_endLayer = aPreset.m_EndLayer;
552 m_viaSize = aPreset.m_ViaSize;
553 m_viaDrill = aPreset.m_ViaDrill;
555 m_filled = aPreset.m_Filled;
556 m_capped = aPreset.m_Capped;
557 m_pitch = aPreset.m_Pitch;
558 m_presetName = aPreset.m_Name;
559}
560
561
563{
564 VIA_STACK_PRESET preset;
565
566 preset.m_Name = m_presetName;
567 preset.m_StartLayer = m_startLayer;
568 preset.m_EndLayer = m_endLayer;
570 preset.m_ViaSize = m_viaSize;
571 preset.m_ViaDrill = m_viaDrill;
573 preset.m_Filled = m_filled;
574 preset.m_Capped = m_capped;
575 preset.m_Pitch = m_pitch;
576
577 return preset;
578}
579
580
581// Copper layers in physical stack order, plus a layer -> position lookup.
582static void layerOrder( BOARD* aBoard, std::vector<PCB_LAYER_ID>& aOrder, std::map<int, int>& aOrdinals )
583{
584 int n = 0;
585
586 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, aBoard->GetCopperLayerCount() ) )
587 {
588 aOrder.push_back( layer );
589 aOrdinals[layer] = n++;
590 }
591}
592
593
594PCB_VIA_STACK* PCB_VIA_STACK::CreateFromItems( const std::vector<BOARD_ITEM*>& aItems, BOARD* aBoard,
595 std::vector<BOARD_ITEM*>* aMembers )
596{
597 std::vector<PCB_VIA*> vias;
598 std::vector<PCB_TRACK*> traces;
599
600 for( BOARD_ITEM* item : aItems )
601 {
602 if( item->Type() == PCB_VIA_T )
603 {
604 PCB_VIA* via = static_cast<PCB_VIA*>( item );
605
606 if( via->GetViaType() != VIATYPE::MICROVIA )
607 return nullptr;
608
609 if( dynamic_cast<PCB_GENERATOR*>( via->GetParentGroup() ) )
610 return nullptr;
611
612 if( !vias.empty() && via->GetNetCode() != vias.front()->GetNetCode() )
613 return nullptr;
614
615 vias.push_back( via );
616 }
617 else if( item->Type() == PCB_TRACE_T )
618 {
619 traces.push_back( static_cast<PCB_TRACK*>( item ) );
620 }
621 else
622 {
623 return nullptr;
624 }
625 }
626
627 if( vias.size() < 2 )
628 return nullptr;
629
630 std::vector<PCB_LAYER_ID> order;
631 std::map<int, int> ordinals;
632 layerOrder( aBoard, order, ordinals );
633
634 // Each via must span one hop and together they must tile a contiguous range.
635 std::map<int, PCB_VIA*> hopByUpper;
636
637 for( PCB_VIA* via : vias )
638 {
639 auto top = ordinals.find( via->TopLayer() );
640 auto bot = ordinals.find( via->BottomLayer() );
641
642 if( top == ordinals.end() || bot == ordinals.end() )
643 return nullptr;
644
645 if( std::abs( top->second - bot->second ) != 1 )
646 return nullptr;
647
648 int upper = std::min( top->second, bot->second );
649
650 if( hopByUpper.count( upper ) )
651 return nullptr;
652
653 hopByUpper[upper] = via;
654 }
655
656 int first = hopByUpper.begin()->first;
657 int last = hopByUpper.rbegin()->first;
658
659 if( (int) hopByUpper.size() != last - first + 1 )
660 return nullptr;
661
662 PCB_VIA* topVia = hopByUpper.begin()->second;
663
664 bool coaxial = true;
665
666 for( PCB_VIA* via : vias )
667 {
668 if( via->GetPosition() != topVia->GetPosition() )
669 coaxial = false;
670 }
671
672 PCB_VIA_STACK* stack = new PCB_VIA_STACK( aBoard, F_Cu );
673
674 stack->SetStartLayer( order[first] );
675 stack->SetEndLayer( order[last + 1] );
676 stack->SetViaSize( topVia->GetWidth( PADSTACK::ALL_LAYERS ) );
677 stack->SetViaDrill( topVia->GetDrillValue() );
678 stack->SetNetCode( topVia->GetNetCode() );
679 stack->SetFilled( topVia->Padstack().Drill().is_filled.value_or( aBoard->GetDesignSettings().m_FillVias ) );
680
681 // Only a hop reaching an outer layer carries capping, so only those can say the stack is capped.
682 bool capped = false;
683
684 for( PCB_VIA* via : vias )
685 {
686 if( IsExternalCopperLayer( via->TopLayer() ) || IsExternalCopperLayer( via->BottomLayer() ) )
687 capped = capped || via->Padstack().Drill().is_capped.value_or( false );
688 }
689
690 stack->SetCapped( capped );
691 stack->SetPosition( topVia->GetPosition() );
692
693 if( coaxial )
694 {
696 stack->SetFilled( true );
697 }
698 else
699 {
701
702 SHAPE_LINE_CHAIN hops;
703
704 for( const auto& [upper, via] : hopByUpper )
705 hops.Append( via->GetPosition(), true );
706
707 stack->SetHops( hops );
708 stack->SetPitch( ( hops.CPoint( 1 ) - hops.CPoint( 0 ) ).EuclideanNorm() );
709 }
710
711 // Only traces that connect hop positions on a shared landing layer belong to the stack,
712 // anything else must stay loose or the next regenerate would delete it.
713 if( aMembers )
714 {
715 std::set<int> landing;
716 std::set<VECTOR2I> viaPositions;
717
718 for( int i = first + 1; i <= last; ++i )
719 landing.insert( order[i] );
720
721 for( PCB_VIA* via : vias )
722 viaPositions.insert( via->GetPosition() );
723
724 for( PCB_VIA* via : vias )
725 aMembers->push_back( via );
726
727 for( PCB_TRACK* trace : traces )
728 {
729 if( landing.count( trace->GetLayer() ) && trace->GetStart() != trace->GetEnd()
730 && trace->GetNetCode() == stack->GetNetCode() && viaPositions.count( trace->GetStart() )
731 && viaPositions.count( trace->GetEnd() ) )
732 {
733 aMembers->push_back( trace );
734 }
735 }
736 }
737
738 return stack;
739}
740
741
742// A loose microvia crossing more than one layer step is what an expansion turns into a stack.
743static bool isExpandableMicrovia( const std::map<int, int>& aOrdinals, PCB_VIA* aVia )
744{
745 if( aVia->GetViaType() != VIATYPE::MICROVIA || aVia->GetParentGroup() )
746 return false;
747
748 auto top = aOrdinals.find( aVia->TopLayer() );
749 auto bot = aOrdinals.find( aVia->BottomLayer() );
750
751 if( top == aOrdinals.end() || bot == aOrdinals.end() )
752 return false;
753
754 return std::abs( top->second - bot->second ) > 1;
755}
756
757
759{
760 std::vector<PCB_LAYER_ID> order;
761 std::map<int, int> ordinals;
762 layerOrder( aBoard, order, ordinals );
763
764 std::set<KIID> ids;
765
766 for( PCB_TRACK* track : aBoard->Tracks() )
767 {
768 if( track->Type() == PCB_VIA_T && isExpandableMicrovia( ordinals, static_cast<PCB_VIA*>( track ) ) )
769 ids.insert( track->m_Uuid );
770 }
771
772 return ids;
773}
774
775
777 const std::function<const VIA_STACK_PRESET*( PCB_VIA* )>& aMatcher )
778{
779 std::vector<PCB_LAYER_ID> order;
780 std::map<int, int> ordinals;
781 layerOrder( aBoard, order, ordinals );
782
783 std::vector<std::pair<PCB_VIA*, const VIA_STACK_PRESET*>> candidates;
784
785 for( PCB_TRACK* track : aBoard->Tracks() )
786 {
787 if( track->Type() != PCB_VIA_T )
788 continue;
789
790 PCB_VIA* via = static_cast<PCB_VIA*>( track );
791
792 if( !isExpandableMicrovia( ordinals, via ) )
793 continue;
794
795 const VIA_STACK_PRESET* preset = nullptr;
796
797 if( aMatcher )
798 {
799 preset = aMatcher( via );
800
801 if( !preset )
802 continue;
803 }
804
805 candidates.push_back( { via, preset } );
806 }
807
808 for( const auto& [via, preset] : candidates )
809 {
811 PCB_LAYER_ID bottom;
812 via->LayerPair( &top, &bottom );
813
814 PCB_VIA_STACK* stack = new PCB_VIA_STACK( aBoard, F_Cu );
815
816 if( preset )
817 stack->ApplyPreset( *preset );
818
819 stack->SetStartLayer( top );
820 stack->SetEndLayer( bottom );
822 stack->SetViaSize( via->GetWidth( PADSTACK::ALL_LAYERS ) );
823 stack->SetViaDrill( via->GetDrillValue() );
824 stack->SetNetCode( via->GetNetCode() );
825 stack->SetFilled( true );
826 stack->SetPosition( via->GetPosition() );
827
828 if( aCommit )
829 {
830 aCommit->Remove( via );
831 aCommit->Add( stack );
832 }
833 else
834 {
835 aBoard->Remove( via );
836 delete via;
837 aBoard->Add( stack, ADD_MODE::APPEND, false );
838 }
839
840 stack->Regenerate( aBoard, aCommit );
841 }
842
843 return (int) candidates.size();
844}
845
846
848{
849 // Dialog edits a scratch copy so the undo snapshot below sees the pre-edit settings.
850 PCB_VIA_STACK temp( GetBoard(), GetLayer() );
851 temp.ApplyPreset( ToPreset() );
852
853 DIALOG_MICROVIA_STACK dlg( aEditFrame, &temp );
854
855 if( dlg.ShowModal() != wxID_OK )
856 return;
857
858 BOARD_COMMIT commit( aEditFrame );
859 GENERATOR_TOOL* tool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
860
861 commit.Modify( this );
862 ApplyPreset( temp.ToPreset() );
863
864 // A staggered stack stores explicit hop positions, so re-space them to the (possibly new)
865 // pitch while keeping each hop's direction. Without this, editing the pitch changes the
866 // value but not the distance between vias.
867 if( m_style == VIA_STACK_STYLE::STAGGERED && m_hops && m_hops->PointCount() >= 2 )
868 {
869 SHAPE_LINE_CHAIN rescaled;
870 rescaled.Append( m_hops->CPoint( 0 ) );
871
872 for( int i = 1; i < m_hops->PointCount(); ++i )
873 {
874 VECTOR2I dir = m_hops->CPoint( i ) - m_hops->CPoint( i - 1 );
875
876 if( dir.EuclideanNorm() > 0 )
877 dir = dir.Resize( m_pitch );
878
879 rescaled.Append( rescaled.CPoint( i - 1 ) + dir, true );
880 }
881
882 SetHops( rescaled );
883 }
884
885 EditStart( tool, GetBoard(), &commit );
886
887 // If the stack has no net, inherit it from the copper it now sits on. This is the
888 // supported way to net a stack, the connecting traces are generator owned and locked.
889 // SetNetCode writes through to the members, so it has to follow EditStart staging them.
891 {
892 LSET span;
893
894 for( PCB_LAYER_ID layer : LAYER_RANGE( m_startLayer, m_endLayer, GetBoard()->GetCopperLayerCount() ) )
895 span.set( layer );
896
898 }
899
900 Update( tool, GetBoard(), &commit );
901 EditFinish( tool, GetBoard(), &commit );
902
903 commit.Push( GetCommitMessage() );
904}
905
906
908{
910
911 props.set( "start_layer", LSET::Name( m_startLayer ) );
912 props.set( "end_layer", LSET::Name( m_endLayer ) );
913 props.set( "style", styleToString( m_style ) );
914 props.set( "filled", m_filled );
915 props.set( "capped", m_capped );
916 props.set( "use_netclass", m_useNetclass );
917 props.set( "preset", m_presetName );
918
919 props.set_iu( "pitch", m_pitch );
920 props.set_iu( "via_size", m_viaSize );
921 props.set_iu( "via_drill", m_viaDrill );
922
923 if( m_hops )
924 props.set( "hops", wxAny( *m_hops ) );
925
926 return props;
927}
928
929
931{
933
934 wxString layerName;
935
936 if( aProps.get_to( "start_layer", layerName ) )
938
939 if( aProps.get_to( "end_layer", layerName ) )
940 m_endLayer = layerFromName( layerName, m_endLayer );
941
942 wxString style;
943
944 if( aProps.get_to( "style", style ) )
945 m_style = styleFromString( style );
946
947 aProps.get_to( "filled", m_filled );
948 aProps.get_to( "capped", m_capped );
949 aProps.get_to( "use_netclass", m_useNetclass );
950 aProps.get_to( "preset", m_presetName );
951
952 aProps.get_to_iu( "pitch", m_pitch );
953 aProps.get_to_iu( "via_size", m_viaSize );
954 aProps.get_to_iu( "via_drill", m_viaDrill );
955
956 if( auto hops = aProps.get_opt<SHAPE_LINE_CHAIN>( "hops" ) )
957 m_hops = *hops;
958}
959
960
962
963
965{
966 // Consume any router handoff seed (a pre-anchored staggered stack).
967 bool seeded = m_viaStackSeeded;
968 VECTOR2I seedPos = m_viaStackSeedPos;
971 m_viaStackSeeded = false;
972
974 return 0;
975
976 if( m_inDrawingTool )
977 return 0;
978
980
982 m_frame->PushTool( aEvent );
983 Activate();
984
985 BOARD* board = m_frame->GetBoard();
986 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
987
988 PCB_VIA_STACK settings( board, F_Cu );
989
990 // Place silently with the preset currently selected in the toolbar, matching how via
991 // placement uses the current via size. Only fall back to the properties dialog when no
992 // presets are defined yet, so there is still a way to place and define one.
993 const std::vector<VIA_STACK_PRESET>& presets = board->GetDesignSettings().m_ViaStackPresets;
994 int idx = board->GetDesignSettings().GetViaStackIndex();
995
996 if( !presets.empty() )
997 {
998 settings.ApplyPreset( presets[std::clamp( idx, 0, (int) presets.size() - 1 )] );
999 }
1000 else if( !seeded )
1001 {
1002 DIALOG_MICROVIA_STACK dlg( m_frame, &settings );
1003
1004 if( dlg.ShowModal() != wxID_OK )
1005 {
1006 m_frame->PopTool( aEvent );
1007 return 0;
1008 }
1009 }
1010
1011 // A router handoff anchors the span to the routed layers and is always a staggered walk.
1012 if( seeded )
1013 {
1014 settings.SetStartLayer( seedStart );
1015 settings.SetEndLayer( seedEnd );
1017 }
1018
1019 bool staggered = settings.GetStyle() == VIA_STACK_STYLE::STAGGERED;
1020
1021 if( !PCB_VIA_STACK::IsSpanValid( board, settings.GetStartLayer(), settings.GetEndLayer() ) )
1022 {
1023 m_frame->ShowInfoBarError( _( "The microvia stack layers are not present on this board." ) );
1024 m_frame->PopTool( aEvent );
1025 return 0;
1026 }
1027
1028 std::vector<PCB_LAYER_ID> spanLayers;
1029
1030 for( PCB_LAYER_ID layer :
1031 LAYER_RANGE( settings.GetStartLayer(), settings.GetEndLayer(), board->GetCopperLayerCount() ) )
1032 spanLayers.push_back( layer );
1033
1034 LSET spanSet;
1035
1036 for( PCB_LAYER_ID layer : spanLayers )
1037 spanSet.set( layer );
1038
1039 int nHops = (int) spanLayers.size() - 1;
1040
1041 // A zero pitch would pin the steering circle onto a single point.
1042 if( staggered && settings.GetPitch() <= 0 )
1043 {
1044 int dia = settings.GetViaSize() > 0 ? settings.GetViaSize() : board->GetDesignSettings().GetCurrentViaSize();
1045 settings.SetPitch( dia * 2 );
1046 }
1047
1048 if( nHops < 1 )
1049 {
1050 m_frame->PopTool( aEvent );
1051 return 0;
1052 }
1053
1054 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1055 PCB_SELECTION preview;
1056 std::vector<BOARD_ITEM*> previewItems;
1057 std::vector<VECTOR2I> hops;
1058 VECTOR2I lastDir( 1, 0 );
1059 bool resumeRouting = false; // seeded handoff places one and routes on
1060 VECTOR2I resumePos;
1061 VECTOR2I lastViaPos;
1062
1063 // Router handoff: the first hop is fixed at the routed head, the user steers the rest.
1064 if( seeded )
1065 hops.push_back( seedPos );
1066
1067 m_view->Add( &preview );
1068
1069 auto clearPreview = [&]()
1070 {
1071 preview.Clear();
1072
1073 for( BOARD_ITEM* item : previewItems )
1074 delete item;
1075
1076 previewItems.clear();
1077 };
1078
1079 // DRC context, shared with the plain via tool so a stack is refused over foreign copper the
1080 // same way (unless "Allow DRC violations" is enabled in the router settings).
1081 std::shared_ptr<DRC_ENGINE> drcEngine = board->GetDesignSettings().m_DRCEngine;
1082 int drcEpsilon = board->GetDesignSettings().GetDRCEpsilon();
1083 int worstClearance = ComputeWorstViaClearance( m_frame, drcEngine.get() );
1084 bool allowDRCViolations = false;
1085
1086 if( ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>() )
1087 allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
1088
1089 auto updatePreview = [&]( const VECTOR2I& aCursor )
1090 {
1091 clearPreview();
1092
1093 if( staggered )
1094 {
1096
1097 for( const VECTOR2I& p : hops )
1098 chain.Append( p, true );
1099
1100 chain.Append( aCursor, true );
1101 settings.SetHops( chain );
1102 settings.SetPosition( hops.empty() ? aCursor : hops.front() );
1103
1104 // Only preview the placed hops plus the one being steered, not the
1105 // rest of the span extrapolated in a straight line.
1106 int previewHops = std::min( (int) hops.size() + 1, nHops );
1107 settings.SetEndLayer( spanLayers[previewHops] );
1108 }
1109 else
1110 {
1111 settings.SetPosition( aCursor );
1112 }
1113
1114 previewItems = settings.BuildMembers( board, 0 );
1115
1116 for( BOARD_ITEM* item : previewItems )
1117 preview.Add( item );
1118
1119 m_view->Update( &preview );
1120 };
1121
1122 auto placeStack = [&]( const VECTOR2I& aCursor ) -> bool
1123 {
1124 BOARD_COMMIT commit( m_frame );
1125 PCB_VIA_STACK* stack = static_cast<PCB_VIA_STACK*>( settings.Clone() );
1126 GENERATOR_TOOL* genTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
1127
1128 // Clone keeps the source uuid, each placed stack needs its own.
1129 stack->SetUuidDirect( KIID() );
1130 stack->SetParent( board );
1131
1132 // The preview may have truncated the span, place the full one.
1133 stack->SetEndLayer( spanLayers.back() );
1134
1135 if( staggered )
1136 {
1138
1139 for( const VECTOR2I& p : hops )
1140 chain.Append( p, true );
1141
1142 stack->SetHops( chain );
1143 stack->SetPosition( hops.front() );
1144 }
1145 else
1146 {
1147 stack->ClearHops();
1148 stack->SetPosition( aCursor );
1149 }
1150
1151 // Inherit the net of whatever the first hop lands on.
1152 stack->SetNetCode( PCB_VIA_STACK::FindNetAtPosition( board, stack->GetPosition(), spanSet ) );
1153 stack->SetFlags( IS_NEW );
1154
1155 // Refuse placement over foreign copper, matching the plain via tool. Build the
1156 // stack's vias off-board and check each before anything is committed.
1157 std::vector<BOARD_ITEM*> candidates = stack->BuildMembers( board, stack->GetNetCode() );
1158 bool violates = false;
1159
1160 for( BOARD_ITEM* item : candidates )
1161 {
1162 if( BOARD_CONNECTED_ITEM* copper = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
1163 {
1164 if( CheckItemDRCViolation( copper, m_frame, drcEngine.get(), worstClearance, drcEpsilon ) )
1165 {
1166 violates = true;
1167 break;
1168 }
1169 }
1170 }
1171
1172 for( BOARD_ITEM* item : candidates )
1173 delete item;
1174
1175 if( violates && !allowDRCViolations )
1176 {
1177 m_frame->ShowInfoBarError( _( "Microvia stack location violates DRC." ), true,
1178 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
1179 delete stack;
1180 return false;
1181 }
1182
1183 stack->EditStart( genTool, board, &commit );
1184 stack->Update( genTool, board, &commit );
1185 stack->EditFinish( genTool, board, &commit );
1186
1187 stack->ClearFlags( IS_NEW );
1188
1189 // Only the last hop reaches the end layer, and a staggered one is not at the cursor.
1190 for( BOARD_ITEM* item : stack->GetBoardItems() )
1191 {
1192 PCB_VIA* via = dynamic_cast<PCB_VIA*>( item );
1193
1194 if( via && via->IsOnLayer( stack->GetEndLayer() ) )
1195 {
1196 lastViaPos = via->GetPosition();
1197 break;
1198 }
1199 }
1200
1201 commit.Push( _( "Place Microvia Stack" ) );
1202 return true;
1203 };
1204
1205 // Snap the anchor onto nearby copper, using the plain via tool's routines so tracks,
1206 // arcs and pads all behave the same. A stack may land on any layer it spans, so the
1207 // probe carries the whole span rather than a single layer.
1208 int snapViaDia = settings.GetViaSize() > 0 ? settings.GetViaSize() : board->GetDesignSettings().GetCurrentViaSize();
1209
1210 PCB_VIA probe( board );
1212 probe.SetWidth( PADSTACK::ALL_LAYERS, snapViaDia );
1213 probe.SetLayerPair( settings.GetStartLayer(), settings.GetEndLayer() );
1214
1215 auto snapToCopper = [&]( const VECTOR2I& aCursor, bool aItemSnap ) -> VECTOR2I
1216 {
1217 MAGNETIC_SETTINGS* mag = m_frame->GetMagneticItemsSettings();
1218
1219 // The loop clears grid snap for the ResolveSnap base, re-enable it here so
1220 // AlignToSegment actually aligns, matching VIA_PLACER::SnapItem.
1221 grid.SetSnap( aItemSnap );
1222
1223 if( aItemSnap && mag )
1224 {
1226 {
1227 if( PCB_TRACK* track = FindSnapTrack( m_frame, &probe, aCursor, spanSet, ViaSnapRange( m_frame ) ) )
1228 return grid.AlignToSegment( aCursor, SEG( track->GetStart(), track->GetEnd() ) );
1229 }
1230
1231 if( mag->pads != MAGNETIC_OPTIONS::NO_EFFECT )
1232 {
1233 if( PAD* pad = FindSnapPad( m_frame, &probe, aCursor, spanSet, ViaSnapRange( m_frame ) ) )
1234 return pad->GetPosition();
1235 }
1236 }
1237
1238 return grid.ResolveSnap( aCursor, nullptr ).position;
1239 };
1240
1241 auto setCursor = [&]()
1242 {
1243 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
1244 };
1245
1246 m_controls->ShowCursor( true );
1247 m_controls->SetAutoPan( true );
1248 setCursor();
1249
1250 while( TOOL_EVENT* evt = Wait() )
1251 {
1252 setCursor();
1253
1254 // Match the via tool: item snaps are handled below, the grid helper only does
1255 // grid and anchor snapping against the GAL grid state.
1256 grid.SetSnap( false );
1257 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1258
1259 bool allowItemSnap = !evt->Modifier( MD_SHIFT );
1260 VECTOR2I rawCursor = m_controls->GetMousePosition();
1261 VECTOR2I cursorPos;
1262
1263 if( staggered && !hops.empty() )
1264 {
1265 // Cursor is locked onto a circle of radius = pitch around the previous hop.
1266 // It steers direction only, following the editor angle mode.
1267 VECTOR2I dir = rawCursor - hops.back();
1268
1269 if( dir.EuclideanNorm() < 1 )
1270 dir = VECTOR2I( 1, 0 );
1271
1272 LEADER_MODE angleSnap = GetAngleSnapMode();
1273
1274 if( evt->Modifier( MD_CTRL ) )
1275 angleSnap = LEADER_MODE::DIRECT;
1276
1277 if( angleSnap != LEADER_MODE::DIRECT )
1278 {
1279 double step = ( angleSnap == LEADER_MODE::DEG90 ) ? M_PI / 2 : M_PI / 4;
1280 double ang = atan2( (double) dir.y, (double) dir.x );
1281 ang = round( ang / step ) * step;
1282
1283 cursorPos = hops.back()
1284 + VECTOR2I( KiROUND( cos( ang ) * settings.GetPitch() ),
1285 KiROUND( sin( ang ) * settings.GetPitch() ) );
1286 }
1287 else
1288 {
1289 cursorPos = hops.back() + dir.Resize( settings.GetPitch() );
1290 }
1291
1292 lastDir = cursorPos - hops.back();
1293 }
1294 else
1295 {
1296 cursorPos = snapToCopper( rawCursor, allowItemSnap );
1297 }
1298
1299 // Only the staggered pitch circle locks the cursor. For the anchor and the
1300 // stacked case let the cross follow the mouse and snap the preview item instead,
1301 // exactly like plain via placement.
1302 if( staggered && !hops.empty() )
1303 m_controls->ForceCursorPosition( true, cursorPos );
1304 else
1305 m_controls->ForceCursorPosition( false );
1306
1307 if( evt->IsCancelInteractive() || evt->IsActivate() )
1308 {
1309 break;
1310 }
1311 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1312 {
1313 // The tool stays active for repeated placement, Esc leaves. Rapid clicks
1314 // arrive as double clicks and must place hops too.
1315 if( staggered )
1316 {
1317 hops.push_back( cursorPos );
1318
1319 if( (int) hops.size() >= nHops )
1320 {
1321 if( !placeStack( cursorPos ) )
1322 {
1323 // Blocked by DRC, let the user reposition the final hop and retry.
1324 hops.pop_back();
1325 }
1326 else
1327 {
1328 hops.clear();
1329 clearPreview();
1330
1331 // A router handoff places exactly one stack, then returns to routing.
1332 if( seeded )
1333 {
1334 resumeRouting = true;
1335 resumePos = lastViaPos;
1336 break;
1337 }
1338 }
1339 }
1340 else
1341 {
1342 // Show the next hop right away, continuing in the last direction,
1343 // instead of waiting for the mouse to move.
1344 cursorPos = hops.back() + lastDir.Resize( settings.GetPitch() );
1345 m_controls->ForceCursorPosition( true, cursorPos );
1346 updatePreview( cursorPos );
1347 }
1348 }
1349 else
1350 {
1351 if( placeStack( cursorPos ) )
1352 {
1353 clearPreview();
1354
1355 if( seeded )
1356 {
1357 resumeRouting = true;
1358 resumePos = lastViaPos;
1359 break;
1360 }
1361 }
1362 }
1363 }
1364 else if( evt->IsAction( &ACTIONS::undo ) || evt->IsAction( &ACTIONS::doDelete ) )
1365 {
1366 // Swallowed for the whole loop like the router does, or it would undo the
1367 // route this placement was handed off from.
1368 if( staggered && hops.size() > ( seeded ? 1u : 0u ) )
1369 {
1370 hops.pop_back();
1371 lastDir = VECTOR2I( 1, 0 );
1372 updatePreview( cursorPos );
1373 }
1374 }
1375 else if( evt->IsClick( BUT_RIGHT ) )
1376 {
1377 m_toolMgr->VetoContextMenuMouseWarp();
1378 m_menu->ShowContextMenu( selection() );
1379 }
1380 else if( evt->IsMotion() )
1381 {
1382 updatePreview( cursorPos );
1383 }
1384 else
1385 {
1386 evt->SetPassEvent();
1387 }
1388 }
1389
1390 clearPreview();
1391 m_view->Remove( &preview );
1392 m_controls->ForceCursorPosition( false );
1393 m_controls->SetAutoPan( false );
1394 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1395 m_frame->PopTool( aEvent );
1396
1397 // Return to routing from the last placed via when handed off from the router. Do not warp
1398 // the view (last arg false), recentering the canvas mid-workflow is jarring, unlike a via.
1399 // Priming starts the route at the via itself, not at whatever the next click snaps to.
1400 if( resumeRouting )
1401 {
1402 m_frame->SetActiveLayer( settings.GetEndLayer() );
1403
1404 if( ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>() )
1405 router->SetViaStackResumeLayer( settings.GetEndLayer() );
1406
1407 m_controls->WarpMouseCursor( resumePos, true, false );
1409 m_toolMgr->PrimeTool( resumePos );
1410 }
1411
1412 return 0;
1413}
1414
1415
1417
1418
1420
1421
1423{
1425 {
1427 .Map( VIA_STACK_STYLE::STACKED, _HKI( "Stacked" ) )
1428 .Map( VIA_STACK_STYLE::STAGGERED, _HKI( "Staggered" ) );
1429
1436
1438
1439 if( layerEnum.Choices().GetCount() == 0 )
1440 {
1441 layerEnum.Undefined( UNDEFINED_LAYER );
1442
1443 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
1444 layerEnum.Map( layer, LSET::Name( layer ) );
1445 }
1446
1447 // A stack spans layers, so the single inherited layer is meaningless, as for a via.
1448 propMgr.Mask( TYPE_HASH( PCB_VIA_STACK ), TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) );
1449
1450 // Read-only: a stack's geometry is rebuilt from these, and the Properties panel has no
1451 // way to trigger that rebuild. Editing goes through the stack's own dialog.
1452 const wxString groupStack = _HKI( "Microvia Stack Properties" );
1453
1454 auto startLayer = new PROPERTY_ENUM<PCB_VIA_STACK, PCB_LAYER_ID>(
1456 startLayer->SetChoices( layerEnum.Choices() );
1457 propMgr.AddProperty( startLayer, groupStack );
1458
1461 endLayer->SetChoices( layerEnum.Choices() );
1462 propMgr.AddProperty( endLayer, groupStack );
1463
1464 propMgr.AddProperty(
1467 groupStack );
1468
1471
1472 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Use Netclass Values" ),
1473 NO_SETTER( PCB_VIA_STACK, bool ),
1475 groupStack );
1476
1477 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Via Diameter" ), NO_SETTER( PCB_VIA_STACK, int ),
1479 groupStack );
1480
1481 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Via Hole" ), NO_SETTER( PCB_VIA_STACK, int ),
1483 groupStack );
1484
1485 auto isStaggered = []( INSPECTABLE* aItem ) -> bool
1486 {
1487 if( PCB_VIA_STACK* stack = dynamic_cast<PCB_VIA_STACK*>( aItem ) )
1488 return stack->GetStyle() == VIA_STACK_STYLE::STAGGERED;
1489
1490 return false;
1491 };
1492
1493 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Pitch" ), NO_SETTER( PCB_VIA_STACK, int ),
1495 groupStack )
1496 .SetAvailableFunc( isStaggered );
1497
1498 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Copper-filled" ),
1499 NO_SETTER( PCB_VIA_STACK, bool ),
1501 groupStack );
1502
1503 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Capped" ), NO_SETTER( PCB_VIA_STACK, bool ),
1505 groupStack );
1506 }
const char * name
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
BASE_SET & set(size_t pos)
Definition base_set.h:126
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
friend class BOARD
Definition board_item.h:578
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1497
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2980
const ZONES & Zones() const
Definition board.h:467
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayer) const
Definition board.cpp:1124
int GetCopperLayerCount() const
Definition board.cpp:1131
const FOOTPRINTS & Footprints() const
Definition board.h:463
const TRACKS & Tracks() const
Definition board.h:461
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1668
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
void Unmodify(EDA_ITEM *aItem, BASE_SCREEN *aScreen)
Definition commit.cpp:197
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
int ShowModal() override
VECTOR2I m_viaStackSeedPos
PCB_LAYER_ID m_viaStackSeedStart
PCB_LAYER_ID m_viaStackSeedEnd
bool m_viaStackSeeded
KIGFX::VIEW_CONTROLS * m_controls
KIGFX::VIEW * m_view
int PlaceMicroviaStack(const TOOL_EVENT &aEvent)
Place a stacked or staggered microvia stack.
PCB_BASE_EDIT_FRAME * m_frame
wxString m_name
Definition eda_group.h:92
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:64
void RemoveItem(EDA_ITEM *aItem)
Remove item from group.
Definition eda_group.cpp:77
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
bool IsNew() const
Definition eda_item.h:131
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:776
static ENUM_MAP< T > & Instance()
Definition property.h:770
ENUM_MAP & Undefined(T aValue)
Definition property.h:783
wxPGChoices & Choices()
Definition property.h:821
Handle actions specific to filling copper zones.
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
Definition kiid.h:46
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ CuStack() const
Return a sequence of copper layers in starting from the front/top and extending to the back/bottom.
Definition lset.cpp:259
static int NameToLayer(wxString &aName)
Return the layer number from a layer name.
Definition lset.cpp:113
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool HasuViaDrill() const
Definition netclass.h:170
int GetuViaDrill() const
Definition netclass.h:171
bool HasuViaDiameter() const
Definition netclass.h:162
int GetuViaDiameter() const
Definition netclass.h:163
bool HasTrackWidth() const
Definition netclass.h:138
int GetTrackWidth() const
Definition netclass.h:139
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:101
DRILL_PROPS & Drill()
Definition padstack.h:361
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
static TOOL_ACTION routeSingleTrack
Activation of the Push and Shove router.
Common, abstract interface for edit frames.
virtual void SetProperties(const STRING_ANY_MAP &aProps)
wxString m_generatorType
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
PCB_GENERATOR(BOARD_ITEM *aParent, PCB_LAYER_ID aLayer)
void Mirror(const VECTOR2I &aCentre, FLIP_DIRECTION aMirrorDirection) override
Mirror this object relative to a given horizontal axis the layer is not changed.
void SetPosition(const VECTOR2I &aPos) override
VECTOR2I GetPosition() const override
void Move(const VECTOR2I &aMoveVector) override
Move this object.
virtual const STRING_ANY_MAP GetProperties() const
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
LEADER_MODE GetAngleSnapMode() const
Get the current angle snapping mode.
BOARD * board() const
const PCB_SELECTION & selection() const
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
virtual void SetWidth(int aWidth)
Definition pcb_track.h:86
virtual int GetWidth() const
Definition pcb_track.h:87
A microvia stack: several single hop microvias, plus connecting traces for staggered stacks,...
VIA_STACK_PRESET ToPreset() const
void SetHops(const SHAPE_LINE_CHAIN &aHops)
void EditCancel(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
bool Update(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void ApplyPreset(const VIA_STACK_PRESET &aPreset)
wxString m_presetName
static int ExpandMultiHopMicrovias(BOARD *aBoard, BOARD_COMMIT *aCommit, const std::function< const VIA_STACK_PRESET *(PCB_VIA *)> &aMatcher=nullptr)
Replace loose microvias spanning more than one hop with stacks of single hop microvias.
void SetProperties(const STRING_ANY_MAP &aProps) override
void SetPitch(int aPitch)
static PCB_VIA_STACK * CreateFromItems(const std::vector< BOARD_ITEM * > &aItems, BOARD *aBoard, std::vector< BOARD_ITEM * > *aMembers=nullptr)
Build a stack from existing board items (microvias plus optional connecting traces).
static std::set< KIID > CollectExpandableMicrovias(BOARD *aBoard)
Ids of the vias ExpandMultiHopMicrovias would consider right now.
static const wxString GENERATOR_TYPE
void EditStart(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void Mirror(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Mirror this object relative to a given horizontal axis the layer is not changed.
PCB_LAYER_ID m_endLayer
static int FindNetAtPosition(BOARD *aBoard, const VECTOR2I &aPosition, PCB_LAYER_ID aLayer)
Net of the copper under a point on the given layer, pads first, then tracks, then filled zones.
VIA_STACK_STYLE m_style
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
std::optional< SHAPE_LINE_CHAIN > m_hops
void EditFinish(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
void Remove(GENERATOR_TOOL *aTool, BOARD *aBoard, BOARD_COMMIT *aCommit) override
bool IsCapped() const
void Regenerate(BOARD *aBoard, BOARD_COMMIT *aCommit)
Rebuild the member vias (and, for a staggered stack, the connecting traces) from the current stack se...
void addMember(BOARD *aBoard, BOARD_COMMIT *aCommit, BOARD_ITEM *aItem)
int GetViaDrill() const
bool IsFilled() const
bool reuseMembers(BOARD_COMMIT *aCommit, const std::vector< BOARD_ITEM * > &aRebuilt)
static const wxString DISPLAY_NAME
VIA_STACK_STYLE GetStyle() const
void SetFilled(bool aFilled)
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
PCB_LAYER_ID m_startLayer
int GetNetCode() const
PCB_LAYER_ID GetStartLayer() const
void SetStartLayer(PCB_LAYER_ID aLayer)
PCB_LAYER_ID GetEndLayer() const
bool GetUseNetclass() const
std::vector< BOARD_ITEM * > BuildMembers(BOARD *aBoard, int aNetCode) const
Build the member vias/traces for the current settings and return them without adding them anywhere.
static bool IsSpanValid(BOARD *aBoard, PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd)
True when both span ends are copper layers within the board's copper layer count.
void SetViaSize(int aSize)
void SetCapped(bool aCapped)
void SetViaDrill(int aDrill)
int GetPitch() const
void ShowPropertiesDialog(PCB_BASE_EDIT_FRAME *aEditFrame) override
const STRING_ANY_MAP GetProperties() const override
void SetStyle(VIA_STACK_STYLE aStyle)
wxString GetCommitMessage() const override
PCB_VIA_STACK(BOARD_ITEM *aParent=nullptr, PCB_LAYER_ID aLayer=F_Cu)
int GetViaSize() const
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetEndLayer(PCB_LAYER_ID aLayer)
void SetNetCode(int aNet)
PCB_LAYER_ID BottomLayer() const
VECTOR2I GetPosition() const override
Definition pcb_track.h:580
const PADSTACK & Padstack() const
Definition pcb_track.h:418
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
For a via m_layer contains the top layer, the other layer is in m_bottomLayer/.
int GetWidth() const override
void SetViaType(VIATYPE aViaType)
Definition pcb_track.h:411
PCB_LAYER_ID TopLayer() const
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
VIATYPE GetViaType() const
Definition pcb_track.h:410
void SetWidth(int aWidth) override
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:263
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
void Mask(TYPE_ID aDerived, TYPE_ID aBase, const wxString &aName)
Sets a base class property as masked in a derived class.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
RAII class that sets an value at construction and resets it to the original value at destruction.
Definition seg.h:38
virtual void Add(EDA_ITEM *aItem)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:97
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.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
A name/value tuple with unique names and wxAny values.
void set_iu(const std::string &aKey, const T &aVar)
bool get_to(const std::string &aKey, T &aVar) const
std::optional< T > get_opt(const std::string &aKey) const
void set(const std::string &aKey, const T &aVar)
bool get_to_iu(const std::string &aKey, T &aVar) const
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:34
Generic, UI-independent tool event.
Definition tool_event.h:167
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
Handle a list of polygons defining a copper zone.
Definition zone.h:70
@ ARROW
Definition cursors.h:42
@ BULLSEYE
Definition cursors.h:54
static bool empty(const wxTextEntryBase *aCtrl)
int ComputeWorstViaClearance(PCB_BASE_EDIT_FRAME *aFrame, DRC_ENGINE *aEngine)
Shared placement DRC checks, used by the plain via tool and the microvia stack tool so both block pla...
PAD * FindSnapPad(PCB_BASE_EDIT_FRAME *aFrame, const PCB_VIA *aVia, const VECTOR2I &aPosition, const LSET &aLayers, int aSnapRange=0)
PCB_TRACK * FindSnapTrack(PCB_BASE_EDIT_FRAME *aFrame, const PCB_VIA *aVia, const VECTOR2I &aPosition, const LSET &aLayers, int aSnapRange=0)
int ViaSnapRange(PCB_BASE_EDIT_FRAME *aFrame)
Shared placement snapping, used by the plain via tool and the microvia stack tool.
bool CheckItemDRCViolation(BOARD_CONNECTED_ITEM *aItem, PCB_BASE_EDIT_FRAME *aFrame, DRC_ENGINE *aEngine, int aWorstClearance, int aEpsilon)
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define IS_NEW
New item, just created.
#define IN_EDIT
Item currently edited.
SCOPED_SET_RESET< EE_GRAPHIC_TOOL::MODE > SCOPED_DRAW_MODE
a few functions useful in geometry calculations.
LEADER_MODE
The kind of the leader line.
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:714
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ In1_Cu
Definition layer_ids.h:62
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
@ F_Cu
Definition layer_ids.h:60
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
FLIP_DIRECTION
Definition mirror.h:23
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
#define _HKI(x)
Definition page_info.cpp:40
static GENERATORS_MGR::REGISTER< PCB_TUNING_PATTERN > registerMe
static VIA_STACK_STYLE styleFromString(const wxString &aStr)
static struct PCB_VIA_STACK_DESC _PCB_VIA_STACK_DESC
static PCB_LAYER_ID layerFromName(const wxString &aName, PCB_LAYER_ID aFallback)
static wxString styleToString(VIA_STACK_STYLE aStyle)
static void layerOrder(BOARD *aBoard, std::vector< PCB_LAYER_ID > &aOrder, std::map< int, int > &aOrdinals)
static std::optional< std::tuple< int, int, int > > memberKey(BOARD_ITEM *aItem)
static bool isExpandableMicrovia(const std::map< int, int > &aOrdinals, PCB_VIA *aVia)
VIA_STACK_STYLE
#define TYPE_HASH(x)
Definition property.h:74
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:875
#define NO_SETTER(owner, type)
Definition property.h:882
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
@ PT_NET
Net selection property.
Definition property.h:70
#define REGISTER_TYPE(x)
Static helper to register a generator.
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:279
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:278
A named microvia stack definition, chosen while routing instead of entering the values each time.
KIBIS top(path, &reporter)
const SHAPE_LINE_CHAIN chain
#define M_PI
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683