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