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#include <api/api_enums.h>
53#include <api/api_utils.h>
54#include <api/board/board_types.pb.h>
55#include <google/protobuf/any.pb.h>
56
57#include <properties/property.h>
59
62#include <generators_mgr.h>
63#include <drc/drc_engine.h>
64#include <router/router_tool.h>
65#include <router/pns_router.h>
67#include <widgets/wx_infobar.h>
68#include <tools/drawing_tool.h>
70#include <tools/pcb_actions.h>
72#include <tools/pcb_selection.h>
73
74
75const wxString PCB_VIA_STACK::DISPLAY_NAME = _HKI( "Microvia Stack" );
76const wxString PCB_VIA_STACK::GENERATOR_TYPE = wxS( "via_stack" );
77
78
79static wxString styleToString( VIA_STACK_STYLE aStyle )
80{
81 return aStyle == VIA_STACK_STYLE::STAGGERED ? wxS( "staggered" ) : wxS( "stacked" );
82}
83
84
85static VIA_STACK_STYLE styleFromString( const wxString& aStr )
86{
87 return aStr == wxS( "staggered" ) ? VIA_STACK_STYLE::STAGGERED : VIA_STACK_STYLE::STACKED;
88}
89
90
91// Resolve a canonical copper layer name back to its id without needing a board.
92static PCB_LAYER_ID layerFromName( const wxString& aName, PCB_LAYER_ID aFallback )
93{
94 wxString name = aName;
95 int layer = LSET::NameToLayer( name );
96
97 if( layer >= 0 && layer < PCB_LAYER_ID_COUNT && !( layer & 1 ) && IsCopperLayer( layer ) )
98 return ToLAYER_ID( layer );
99
100 return aFallback;
101}
102
103
105 PCB_GENERATOR( aParent, aLayer ),
106 m_startLayer( aLayer ),
109 m_pitch( 0 ),
110 m_filled( true ),
111 m_capped( false ),
112 m_viaSize( 0 ),
113 m_viaDrill( 0 ),
114 m_useNetclass( false ),
115 m_netCode( 0 )
116{
119}
120
121
123{
124 for( BOARD_ITEM* item : GetBoardItems() )
125 {
126 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
127 return bci->GetNetCode();
128 }
129
130 return m_netCode;
131}
132
133
135{
136 m_netCode = aNet;
137
138 for( BOARD_ITEM* item : GetBoardItems() )
139 {
140 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
141 bci->SetNetCode( aNet );
142 }
143}
144
145
147{
148 LSET enabled = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
149
150 auto present = [&]( PCB_LAYER_ID aLayer )
151 {
152 return aLayer >= 0 && aLayer < PCB_LAYER_ID_COUNT && enabled.test( aLayer );
153 };
154
155 return present( aStart ) && present( aEnd ) && aStart != aEnd;
156}
157
158
159void PCB_VIA_STACK::Move( const VECTOR2I& aMoveVector )
160{
161 PCB_GENERATOR::Move( aMoveVector );
162
163 if( m_hops )
164 m_hops->Move( aMoveVector );
165}
166
167
168void PCB_VIA_STACK::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
169{
170 PCB_GENERATOR::Rotate( aRotCentre, aAngle );
171
172 if( m_hops )
173 m_hops->Rotate( aAngle, aRotCentre );
174}
175
176
177void PCB_VIA_STACK::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
178{
179 PCB_GENERATOR::Flip( aCentre, aFlipDirection );
180
181 if( m_hops )
182 m_hops->Mirror( aCentre, aFlipDirection );
183
186}
187
188
189void PCB_VIA_STACK::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
190{
191 PCB_GENERATOR::Mirror( aCentre, aFlipDirection );
192
193 if( m_hops )
194 m_hops->Mirror( aCentre, aFlipDirection );
195}
196
197
198std::vector<int> PCB_VIA_STACK::ViewGetLayers() const
199{
200 std::vector<int> layers = { LAYER_ANCHOR };
201 LSET cuMask = LSET::AllCuMask();
202
203 if( const BOARD* board = GetBoard() )
204 cuMask &= board->GetEnabledLayers();
205
207 {
208 if( cuMask.Contains( layer ) )
209 layers.push_back( layer );
210 }
211
212 return layers;
213}
214
215
216void PCB_VIA_STACK::addMember( BOARD* aBoard, BOARD_COMMIT* aCommit, BOARD_ITEM* aItem )
217{
218 if( aCommit )
219 aCommit->Add( aItem );
220 else
221 aBoard->Add( aItem, ADD_MODE::APPEND, false );
222
223 AddItem( aItem );
224}
225
226
227std::pair<int, int> PCB_VIA_STACK::GetNetclassViaSize( BOARD* aBoard, int aNetCode )
228{
230 NETINFO_ITEM* net = aBoard->FindNet( aNetCode );
231 NETCLASS* nc = net ? net->GetNetClass() : nullptr;
232
233 return { ( nc && nc->HasuViaDiameter() ) ? nc->GetuViaDiameter() : bds.GetCurrentViaSize(),
234 ( nc && nc->HasuViaDrill() ) ? nc->GetuViaDrill() : bds.GetCurrentViaDrill() };
235}
236
237
238std::vector<BOARD_ITEM*> PCB_VIA_STACK::BuildMembers( BOARD* aBoard, int aNetCode ) const
239{
240 std::vector<BOARD_ITEM*> members;
241
242 if( !IsSpanValid( aBoard, m_startLayer, m_endLayer ) )
243 return members;
244
245 // Copper layers spanned, in physical stackup order.
246 std::vector<PCB_LAYER_ID> layers;
247
249 layers.push_back( layer );
250
251 if( layers.size() < 2 )
252 return members;
253
254 const int nHops = (int) layers.size() - 1;
255
257 int viaSize;
258 int viaDrill;
259
260 if( m_useNetclass )
261 {
262 std::tie( viaSize, viaDrill ) = GetNetclassViaSize( aBoard, aNetCode );
263 }
264 else
265 {
266 viaSize = m_viaSize > 0 ? m_viaSize : bds.GetCurrentViaSize();
267 viaDrill = m_viaDrill > 0 ? m_viaDrill : bds.GetCurrentViaDrill();
268 }
269
270 auto hopPos = [&]( int aIndex ) -> VECTOR2I
271 {
273 {
274 if( m_hops && aIndex < (int) m_hops->PointCount() )
275 return m_hops->CPoint( aIndex );
276
277 if( m_hops && m_hops->PointCount() > 1 )
278 {
279 int last = m_hops->PointCount() - 1;
280 VECTOR2I step = ( m_hops->CPoint( last ) - m_hops->CPoint( last - 1 ) ).Resize( m_pitch );
281
282 if( step != VECTOR2I( 0, 0 ) )
283 return m_hops->CPoint( last ) + step * ( aIndex - last );
284 }
285
286 return GetPosition() + VECTOR2I( (long long int) m_pitch * aIndex, 0 );
287 }
288
289 return GetPosition(); // stacked hops are coaxial
290 };
291
292 // One microvia per adjacent copper pair.
293 for( int i = 0; i < nHops; ++i )
294 {
295 PCB_VIA* via = new PCB_VIA( aBoard );
296 via->SetNetCode( aNetCode );
297 via->SetViaType( VIATYPE::MICROVIA );
298 via->SetLayerPair( layers[i], layers[i + 1] );
299 via->SetWidth( PADSTACK::ALL_LAYERS, viaSize );
300 via->SetDrill( viaDrill );
301 via->SetPosition( hopPos( i ) );
302
303 // Net class sizes follow the via rules, which can depend on each hop's layers and position
304 if( m_useNetclass )
305 via->SetSizeFromRules( viaSize, viaDrill );
306
307 // Only a hop reaching an outer layer has a face to cap.
308 bool capped = m_capped && ( IsExternalCopperLayer( layers[i] ) || IsExternalCopperLayer( layers[i + 1] ) );
309
310 // Stacked hops land on filled copper, and capping needs a filled via under it.
311 bool filled = ( m_style == VIA_STACK_STYLE::STACKED ) || m_filled || capped;
312
313 via->Padstack().Drill().is_filled = filled;
314 via->Padstack().Drill().is_capped = capped;
315
316 members.push_back( via );
317 }
318
319 // Staggered stacks need a short trace on each shared landing layer.
321 {
322 // The trace takes the net's width, not whatever the toolbar shows.
323 NETINFO_ITEM* net = aBoard->FindNet( aNetCode );
324 NETCLASS* nc = net ? net->GetNetClass() : nullptr;
325
326 int trackWidth = ( nc && nc->HasTrackWidth() ) ? nc->GetTrackWidth() : bds.GetCurrentTrackWidth();
327
328 DRC_ENGINE* drcEngine = bds.m_DRCEngine.get();
329
330 for( int i = 0; i < nHops - 1; ++i )
331 {
332 VECTOR2I a = hopPos( i );
333 VECTOR2I b = hopPos( i + 1 );
334
335 if( a == b )
336 continue;
337
338 PCB_TRACK* trace = new PCB_TRACK( aBoard );
339 trace->SetNetCode( aNetCode );
340 trace->SetStart( a );
341 trace->SetEnd( b );
342 trace->SetLayer( layers[i + 1] );
343
344 // Width rules can depend on the layer and the net, as the router resolves them
345 DRC_CONSTRAINT width;
346
347 if( drcEngine )
348 {
349 // Rebuilds are freed on each drag step, so their pointers must not seed the geometry caches
350 trace->SetFlags( ROUTER_TRANSIENT );
351 width = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, trace, nullptr, layers[i + 1] );
353 }
354
355 int layerWidth = trackWidth;
356
357 if( !width.IsNull() && width.GetSeverity() != RPT_SEVERITY_IGNORE )
358 layerWidth = width.GetValue().PinnedOpt();
359
360 trace->SetWidth( std::max( bds.m_TrackMinWidth, layerWidth ) );
361
362 members.push_back( trace );
363 }
364 }
365
366 return members;
367}
368
369
370// Identifies the hop a member implements. Two members with the same key are interchangeable.
371static std::optional<std::tuple<int, int, int>> memberKey( BOARD_ITEM* aItem )
372{
373 if( aItem->Type() == PCB_VIA_T )
374 {
375 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
376
377 return std::make_tuple( (int) PCB_VIA_T, (int) via->TopLayer(), (int) via->BottomLayer() );
378 }
379
380 if( aItem->Type() == PCB_TRACE_T )
381 return std::make_tuple( (int) PCB_TRACE_T, (int) aItem->GetLayer(), 0 );
382
383 return std::nullopt;
384}
385
386
387bool PCB_VIA_STACK::reuseMembers( BOARD_COMMIT* aCommit, const std::vector<BOARD_ITEM*>& aRebuilt )
388{
389 if( aRebuilt.empty() || aRebuilt.size() != GetItems().size() )
390 return false;
391
392 std::map<std::tuple<int, int, int>, BOARD_ITEM*> live;
393
394 for( BOARD_ITEM* item : GetBoardItems() )
395 {
396 std::optional<std::tuple<int, int, int>> key = memberKey( item );
397
398 if( !key || !live.emplace( *key, item ).second )
399 return false;
400 }
401
402 std::vector<std::pair<BOARD_ITEM*, BOARD_ITEM*>> matched;
403
404 for( BOARD_ITEM* fresh : aRebuilt )
405 {
406 std::optional<std::tuple<int, int, int>> key = memberKey( fresh );
407
408 if( !key )
409 return false;
410
411 auto it = live.find( *key );
412
413 if( it == live.end() )
414 return false;
415
416 matched.emplace_back( it->second, fresh );
417 live.erase( it );
418 }
419
420 for( const auto& [item, fresh] : matched )
421 {
422 if( aCommit )
423 aCommit->Modify( item );
424
425 if( item->Type() == PCB_VIA_T )
426 {
427 PCB_VIA* via = static_cast<PCB_VIA*>( item );
428 PCB_VIA* src = static_cast<PCB_VIA*>( fresh );
429
430 via->SetNetCode( src->GetNetCode() );
431 via->SetPosition( src->GetPosition() );
433 via->SetDrill( src->GetDrillValue() );
434 via->Padstack().Drill().is_filled = src->Padstack().Drill().is_filled;
435 via->Padstack().Drill().is_capped = src->Padstack().Drill().is_capped;
436 }
437 else
438 {
439 PCB_TRACK* trace = static_cast<PCB_TRACK*>( item );
440 PCB_TRACK* src = static_cast<PCB_TRACK*>( fresh );
441
442 trace->SetNetCode( src->GetNetCode() );
443 trace->SetStart( src->GetStart() );
444 trace->SetEnd( src->GetEnd() );
445 trace->SetWidth( src->GetWidth() );
446 }
447 }
448
449 return true;
450}
451
452
454{
455 int net = GetNetCode();
456 std::vector<BOARD_ITEM*> rebuilt = BuildMembers( aBoard, net );
457
458 if( rebuilt.empty() && !GetItems().empty() )
459 return;
460
461 // A drag rebuilds on every motion event. Replacing the members each time would stage an
462 // add and a remove per generation in one commit, and undo brings all of them back.
463 if( reuseMembers( aCommit, rebuilt ) )
464 {
465 for( BOARD_ITEM* item : rebuilt )
466 delete item;
467
468 return;
469 }
470
471 // Remove any existing members. For the commit path the item must be snapshotted for undo
472 // (aCommit->Remove) while it is STILL in this group, otherwise the undo image is ungrouped
473 // and undoing an edit leaves the vias loose. RemoveItem() unlinks it afterwards.
474 for( BOARD_ITEM* item : GetBoardItems() )
475 {
476 if( aCommit )
477 {
478 aCommit->Unmodify( item, nullptr );
479
480 // A member added by an earlier motion event in this commit is freed, not orphaned
481 if( !aCommit->RemoveOrDiscard( item ) )
482 RemoveItem( item );
483 }
484 else
485 {
486 RemoveItem( item );
487 aBoard->Remove( item );
488 delete item;
489 }
490 }
491
492 for( BOARD_ITEM* member : rebuilt )
493 addMember( aBoard, aCommit, member );
494}
495
496
498{
499 if( aCommit )
500 {
501 if( IsNew() )
502 {
503 aCommit->Add( this );
504 }
505 else
506 {
507 // Members must be staged before the move, not after.
508 aCommit->Modify( this, nullptr, RECURSE_MODE::RECURSE );
509 }
510 }
511
512 SetFlags( IN_EDIT );
513}
514
515
517{
518 if( !( GetFlags() & IN_EDIT ) )
519 return false;
520
521 if( !aBoard || !aCommit )
522 return false;
523
524 Regenerate( aBoard, aCommit );
525 return true;
526}
527
528
533
534
539
540
542{
543 if( !aCommit )
544 return;
545
546 for( BOARD_ITEM* item : GetBoardItems() )
547 {
548 aCommit->Unmodify( item, nullptr );
549 aCommit->Remove( item );
550 }
551
552 aCommit->Remove( this );
553}
554
555
556int PCB_VIA_STACK::FindNetAtPosition( BOARD* aBoard, const VECTOR2I& aPosition, PCB_LAYER_ID aLayer )
557{
558 for( FOOTPRINT* footprint : aBoard->Footprints() )
559 {
560 for( PAD* pad : footprint->Pads() )
561 {
562 // A mechanical hole carries no net, and must not hide copper under it.
563 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
564 continue;
565
566 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition ) )
567 return pad->GetNetCode();
568 }
569 }
570
571 for( PCB_TRACK* track : aBoard->Tracks() )
572 {
573 if( track->IsOnLayer( aLayer ) && track->HitTest( aPosition ) )
574 return track->GetNetCode();
575 }
576
577 for( ZONE* zone : aBoard->Zones() )
578 {
579 if( !zone->GetIsRuleArea() && zone->IsOnLayer( aLayer ) && zone->HitTestFilledArea( aLayer, aPosition ) )
580 {
581 return zone->GetNetCode();
582 }
583 }
584
585 return 0;
586}
587
588
589int PCB_VIA_STACK::FindNetAtPosition( BOARD* aBoard, const VECTOR2I& aPosition, const LSET& aLayers )
590{
591 for( PCB_LAYER_ID layer : aLayers.CuStack() )
592 {
593 if( int net = FindNetAtPosition( aBoard, aPosition, layer ) )
594 return net;
595 }
596
597 return 0;
598}
599
600
602{
603 SetStartLayer( aPreset.m_StartLayer );
604 m_endLayer = aPreset.m_EndLayer;
606 m_viaSize = aPreset.m_ViaSize;
607 m_viaDrill = aPreset.m_ViaDrill;
609 m_filled = aPreset.m_Filled;
610 m_capped = aPreset.m_Capped;
611 m_pitch = aPreset.m_Pitch;
612 m_presetName = aPreset.m_Name;
613}
614
615
617{
618 VIA_STACK_PRESET preset;
619
620 preset.m_Name = m_presetName;
621 preset.m_StartLayer = m_startLayer;
622 preset.m_EndLayer = m_endLayer;
624 preset.m_ViaSize = m_viaSize;
625 preset.m_ViaDrill = m_viaDrill;
627 preset.m_Filled = m_filled;
628 preset.m_Capped = m_capped;
629 preset.m_Pitch = m_pitch;
630
631 return preset;
632}
633
634
635// Copper layers in physical stack order, plus a layer -> position lookup.
636static void layerOrder( BOARD* aBoard, std::vector<PCB_LAYER_ID>& aOrder, std::map<int, int>& aOrdinals )
637{
638 int n = 0;
639
640 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, aBoard->GetCopperLayerCount() ) )
641 {
642 aOrder.push_back( layer );
643 aOrdinals[layer] = n++;
644 }
645}
646
647
648PCB_VIA_STACK* PCB_VIA_STACK::CreateFromItems( const std::vector<BOARD_ITEM*>& aItems, BOARD* aBoard,
649 std::vector<BOARD_ITEM*>* aMembers, wxString* aError )
650{
651 std::vector<PCB_VIA*> vias;
652 std::vector<PCB_TRACK*> traces;
653
654 for( BOARD_ITEM* item : aItems )
655 {
656 if( item->Type() == PCB_VIA_T )
657 {
658 PCB_VIA* via = static_cast<PCB_VIA*>( item );
659
660 if( via->GetViaType() != VIATYPE::MICROVIA )
661 return nullptr;
662
663 if( dynamic_cast<PCB_GENERATOR*>( via->GetParentGroup() ) )
664 return nullptr;
665
666 if( !vias.empty() && via->GetNetCode() != vias.front()->GetNetCode() )
667 return nullptr;
668
669 // A stack carries one size for every hop, so a mixed selection would be resized
670 // by the next regeneration.
671 if( !vias.empty()
672 && ( via->GetWidth( PADSTACK::ALL_LAYERS ) != vias.front()->GetWidth( PADSTACK::ALL_LAYERS )
673 || via->GetDrillValue() != vias.front()->GetDrillValue() ) )
674 {
675 if( aError )
676 {
677 *aError = _( "The selected microvias have different diameters or hole sizes. A "
678 "microvia stack uses one size for every hop." );
679 }
680
681 return nullptr;
682 }
683
684 vias.push_back( via );
685 }
686 else if( item->Type() == PCB_TRACE_T )
687 {
688 traces.push_back( static_cast<PCB_TRACK*>( item ) );
689 }
690 else
691 {
692 return nullptr;
693 }
694 }
695
696 if( vias.size() < 2 )
697 return nullptr;
698
699 std::vector<PCB_LAYER_ID> order;
700 std::map<int, int> ordinals;
701 layerOrder( aBoard, order, ordinals );
702
703 // Each via must span one hop and together they must tile a contiguous range.
704 std::map<int, PCB_VIA*> hopByUpper;
705
706 for( PCB_VIA* via : vias )
707 {
708 auto top = ordinals.find( via->TopLayer() );
709 auto bot = ordinals.find( via->BottomLayer() );
710
711 if( top == ordinals.end() || bot == ordinals.end() )
712 return nullptr;
713
714 if( std::abs( top->second - bot->second ) != 1 )
715 return nullptr;
716
717 int upper = std::min( top->second, bot->second );
718
719 if( hopByUpper.count( upper ) )
720 return nullptr;
721
722 hopByUpper[upper] = via;
723 }
724
725 int first = hopByUpper.begin()->first;
726 int last = hopByUpper.rbegin()->first;
727
728 if( (int) hopByUpper.size() != last - first + 1 )
729 return nullptr;
730
731 PCB_VIA* topVia = hopByUpper.begin()->second;
732
733 bool coaxial = true;
734
735 for( PCB_VIA* via : vias )
736 {
737 if( via->GetPosition() != topVia->GetPosition() )
738 coaxial = false;
739 }
740
741 PCB_VIA_STACK* stack = new PCB_VIA_STACK( aBoard, F_Cu );
742
743 stack->SetStartLayer( order[first] );
744 stack->SetEndLayer( order[last + 1] );
745 stack->SetViaSize( topVia->GetWidth( PADSTACK::ALL_LAYERS ) );
746 stack->SetViaDrill( topVia->GetDrillValue() );
747 stack->SetNetCode( topVia->GetNetCode() );
748 stack->SetFilled( topVia->Padstack().Drill().is_filled.value_or( aBoard->GetDesignSettings().m_FillVias ) );
749
750 // Only a hop reaching an outer layer carries capping, so only those can say the stack is capped.
751 bool capped = false;
752
753 for( PCB_VIA* via : vias )
754 {
755 if( IsExternalCopperLayer( via->TopLayer() ) || IsExternalCopperLayer( via->BottomLayer() ) )
756 capped = capped || via->Padstack().Drill().is_capped.value_or( false );
757 }
758
759 stack->SetCapped( capped );
760 stack->SetPosition( topVia->GetPosition() );
761
762 if( coaxial )
763 {
765 stack->SetFilled( true );
766 }
767 else
768 {
770
771 SHAPE_LINE_CHAIN hops;
772
773 for( const auto& [upper, via] : hopByUpper )
774 hops.Append( via->GetPosition(), true );
775
776 stack->SetHops( hops );
777 stack->SetPitch( ( hops.CPoint( 1 ) - hops.CPoint( 0 ) ).EuclideanNorm() );
778 }
779
780 // Only traces that connect hop positions on a shared landing layer belong to the stack,
781 // anything else must stay loose or the next regenerate would delete it.
782 if( aMembers )
783 {
784 std::set<int> landing;
785 std::set<VECTOR2I> viaPositions;
786
787 for( int i = first + 1; i <= last; ++i )
788 landing.insert( order[i] );
789
790 for( PCB_VIA* via : vias )
791 viaPositions.insert( via->GetPosition() );
792
793 for( PCB_VIA* via : vias )
794 aMembers->push_back( via );
795
796 for( PCB_TRACK* trace : traces )
797 {
798 if( landing.count( trace->GetLayer() ) && trace->GetStart() != trace->GetEnd()
799 && trace->GetNetCode() == stack->GetNetCode() && viaPositions.count( trace->GetStart() )
800 && viaPositions.count( trace->GetEnd() ) )
801 {
802 aMembers->push_back( trace );
803 }
804 }
805 }
806
807 return stack;
808}
809
810
811// A loose microvia crossing more than one layer step is what an expansion turns into a stack.
812static bool isExpandableMicrovia( const std::map<int, int>& aOrdinals, PCB_VIA* aVia )
813{
814 if( aVia->GetViaType() != VIATYPE::MICROVIA || aVia->GetParentGroup() )
815 return false;
816
817 auto top = aOrdinals.find( aVia->TopLayer() );
818 auto bot = aOrdinals.find( aVia->BottomLayer() );
819
820 if( top == aOrdinals.end() || bot == aOrdinals.end() )
821 return false;
822
823 return std::abs( top->second - bot->second ) > 1;
824}
825
826
828{
829 std::vector<PCB_LAYER_ID> order;
830 std::map<int, int> ordinals;
831 layerOrder( aBoard, order, ordinals );
832
833 std::set<KIID> ids;
834
835 for( PCB_TRACK* track : aBoard->Tracks() )
836 {
837 if( track->Type() == PCB_VIA_T && isExpandableMicrovia( ordinals, static_cast<PCB_VIA*>( track ) ) )
838 ids.insert( track->m_Uuid );
839 }
840
841 return ids;
842}
843
844
846 const std::function<const VIA_STACK_PRESET*( PCB_VIA* )>& aMatcher )
847{
848 std::vector<PCB_LAYER_ID> order;
849 std::map<int, int> ordinals;
850 layerOrder( aBoard, order, ordinals );
851
852 std::vector<std::pair<PCB_VIA*, const VIA_STACK_PRESET*>> candidates;
853
854 for( PCB_TRACK* track : aBoard->Tracks() )
855 {
856 if( track->Type() != PCB_VIA_T )
857 continue;
858
859 PCB_VIA* via = static_cast<PCB_VIA*>( track );
860
861 if( !isExpandableMicrovia( ordinals, via ) )
862 continue;
863
864 const VIA_STACK_PRESET* preset = nullptr;
865
866 if( aMatcher )
867 {
868 preset = aMatcher( via );
869
870 if( !preset )
871 continue;
872 }
873
874 candidates.push_back( { via, preset } );
875 }
876
877 for( const auto& [via, preset] : candidates )
878 {
880 PCB_LAYER_ID bottom;
881 via->LayerPair( &top, &bottom );
882
883 PCB_VIA_STACK* stack = new PCB_VIA_STACK( aBoard, F_Cu );
884
885 if( preset )
886 stack->ApplyPreset( *preset );
887
888 stack->SetStartLayer( top );
889 stack->SetEndLayer( bottom );
891 stack->SetViaSize( via->GetWidth( PADSTACK::ALL_LAYERS ) );
892 stack->SetViaDrill( via->GetDrillValue() );
893 stack->SetNetCode( via->GetNetCode() );
894 stack->SetFilled( true );
895 stack->SetPosition( via->GetPosition() );
896
897 if( aCommit )
898 {
899 aCommit->Remove( via );
900 aCommit->Add( stack );
901 }
902 else
903 {
904 aBoard->Remove( via );
905 delete via;
906 aBoard->Add( stack, ADD_MODE::APPEND, false );
907 }
908
909 stack->Regenerate( aBoard, aCommit );
910 }
911
912 return (int) candidates.size();
913}
914
915
917{
918 // Dialog edits a scratch copy so the undo snapshot below sees the pre-edit settings.
919 PCB_VIA_STACK temp( GetBoard(), GetLayer() );
920 temp.ApplyPreset( ToPreset() );
921 temp.SetNetCode( GetNetCode() );
922
923 DIALOG_MICROVIA_STACK dlg( aEditFrame, &temp );
924
925 if( dlg.ShowModal() != wxID_OK )
926 return;
927
928 BOARD_COMMIT commit( aEditFrame );
929 GENERATOR_TOOL* tool = aEditFrame->GetToolManager()->GetTool<GENERATOR_TOOL>();
930
931 commit.Modify( this );
932 ApplyPreset( temp.ToPreset() );
933
934 // A staggered stack stores explicit hop positions, so re-space them to the (possibly new)
935 // pitch while keeping each hop's direction. Without this, editing the pitch changes the
936 // value but not the distance between vias.
937 if( m_style == VIA_STACK_STYLE::STAGGERED && m_hops && m_hops->PointCount() >= 2 )
938 {
939 SHAPE_LINE_CHAIN rescaled;
940 rescaled.Append( m_hops->CPoint( 0 ) );
941
942 for( int i = 1; i < m_hops->PointCount(); ++i )
943 {
944 VECTOR2I dir = m_hops->CPoint( i ) - m_hops->CPoint( i - 1 );
945
946 if( dir.EuclideanNorm() > 0 )
947 dir = dir.Resize( m_pitch );
948
949 rescaled.Append( rescaled.CPoint( i - 1 ) + dir, true );
950 }
951
952 SetHops( rescaled );
953 }
954
955 EditStart( tool, GetBoard(), &commit );
956
957 // If the stack has no net, inherit it from the copper it now sits on. This is the
958 // supported way to net a stack, the connecting traces are generator owned and locked.
959 // SetNetCode writes through to the members, so it has to follow EditStart staging them.
961 {
962 LSET span;
963
964 for( PCB_LAYER_ID layer : LAYER_RANGE( m_startLayer, m_endLayer, GetBoard()->GetCopperLayerCount() ) )
965 span.set( layer );
966
968 }
969
970 Update( tool, GetBoard(), &commit );
971 EditFinish( tool, GetBoard(), &commit );
972
973 commit.Push( GetCommitMessage() );
974}
975
976
978{
980
981 props.set( "start_layer", LSET::Name( m_startLayer ) );
982 props.set( "end_layer", LSET::Name( m_endLayer ) );
983 props.set( "style", styleToString( m_style ) );
984 props.set( "filled", m_filled );
985 props.set( "capped", m_capped );
986 props.set( "use_netclass", m_useNetclass );
987 props.set( "preset", m_presetName );
988
989 props.set_iu( "pitch", m_pitch );
990 props.set_iu( "via_size", m_viaSize );
991 props.set_iu( "via_drill", m_viaDrill );
992
993 if( m_hops )
994 props.set( "hops", wxAny( *m_hops ) );
995
996 return props;
997}
998
999
1001{
1003
1004 wxString layerName;
1005
1006 if( aProps.get_to( "start_layer", layerName ) )
1007 SetStartLayer( layerFromName( layerName, m_startLayer ) );
1008
1009 if( aProps.get_to( "end_layer", layerName ) )
1010 m_endLayer = layerFromName( layerName, m_endLayer );
1011
1012 wxString style;
1013
1014 if( aProps.get_to( "style", style ) )
1015 m_style = styleFromString( style );
1016
1017 aProps.get_to( "filled", m_filled );
1018 aProps.get_to( "capped", m_capped );
1019 aProps.get_to( "use_netclass", m_useNetclass );
1020 aProps.get_to( "preset", m_presetName );
1021
1022 aProps.get_to_iu( "pitch", m_pitch );
1023 aProps.get_to_iu( "via_size", m_viaSize );
1024 aProps.get_to_iu( "via_drill", m_viaDrill );
1025
1026 if( auto hops = aProps.get_opt<SHAPE_LINE_CHAIN>( "hops" ) )
1027 m_hops = *hops;
1028}
1029
1030
1031void PCB_VIA_STACK::Serialize( google::protobuf::Any& aContainer ) const
1032{
1033 using namespace kiapi::board::types;
1034 using namespace kiapi::common::types;
1035
1036 ViaStack stack;
1037
1038 stack.mutable_id()->set_value( m_Uuid.AsStdString() );
1039 stack.set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_startLayer ) );
1040 stack.set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_endLayer ) );
1042 stack.mutable_pitch()->set_value_nm( pcbIUScale.IUToNm( m_pitch ) );
1043 stack.set_filled( m_filled );
1044 stack.set_capped( m_capped );
1045 stack.mutable_via_size()->set_value_nm( pcbIUScale.IUToNm( m_viaSize ) );
1046 stack.mutable_via_drill()->set_value_nm( pcbIUScale.IUToNm( m_viaDrill ) );
1047 stack.set_use_netclass( m_useNetclass );
1048
1049 if( !m_presetName.empty() )
1050 stack.set_preset_name( m_presetName.ToUTF8() );
1051
1052 if( m_hops )
1053 kiapi::common::PackPolyLine( *stack.mutable_hops(), *m_hops );
1054
1055 if( GetNetCode() != 0 && GetBoard() )
1056 {
1057 if( NETINFO_ITEM* net = GetBoard()->FindNet( GetNetCode() ) )
1058 stack.mutable_net()->set_name( net->GetNetname().ToUTF8() );
1059 }
1060
1061 stack.set_locked( IsLocked() ? LockedState::LS_LOCKED : LockedState::LS_UNLOCKED );
1062
1063 if( const BOARD* board = GetBoard() )
1064 stack.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
1065
1066 for( EDA_ITEM* member : GetItems() )
1067 stack.add_members()->set_value( member->m_Uuid.AsStdString() );
1068
1069 kiapi::common::PackCustomProperties( stack.mutable_custom_properties(), *this );
1070 aContainer.PackFrom( stack );
1071}
1072
1073
1074bool PCB_VIA_STACK::Deserialize( const google::protobuf::Any& aContainer )
1075{
1076 using namespace kiapi::board::types;
1077 using namespace kiapi::common::types;
1078
1079 kiapi::board::types::ViaStack stack;
1080
1081 if( !aContainer.UnpackTo( &stack ) )
1082 return false;
1083
1084 SetUuidDirect( ::KIID( stack.id().value() ) );
1085 SetStartLayer( FromProtoEnum<PCB_LAYER_ID>( stack.start_layer() ) );
1086 SetEndLayer( FromProtoEnum<PCB_LAYER_ID>( stack.end_layer() ) );
1087 m_style = FromProtoEnum<VIA_STACK_STYLE>( stack.style() );
1088 m_pitch = pcbIUScale.NmToIU( stack.pitch().value_nm() );
1089 m_filled = stack.filled();
1090 m_capped = stack.capped();
1091 m_useNetclass = stack.use_netclass();
1092
1093 if( stack.has_preset_name() )
1094 m_presetName = wxString( stack.preset_name().c_str(), wxConvUTF8 );
1095 else
1096 m_presetName.clear();
1097
1098 if( stack.has_hops() )
1099 m_hops = kiapi::common::UnpackPolyLine( stack.hops() );
1100 else
1101 m_hops.reset();
1102
1103 if( stack.net().name().empty() )
1104 m_netCode = 0;
1105
1106 SetLocked( stack.locked() == LockedState::LS_LOCKED );
1107 kiapi::common::UnpackCustomProperties( stack.custom_properties(), *this );
1108
1109 m_items.clear();
1110 m_deserializedItems.clear();
1111
1112 BOARD* board = GetBoard();
1113
1114 if( !board )
1115 return false;
1116
1117 for( const kiapi::common::types::KIID& memberId : stack.members() )
1118 {
1119 if( EDA_ITEM* item = board->ResolveItem( ::KIID( memberId.value() ), true ) )
1120 m_deserializedItems.insert( item );
1121 }
1122
1123 return true;
1124}
1125
1126
1128
1129
1131{
1132 // Consume any router handoff seed (a pre-anchored staggered stack).
1133 bool seeded = m_viaStackSeeded;
1134 VECTOR2I seedPos = m_viaStackSeedPos;
1137 m_viaStackSeeded = false;
1138
1140 return 0;
1141
1142 if( m_inDrawingTool )
1143 return 0;
1144
1146
1147 m_toolMgr->RunAction( ACTIONS::selectionClear );
1148 m_frame->PushTool( aEvent );
1149 Activate();
1150
1151 BOARD* board = m_frame->GetBoard();
1152 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
1153
1154 PCB_VIA_STACK settings( board, F_Cu );
1155
1156 // Place silently with the preset currently selected in the toolbar, matching how via
1157 // placement uses the current via size. Only fall back to the properties dialog when no
1158 // presets are defined yet, so there is still a way to place and define one.
1159 const std::vector<VIA_STACK_PRESET>& presets = board->GetDesignSettings().m_ViaStackPresets;
1160 int idx = board->GetDesignSettings().GetViaStackIndex();
1161
1162 if( !presets.empty() )
1163 {
1164 settings.ApplyPreset( presets[std::clamp( idx, 0, (int) presets.size() - 1 )] );
1165 }
1166 else if( !seeded )
1167 {
1168 DIALOG_MICROVIA_STACK dlg( m_frame, &settings );
1169
1170 if( dlg.ShowModal() != wxID_OK )
1171 {
1172 m_frame->PopTool( aEvent );
1173 return 0;
1174 }
1175 }
1176
1177 // A router handoff anchors the span to the routed layers and is always a staggered walk.
1178 if( seeded )
1179 {
1180 settings.SetStartLayer( seedStart );
1181 settings.SetEndLayer( seedEnd );
1183 }
1184
1185 bool staggered = settings.GetStyle() == VIA_STACK_STYLE::STAGGERED;
1186
1187 if( !PCB_VIA_STACK::IsSpanValid( board, settings.GetStartLayer(), settings.GetEndLayer() ) )
1188 {
1189 m_frame->ShowInfoBarError( _( "The microvia stack layers are not present on this board." ) );
1190 m_frame->PopTool( aEvent );
1191 return 0;
1192 }
1193
1194 std::vector<PCB_LAYER_ID> spanLayers;
1195
1196 for( PCB_LAYER_ID layer :
1197 LAYER_RANGE( settings.GetStartLayer(), settings.GetEndLayer(), board->GetCopperLayerCount() ) )
1198 spanLayers.push_back( layer );
1199
1200 LSET spanSet;
1201
1202 for( PCB_LAYER_ID layer : spanLayers )
1203 spanSet.set( layer );
1204
1205 int nHops = (int) spanLayers.size() - 1;
1206
1207 // A zero pitch would pin the steering circle onto a single point.
1208 if( staggered && settings.GetPitch() <= 0 )
1209 {
1210 int dia = settings.GetViaSize() > 0 ? settings.GetViaSize() : board->GetDesignSettings().GetCurrentViaSize();
1211 settings.SetPitch( dia * 2 );
1212 }
1213
1214 if( nHops < 1 )
1215 {
1216 m_frame->PopTool( aEvent );
1217 return 0;
1218 }
1219
1220 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1221 PCB_SELECTION preview;
1222 std::vector<BOARD_ITEM*> previewItems;
1223 std::vector<VECTOR2I> hops;
1224 VECTOR2I lastDir( 1, 0 );
1225 bool resumeRouting = false; // seeded handoff places one and routes on
1226 VECTOR2I resumePos;
1227 VECTOR2I lastViaPos;
1228
1229 // Router handoff: the first hop is fixed at the routed head, the user steers the rest.
1230 if( seeded )
1231 hops.push_back( seedPos );
1232
1233 m_view->Add( &preview );
1234
1235 auto clearPreview = [&]()
1236 {
1237 preview.Clear();
1238
1239 for( BOARD_ITEM* item : previewItems )
1240 delete item;
1241
1242 previewItems.clear();
1243 };
1244
1245 // DRC context, shared with the plain via tool so a stack is refused over foreign copper the
1246 // same way (unless "Allow DRC violations" is enabled in the router settings).
1247 std::shared_ptr<DRC_ENGINE> drcEngine = board->GetDesignSettings().m_DRCEngine;
1248 int drcEpsilon = board->GetDesignSettings().GetDRCEpsilon();
1249 int worstClearance = ComputeWorstViaClearance( m_frame, drcEngine.get() );
1250 bool allowDRCViolations = false;
1251
1252 if( ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>() )
1253 allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
1254
1255 auto updatePreview = [&]( const VECTOR2I& aCursor )
1256 {
1257 clearPreview();
1258
1259 if( staggered )
1260 {
1262
1263 for( const VECTOR2I& p : hops )
1264 chain.Append( p, true );
1265
1266 chain.Append( aCursor, true );
1267 settings.SetHops( chain );
1268 settings.SetPosition( hops.empty() ? aCursor : hops.front() );
1269
1270 // Only preview the placed hops plus the one being steered, not the
1271 // rest of the span extrapolated in a straight line.
1272 int previewHops = std::min( (int) hops.size() + 1, nHops );
1273 settings.SetEndLayer( spanLayers[previewHops] );
1274 }
1275 else
1276 {
1277 settings.SetPosition( aCursor );
1278 }
1279
1280 previewItems = settings.BuildMembers( board, 0 );
1281
1282 for( BOARD_ITEM* item : previewItems )
1283 preview.Add( item );
1284
1285 m_view->Update( &preview );
1286 };
1287
1288 auto placeStack = [&]( const VECTOR2I& aCursor ) -> bool
1289 {
1290 BOARD_COMMIT commit( m_frame );
1291 PCB_VIA_STACK* stack = static_cast<PCB_VIA_STACK*>( settings.Clone() );
1292 GENERATOR_TOOL* genTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
1293
1294 // Clone keeps the source uuid, each placed stack needs its own.
1295 stack->SetUuidDirect( KIID() );
1296 stack->SetParent( board );
1297
1298 // The preview may have truncated the span, place the full one.
1299 stack->SetEndLayer( spanLayers.back() );
1300
1301 if( staggered )
1302 {
1304
1305 for( const VECTOR2I& p : hops )
1306 chain.Append( p, true );
1307
1308 stack->SetHops( chain );
1309 stack->SetPosition( hops.front() );
1310 }
1311 else
1312 {
1313 stack->ClearHops();
1314 stack->SetPosition( aCursor );
1315 }
1316
1317 // Inherit the net of whatever the first hop lands on.
1318 stack->SetNetCode( PCB_VIA_STACK::FindNetAtPosition( board, stack->GetPosition(), spanSet ) );
1319 stack->SetFlags( IS_NEW );
1320
1321 // Refuse placement over foreign copper, matching the plain via tool. Build the
1322 // stack's vias off-board and check each before anything is committed.
1323 std::vector<BOARD_ITEM*> candidates = stack->BuildMembers( board, stack->GetNetCode() );
1324 bool violates = false;
1325
1326 for( BOARD_ITEM* item : candidates )
1327 {
1328 if( BOARD_CONNECTED_ITEM* copper = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
1329 {
1330 // Candidates are freed below, so their pointers must not seed the rule caches
1331 copper->SetFlags( ROUTER_TRANSIENT );
1332
1333 if( CheckItemDRCViolation( copper, m_frame, drcEngine.get(), worstClearance, drcEpsilon ) )
1334 {
1335 violates = true;
1336 break;
1337 }
1338 }
1339 }
1340
1341 for( BOARD_ITEM* item : candidates )
1342 delete item;
1343
1344 if( violates && !allowDRCViolations )
1345 {
1346 m_frame->ShowInfoBarError( _( "Microvia stack location violates DRC." ), true,
1347 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
1348 delete stack;
1349 return false;
1350 }
1351
1352 stack->EditStart( genTool, board, &commit );
1353 stack->Update( genTool, board, &commit );
1354 stack->EditFinish( genTool, board, &commit );
1355
1356 stack->ClearFlags( IS_NEW );
1357
1358 // Only the last hop reaches the end layer, and a staggered one is not at the cursor.
1359 for( BOARD_ITEM* item : stack->GetBoardItems() )
1360 {
1361 PCB_VIA* via = dynamic_cast<PCB_VIA*>( item );
1362
1363 if( via && via->IsOnLayer( stack->GetEndLayer() ) )
1364 {
1365 lastViaPos = via->GetPosition();
1366 break;
1367 }
1368 }
1369
1370 commit.Push( _( "Place Microvia Stack" ) );
1371 return true;
1372 };
1373
1374 // Snap the anchor onto nearby copper, using the plain via tool's routines so tracks,
1375 // arcs and pads all behave the same. A stack may land on any layer it spans, so the
1376 // probe carries the whole span rather than a single layer.
1377 int snapViaDia = settings.GetViaSize() > 0 ? settings.GetViaSize() : board->GetDesignSettings().GetCurrentViaSize();
1378
1379 PCB_VIA probe( board );
1381 probe.SetWidth( PADSTACK::ALL_LAYERS, snapViaDia );
1382 probe.SetLayerPair( settings.GetStartLayer(), settings.GetEndLayer() );
1383
1384 auto snapToCopper = [&]( const VECTOR2I& aCursor, bool aItemSnap ) -> VECTOR2I
1385 {
1386 MAGNETIC_SETTINGS* mag = m_frame->GetMagneticItemsSettings();
1387
1388 // The loop clears grid snap for the ResolveSnap base, re-enable it here so
1389 // AlignToSegment actually aligns, matching VIA_PLACER::SnapItem.
1390 grid.SetSnap( aItemSnap );
1391
1392 if( aItemSnap && mag )
1393 {
1395 {
1396 if( PCB_TRACK* track = FindSnapTrack( m_frame, &probe, aCursor, spanSet, ViaSnapRange( m_frame ) ) )
1397 return grid.AlignToSegment( aCursor, SEG( track->GetStart(), track->GetEnd() ) );
1398 }
1399
1400 if( mag->pads != MAGNETIC_OPTIONS::NO_EFFECT )
1401 {
1402 if( PAD* pad = FindSnapPad( m_frame, &probe, aCursor, spanSet, ViaSnapRange( m_frame ) ) )
1403 return pad->GetPosition();
1404 }
1405 }
1406
1407 return grid.ResolveSnap( aCursor, nullptr ).position;
1408 };
1409
1410 auto setCursor = [&]()
1411 {
1412 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
1413 };
1414
1415 m_controls->ShowCursor( true );
1416 m_controls->SetAutoPan( true );
1417 setCursor();
1418
1419 while( TOOL_EVENT* evt = Wait() )
1420 {
1421 setCursor();
1422
1423 // Match the via tool: item snaps are handled below, the grid helper only does
1424 // grid and anchor snapping against the GAL grid state.
1425 grid.SetSnap( false );
1426 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1427
1428 bool allowItemSnap = !evt->Modifier( MD_SHIFT );
1429 VECTOR2I rawCursor = m_controls->GetMousePosition();
1430 VECTOR2I cursorPos;
1431
1432 if( staggered && !hops.empty() )
1433 {
1434 // Cursor is locked onto a circle of radius = pitch around the previous hop.
1435 // It steers direction only, following the editor angle mode.
1436 VECTOR2I dir = rawCursor - hops.back();
1437
1438 if( dir.EuclideanNorm() < 1 )
1439 dir = VECTOR2I( 1, 0 );
1440
1441 LEADER_MODE angleSnap = GetAngleSnapMode();
1442
1443 if( evt->Modifier( MD_CTRL ) )
1444 angleSnap = LEADER_MODE::DIRECT;
1445
1446 if( angleSnap != LEADER_MODE::DIRECT )
1447 {
1448 double step = ( angleSnap == LEADER_MODE::DEG90 ) ? M_PI / 2 : M_PI / 4;
1449 double ang = atan2( (double) dir.y, (double) dir.x );
1450 ang = round( ang / step ) * step;
1451
1452 cursorPos = hops.back()
1453 + VECTOR2I( KiROUND( cos( ang ) * settings.GetPitch() ),
1454 KiROUND( sin( ang ) * settings.GetPitch() ) );
1455 }
1456 else
1457 {
1458 cursorPos = hops.back() + dir.Resize( settings.GetPitch() );
1459 }
1460
1461 lastDir = cursorPos - hops.back();
1462 }
1463 else
1464 {
1465 cursorPos = snapToCopper( rawCursor, allowItemSnap );
1466 }
1467
1468 // Only the staggered pitch circle locks the cursor. For the anchor and the
1469 // stacked case let the cross follow the mouse and snap the preview item instead,
1470 // exactly like plain via placement.
1471 if( staggered && !hops.empty() )
1472 m_controls->ForceCursorPosition( true, cursorPos );
1473 else
1474 m_controls->ForceCursorPosition( false );
1475
1476 if( evt->IsCancelInteractive() || evt->IsActivate() )
1477 {
1478 break;
1479 }
1480 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1481 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1482 {
1483 // The tool stays active for repeated placement, Esc leaves. Rapid clicks
1484 // arrive as double clicks and must place hops too.
1485 if( staggered )
1486 {
1487 hops.push_back( cursorPos );
1488
1489 if( (int) hops.size() >= nHops )
1490 {
1491 if( !placeStack( cursorPos ) )
1492 {
1493 // Blocked by DRC, let the user reposition the final hop and retry.
1494 hops.pop_back();
1495 }
1496 else
1497 {
1498 hops.clear();
1499 clearPreview();
1500
1501 // A router handoff places exactly one stack, then returns to routing.
1502 if( seeded )
1503 {
1504 resumeRouting = true;
1505 resumePos = lastViaPos;
1506 break;
1507 }
1508 }
1509 }
1510 else
1511 {
1512 // Show the next hop right away, continuing in the last direction,
1513 // instead of waiting for the mouse to move.
1514 cursorPos = hops.back() + lastDir.Resize( settings.GetPitch() );
1515 m_controls->ForceCursorPosition( true, cursorPos );
1516 updatePreview( cursorPos );
1517 }
1518 }
1519 else
1520 {
1521 if( placeStack( cursorPos ) )
1522 {
1523 clearPreview();
1524
1525 if( seeded )
1526 {
1527 resumeRouting = true;
1528 resumePos = lastViaPos;
1529 break;
1530 }
1531 }
1532 }
1533 }
1534 else if( evt->IsAction( &ACTIONS::undo ) || evt->IsAction( &ACTIONS::doDelete ) )
1535 {
1536 // Swallowed for the whole loop like the router does, or it would undo the
1537 // route this placement was handed off from.
1538 if( staggered && hops.size() > ( seeded ? 1u : 0u ) )
1539 {
1540 hops.pop_back();
1541 lastDir = VECTOR2I( 1, 0 );
1542 updatePreview( cursorPos );
1543 }
1544 }
1545 else if( evt->IsClick( BUT_RIGHT ) )
1546 {
1547 m_toolMgr->VetoContextMenuMouseWarp();
1548 m_menu->ShowContextMenu( selection() );
1549 }
1550 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
1551 {
1552 updatePreview( cursorPos );
1553 }
1554 else
1555 {
1556 evt->SetPassEvent();
1557 }
1558 }
1559
1560 clearPreview();
1561 m_view->Remove( &preview );
1562 m_controls->ForceCursorPosition( false );
1563 m_controls->SetAutoPan( false );
1564 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1565 m_frame->PopTool( aEvent );
1566
1567 // Return to routing from the last placed via when handed off from the router. Do not warp
1568 // the view (last arg false), recentering the canvas mid-workflow is jarring, unlike a via.
1569 // Priming starts the route at the via itself, not at whatever the next click snaps to.
1570 if( resumeRouting )
1571 {
1572 m_frame->SetActiveLayer( settings.GetEndLayer() );
1573
1574 if( ROUTER_TOOL* router = m_toolMgr->GetTool<ROUTER_TOOL>() )
1575 router->SetViaStackResumeLayer( settings.GetEndLayer() );
1576
1577 m_controls->WarpMouseCursor( resumePos, true, false );
1579 m_toolMgr->PrimeTool( resumePos );
1580 }
1581
1582 return 0;
1583}
1584
1585
1587
1588
1590
1591
1593{
1595 {
1597 .Map( VIA_STACK_STYLE::STACKED, _HKI( "Stacked" ) )
1598 .Map( VIA_STACK_STYLE::STAGGERED, _HKI( "Staggered" ) );
1599
1606
1608
1609 if( layerEnum.Choices().GetCount() == 0 )
1610 {
1611 layerEnum.Undefined( UNDEFINED_LAYER );
1612
1613 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
1614 layerEnum.Map( layer, LSET::Name( layer ) );
1615 }
1616
1617 // A stack spans layers, so the single inherited layer is meaningless, as for a via.
1618 propMgr.Mask( TYPE_HASH( PCB_VIA_STACK ), TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ) );
1619
1620 // Read-only: a stack's geometry is rebuilt from these, and the Properties panel has no
1621 // way to trigger that rebuild. Editing goes through the stack's own dialog.
1622 const wxString groupStack = _HKI( "Microvia Stack Properties" );
1623
1624 auto startLayer = new PROPERTY_ENUM<PCB_VIA_STACK, PCB_LAYER_ID>(
1626 startLayer->SetChoices( layerEnum.Choices() );
1627 propMgr.AddProperty( startLayer, groupStack );
1628
1631 endLayer->SetChoices( layerEnum.Choices() );
1632 propMgr.AddProperty( endLayer, groupStack );
1633
1634 propMgr.AddProperty(
1637 groupStack );
1638
1641
1642 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Use Netclass Values" ),
1643 NO_SETTER( PCB_VIA_STACK, bool ),
1645 groupStack );
1646
1647 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Via Diameter" ), NO_SETTER( PCB_VIA_STACK, int ),
1649 groupStack );
1650
1651 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Via Hole" ), NO_SETTER( PCB_VIA_STACK, int ),
1653 groupStack );
1654
1655 auto isStaggered = []( INSPECTABLE* aItem ) -> bool
1656 {
1657 if( PCB_VIA_STACK* stack = dynamic_cast<PCB_VIA_STACK*>( aItem ) )
1658 return stack->GetStyle() == VIA_STACK_STYLE::STAGGERED;
1659
1660 return false;
1661 };
1662
1663 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, int>( _HKI( "Pitch" ), NO_SETTER( PCB_VIA_STACK, int ),
1665 groupStack )
1666 .SetAvailableFunc( isStaggered );
1667
1668 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Copper-filled" ),
1669 NO_SETTER( PCB_VIA_STACK, bool ),
1671 groupStack );
1672
1673 propMgr.AddProperty( new PROPERTY<PCB_VIA_STACK, bool>( _HKI( "Capped" ), NO_SETTER( PCB_VIA_STACK, bool ),
1675 groupStack );
1676 }
const char * name
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
BASE_SET & set(size_t pos)
Definition base_set.h:126
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
bool RemoveOrDiscard(BOARD_ITEM *aItem)
Remove aItem, or free it if it was added earlier in this commit and never reached the board.
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.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
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:593
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
bool IsLocked() const override
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:410
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1524
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:3001
const ZONES & Zones() const
Definition board.h:468
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayer) const
Definition board.cpp:1131
int GetCopperLayerCount() const
Definition board.cpp:1138
const FOOTPRINTS & Footprints() const
Definition board.h:464
const TRACKS & Tracks() const
Definition board.h:462
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1695
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:200
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
SEVERITY GetSeverity() const
Definition drc_rule.h:256
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:204
bool IsNull() const
Definition drc_rule.h:197
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
wxString m_name
Definition eda_group.h:106
std::unordered_set< EDA_ITEM * > m_items
Definition eda_group.h:105
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
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
std::unordered_set< EDA_ITEM * > m_deserializedItems
Definition eda_group.h:109
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
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:789
static ENUM_MAP< T > & Instance()
Definition property.h:783
ENUM_MAP & Undefined(T aValue)
Definition property.h:796
wxPGChoices & Choices()
Definition property.h:834
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
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
T PinnedOpt() const
Definition minoptmax.h:32
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:43
bool HasuViaDrill() const
Definition netclass.h:173
int GetuViaDrill() const
Definition netclass.h:174
bool HasuViaDiameter() const
Definition netclass.h:165
int GetuViaDiameter() const
Definition netclass.h:166
bool HasTrackWidth() const
Definition netclass.h:141
int GetTrackWidth() const
Definition netclass.h:142
Handle the data for a net.
Definition netinfo.h:50
NETCLASS * GetNetClass()
Definition netinfo.h:101
DRILL_PROPS & Drill()
Definition padstack.h:356
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
void SetLocked(bool aLocked) override
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:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
virtual int GetWidth() const
Definition pcb_track.h:92
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, wxString *aError=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
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
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 Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
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
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
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)
static std::pair< int, int > GetNetclassViaSize(BOARD *aBoard, int aNetCode)
The microvia diameter and drill that "use netclass values" gives hops on aNetCode,...
PCB_LAYER_ID BottomLayer() const
VECTOR2I GetPosition() const override
Definition pcb_track.h:599
const PADSTACK & Padstack() const
Definition pcb_track.h:427
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:419
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:418
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.
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
Return the instance of KIGFX::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 * GetTool()
Return the tool of given type or nullptr if there is no such tool registered.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
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)
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:61
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define IS_NEW
New item, just created.
#define ROUTER_TRANSIENT
transient items that should NOT be cached
#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
#define MAX_CU_LAYERS
Definition layer_ids.h:172
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:244
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
KICOMMON_API void PackCustomProperties(google::protobuf::RepeatedPtrField< types::CustomProperty > *aOutput, const EDA_ITEM &aItem)
KICOMMON_API SHAPE_LINE_CHAIN UnpackPolyLine(const types::PolyLine &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void UnpackCustomProperties(const google::protobuf::RepeatedPtrField< types::CustomProperty > &aInput, EDA_ITEM &aItem)
KICOMMON_API void PackPolyLine(types::PolyLine &aOutput, const SHAPE_LINE_CHAIN &aSlc, const EDA_IU_SCALE &aScale)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ 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)
Macro to generate unique identifier for a type.
Definition property.h:74
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:888
#define NO_SETTER(owner, type)
Macro to define read-only fields (no setter method available)
Definition property.h:895
@ 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)
Helper macro to map type hashes to names.
@ RPT_SEVERITY_IGNORE
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)
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
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:708