KiCad PCB EDA Suite
Loading...
Searching...
No Matches
padstack.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 (C) 2024 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <convert_basic_shapes_to_polygon.h> // RECT_CHAMFER_POSITIONS
22#include "padstack.h"
23#include <api/api_enums.h>
24#include <api/api_utils.h>
25#include <api/api_pcb_utils.h>
26#include <api/board/board_types.pb.h>
27#include <layer_range.h>
28#include <macros.h>
29#include <magic_enum.hpp>
30#include <pad.h>
31#include <pcb_shape.h>
32
33
35 m_parent( aParent ),
36 m_mode( MODE::NORMAL ),
37 m_orientation( ANGLE_0 ),
38 m_unconnectedLayerMode( UNCONNECTED_LAYER_MODE::KEEP_ALL ),
39 m_customShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE::OUTLINE )
40{
42 m_copperProps[PADSTACK::ALL_LAYERS].zone_connection = ZONE_CONNECTION::INHERITED;
43 m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_width = std::nullopt;
44 m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_angle = ANGLE_45;
45 m_copperProps[PADSTACK::ALL_LAYERS].thermal_gap = std::nullopt;
46
47 m_drill.shape = PAD_DRILL_SHAPE::CIRCLE;
50
51 m_secondaryDrill.shape = PAD_DRILL_SHAPE::UNDEFINED;
54}
55
56
58{
59 m_parent = aOther.m_parent;
60 *this = aOther;
61
63 [&]( PCB_LAYER_ID aLayer )
64 {
65 for( std::shared_ptr<PCB_SHAPE>& shape : CopperLayer( aLayer ).custom_shapes )
66 shape->SetParent( m_parent );
67 } );
68}
69
70
72{
73 // NOTE: m_parent is not copied from operator=, because this operator is commonly used to
74 // update the padstack properties, and such an update must not change the parent PAD to point to
75 // the parent of some different padstack.
76
77 m_mode = aOther.m_mode;
78 m_layerSet = aOther.m_layerSet;
86 m_drill = aOther.m_drill;
88
89 // Data consistency enforcement logic that used to live in the pad properties dialog
90 // TODO(JE) Should these move to individual property setters, so that they are always
91 // enforced even through the properties panel and API?
92
94 [&]( PCB_LAYER_ID aLayer )
95 {
96 PAD_SHAPE shape = Shape( aLayer );
97
98 // Make sure leftover primitives don't stick around
99 ClearPrimitives( aLayer );
100
101 // For custom pad shape, duplicate primitives of the pad to copy
102 if( shape == PAD_SHAPE::CUSTOM )
103 ReplacePrimitives( aOther.Primitives( aLayer ), aLayer );
104
105 // rounded rect pads with radius ratio = 0 are in fact rect pads.
106 // So set the right shape (and perhaps issues with a radius = 0)
107 if( shape == PAD_SHAPE::ROUNDRECT && RoundRectRadiusRatio( aLayer ) == 0.0 )
108 SetShape( PAD_SHAPE::RECTANGLE, aLayer );
109 } );
110
111 return *this;
112}
113
114
115bool PADSTACK::operator==( const PADSTACK& aOther ) const
116{
117 if( m_mode != aOther.m_mode )
118 return false;
119
120 if( m_layerSet != aOther.m_layerSet )
121 return false;
122
123 if( m_customName != aOther.m_customName )
124 return false;
125
126 if( m_orientation != aOther.m_orientation )
127 return false;
128
129 if( m_frontMaskProps != aOther.m_frontMaskProps )
130 return false;
131
132 if( m_backMaskProps != aOther.m_backMaskProps )
133 return false;
134
136 return false;
137
139 return false;
140
141 if( m_drill != aOther.m_drill )
142 return false;
143
144 if( m_secondaryDrill != aOther.m_secondaryDrill )
145 return false;
146
147 bool copperMatches = true;
148
150 [&]( PCB_LAYER_ID aLayer )
151 {
152 if( CopperLayer( aLayer ) != aOther.CopperLayer( aLayer ) )
153 copperMatches = false;
154 } );
155
156 return copperMatches;
157}
158
159
160bool PADSTACK::unpackCopperLayer( const kiapi::board::types::PadStackLayer& aProto )
161{
162 using namespace kiapi::board::types;
163 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID, BoardLayer>( aProto.layer() );
164
165 if( m_mode == MODE::NORMAL && layer != ALL_LAYERS )
166 return false;
167
168 if( m_mode == MODE::FRONT_INNER_BACK && layer != F_Cu && layer != INNER_LAYERS && layer != B_Cu )
169 return false;
170
171 SetSize( kiapi::common::UnpackVector2( aProto.size() ), layer );
172 SetShape( FromProtoEnum<PAD_SHAPE>( aProto.shape() ), layer );
173 Offset( layer ) = kiapi::common::UnpackVector2( aProto.offset() );
174 SetAnchorShape( FromProtoEnum<PAD_SHAPE>( aProto.custom_anchor_shape() ), layer );
175
176 SHAPE_PROPS& props = CopperLayer( layer ).shape;
177 props.chamfered_rect_ratio = aProto.chamfer_ratio();
178 props.round_rect_radius_ratio = aProto.corner_rounding_ratio();
179
180 if( Shape( layer ) == PAD_SHAPE::TRAPEZOID && aProto.has_trapezoid_delta() )
181 TrapezoidDeltaSize( layer ) = kiapi::common::UnpackVector2( aProto.trapezoid_delta() );
182
183 if( aProto.chamfered_corners().top_left() )
185
186 if( aProto.chamfered_corners().top_right() )
188
189 if( aProto.chamfered_corners().bottom_left() )
191
192 if( aProto.chamfered_corners().bottom_right() )
194
195 ClearPrimitives( layer );
196 google::protobuf::Any a;
197
198 for( const BoardGraphicShape& shapeProto : aProto.custom_shapes() )
199 {
200 a.PackFrom( shapeProto );
201 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_parent );
202
203 if( shape->Deserialize( a ) )
204 AddPrimitive( shape.release(), layer );
205 }
206
207 return true;
208}
209
210
211bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
212{
213 using namespace kiapi::board::types;
214 PadStack padstack;
215
216 if( !aContainer.UnpackTo( &padstack ) )
217 return false;
218
219 m_mode = FromProtoEnum<MODE>( padstack.type() );
220 SetLayerSet( kiapi::board::UnpackLayerSet( padstack.layers() ) );
221 m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T );
222
223 Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() );
224 Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
225 Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
226
227 for( const PadStackLayer& layer : padstack.copper_layers() )
228 {
229 if( !unpackCopperLayer( layer ) )
230 return false;
231 }
232
233 CopperLayer( ALL_LAYERS ).thermal_gap = std::nullopt;
235
236 if( padstack.has_zone_settings() )
237 {
239 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
240
241 if( padstack.zone_settings().has_thermal_spokes() )
242 {
243 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
244
245 if( thermals.has_gap() )
246 CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap().value_nm();
247
248 if( thermals.has_width() )
249 CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width().value_nm();
250
251 SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu );
252 }
253 }
254 else
255 {
256 CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED;
258 }
259
261 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
262
263 auto unpackMask =
264 []( const SolderMaskMode& aProto, std::optional<bool>& aDest )
265 {
266 switch( aProto )
267 {
268 case kiapi::board::types::SMM_MASKED:
269 aDest = true;
270 break;
271
272 case kiapi::board::types::SMM_UNMASKED:
273 aDest = false;
274 break;
275
276 default:
277 case kiapi::board::types::SMM_FROM_DESIGN_RULES:
278 aDest = std::nullopt;
279 break;
280 }
281 };
282
283 unpackMask( padstack.front_outer_layers().solder_mask_mode(),
284 FrontOuterLayers().has_solder_mask );
285
286 unpackMask( padstack.back_outer_layers().solder_mask_mode(),
287 BackOuterLayers().has_solder_mask );
288
289 auto unpackPaste =
290 []( const SolderPasteMode& aProto, std::optional<bool>& aDest )
291 {
292 switch( aProto )
293 {
294 case kiapi::board::types::SPM_PASTE:
295 aDest = true;
296 break;
297
298 case kiapi::board::types::SPM_NO_PASTE:
299 aDest = false;
300 break;
301
302 default:
303 case kiapi::board::types::SPM_FROM_DESIGN_RULES:
304 aDest = std::nullopt;
305 break;
306 }
307 };
308
309 unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
310 FrontOuterLayers().has_solder_paste );
311
312 unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
313 BackOuterLayers().has_solder_paste );
314
315 if( padstack.front_outer_layers().has_solder_mask_settings()
316 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
317 {
319 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
320 }
321 else
322 {
323 FrontOuterLayers().solder_mask_margin = std::nullopt;
324 }
325
326 if( padstack.back_outer_layers().has_solder_mask_settings()
327 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
328 {
330 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
331 }
332 else
333 {
334 BackOuterLayers().solder_mask_margin = std::nullopt;
335 }
336
337 if( padstack.front_outer_layers().has_solder_paste_settings()
338 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
339 {
341 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
342 }
343 else
344 {
345 FrontOuterLayers().solder_paste_margin = std::nullopt;
346 }
347
348 if( padstack.back_outer_layers().has_solder_paste_settings()
349 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
350 {
352 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
353 }
354 else
355 {
356 BackOuterLayers().solder_paste_margin = std::nullopt;
357 }
358
359 if( padstack.front_outer_layers().has_solder_paste_settings()
360 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
361 {
363 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
364 }
365 else
366 {
368 }
369
370 if( padstack.back_outer_layers().has_solder_paste_settings()
371 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
372 {
374 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
375 }
376 else
377 {
379 }
380
381
382 return true;
383}
384
385
386void PADSTACK::packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const
387{
388 using namespace kiapi::board::types;
389
390 PadStackLayer* stackLayer = aProto.add_copper_layers();
391
392 stackLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
393 kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( aLayer ) );
394 kiapi::common::PackVector2( *stackLayer->mutable_offset(), Offset( aLayer ) );
395
396 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>( Shape( aLayer ) ) );
397
398 stackLayer->set_custom_anchor_shape(
399 ToProtoEnum<PAD_SHAPE, PadStackShape>( AnchorShape( aLayer ) ) );
400
401 stackLayer->set_chamfer_ratio( CopperLayer( aLayer ).shape.chamfered_rect_ratio );
402 stackLayer->set_corner_rounding_ratio( CopperLayer( aLayer ).shape.round_rect_radius_ratio );
403
404 if( Shape( aLayer ) == PAD_SHAPE::TRAPEZOID )
405 {
406 kiapi::common::PackVector2( *stackLayer->mutable_trapezoid_delta(),
407 TrapezoidDeltaSize( aLayer ) );
408 }
409
410 google::protobuf::Any a;
411
412 for( const std::shared_ptr<PCB_SHAPE>& shape : Primitives( aLayer ) )
413 {
414 shape->Serialize( a );
415 BoardGraphicShape* s = stackLayer->add_custom_shapes();
416 a.UnpackTo( s );
417 }
418
419 const int& corners = CopperLayer( aLayer ).shape.chamfered_rect_positions;
420 stackLayer->mutable_chamfered_corners()->set_top_left( corners & RECT_CHAMFER_TOP_LEFT );
421 stackLayer->mutable_chamfered_corners()->set_top_right( corners & RECT_CHAMFER_TOP_RIGHT );
422 stackLayer->mutable_chamfered_corners()->set_bottom_left( corners & RECT_CHAMFER_BOTTOM_LEFT );
423 stackLayer->mutable_chamfered_corners()->set_bottom_right( corners & RECT_CHAMFER_BOTTOM_RIGHT );
424}
425
426
427void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
428{
429 using namespace kiapi::board::types;
430 PadStack padstack;
431
432 padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
433 kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet );
434 padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
435
436 DrillProperties* drill = padstack.mutable_drill();
437 drill->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( StartLayer() ) );
438 drill->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( EndLayer() ) );
439 kiapi::common::PackVector2( *drill->mutable_diameter(), Drill().size );
440
441 ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer )
442 {
443 packCopperLayer( aLayer, padstack );
444 } );
445
446 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
447 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
448
449 if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() )
450 {
451 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
452 *CopperLayer( ALL_LAYERS ).zone_connection ) );
453 }
454
455 if( std::optional<int> width = CopperLayer( ALL_LAYERS ).thermal_spoke_width )
456 thermalSettings->mutable_width()->set_value_nm( *width );
457
458 if( std::optional<int> gap = CopperLayer( ALL_LAYERS ).thermal_gap )
459 thermalSettings->mutable_gap()->set_value_nm( *gap );
460
461 thermalSettings->mutable_angle()->set_value_degrees( ThermalSpokeAngle( F_Cu ).AsDegrees() );
462
463 padstack.set_unconnected_layer_removal(
464 ToProtoEnum<UNCONNECTED_LAYER_MODE, UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
465
466 auto packOptional =
467 []<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
468 ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
469 {
470 if( aVal.has_value() )
471 return *aVal ? aTrueVal : aFalseVal;
472
473 return aNullVal;
474 };
475
476 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
477 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
478
479 frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask,
480 SMM_MASKED, SMM_UNMASKED,
481 SMM_FROM_DESIGN_RULES ) );
482
483 backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask,
484 SMM_MASKED, SMM_UNMASKED,
485 SMM_FROM_DESIGN_RULES ) );
486
487 frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste,
488 SPM_PASTE, SPM_NO_PASTE,
489 SPM_FROM_DESIGN_RULES ) );
490
491 backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste,
492 SPM_PASTE, SPM_NO_PASTE,
493 SPM_FROM_DESIGN_RULES ) );
494
495 if( FrontOuterLayers().solder_mask_margin.has_value() )
496 {
497 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
499 }
500
501 if( BackOuterLayers().solder_mask_margin.has_value() )
502 {
503 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
505 }
506
507 if( FrontOuterLayers().solder_paste_margin.has_value() )
508 {
509 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
511 }
512
513 if( BackOuterLayers().solder_paste_margin.has_value() )
514 {
515 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
517 }
518
520 {
521 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
523 }
524
526 {
527 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
529 }
530
531 aContainer.PackFrom( padstack );
532}
533
534
535int PADSTACK::Compare( const PADSTACK* aPadstackRef, const PADSTACK* aPadstackCmp )
536{
537 int diff;
538
539 auto compareCopperProps =
540 [&]( PCB_LAYER_ID aLayer )
541 {
542 if( ( diff = static_cast<int>( aPadstackRef->Shape( aLayer ) ) -
543 static_cast<int>( aPadstackCmp->Shape( aLayer ) ) ) != 0 )
544 return diff;
545
546 if( ( diff = aPadstackRef->Size( aLayer ).x - aPadstackCmp->Size( aLayer ).x ) != 0 )
547 return diff;
548
549 if( ( diff = aPadstackRef->Size( aLayer ).y - aPadstackCmp->Size( aLayer ).y ) != 0 )
550 return diff;
551
552 if( ( diff = aPadstackRef->Offset( aLayer ).x
553 - aPadstackCmp->Offset( aLayer ).x ) != 0 )
554 return diff;
555
556 if( ( diff = aPadstackRef->Offset( aLayer ).y
557 - aPadstackCmp->Offset( aLayer ).y ) != 0 )
558 return diff;
559
560 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).x
561 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).x )
562 != 0 )
563 {
564 return diff;
565 }
566
567 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).y
568 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).y )
569 != 0 )
570 {
571 return diff;
572 }
573
574 if( ( diff = aPadstackRef->RoundRectRadiusRatio( aLayer )
575 - aPadstackCmp->RoundRectRadiusRatio( aLayer ) )
576 != 0 )
577 {
578 return diff;
579 }
580
581 if( ( diff = aPadstackRef->ChamferPositions( aLayer )
582 - aPadstackCmp->ChamferPositions( aLayer ) ) != 0 )
583 return diff;
584
585 if( ( diff = aPadstackRef->ChamferRatio( aLayer )
586 - aPadstackCmp->ChamferRatio( aLayer ) ) != 0 )
587 return diff;
588
589 if( ( diff = static_cast<int>( aPadstackRef->Primitives( aLayer ).size() ) -
590 static_cast<int>( aPadstackCmp->Primitives( aLayer ).size() ) ) != 0 )
591 return diff;
592
593 // @todo: Compare custom pad primitives for pads that have the same number of primitives
594 // here. Currently there is no compare function for PCB_SHAPE objects.
595 return 0;
596 };
597
598 aPadstackRef->ForEachUniqueLayer( compareCopperProps );
599
600 if( ( diff = static_cast<int>( aPadstackRef->DrillShape() ) -
601 static_cast<int>( aPadstackCmp->DrillShape() ) ) != 0 )
602 return diff;
603
604 if( ( diff = aPadstackRef->Drill().size.x - aPadstackCmp->Drill().size.x ) != 0 )
605 return diff;
606
607 if( ( diff = aPadstackRef->Drill().size.y - aPadstackCmp->Drill().size.y ) != 0 )
608 return diff;
609
610 return aPadstackRef->LayerSet().compare( aPadstackCmp->LayerSet() );
611}
612
613
614double PADSTACK::Similarity( const PADSTACK& aOther ) const
615{
616 double similarity = 1.0;
617
619 [&]( PCB_LAYER_ID aLayer )
620 {
621 if( Shape( aLayer ) != aOther.Shape( aLayer ) )
622 similarity *= 0.9;
623
624 if( Size( aLayer ) != aOther.Size( aLayer ) )
625 similarity *= 0.9;
626
627 if( Offset( aLayer ) != aOther.Offset( aLayer ) )
628 similarity *= 0.9;
629
630 if( RoundRectRadiusRatio( aLayer ) != aOther.RoundRectRadiusRatio( aLayer ) )
631 similarity *= 0.9;
632
633 if( ChamferRatio( aLayer ) != aOther.ChamferRatio( aLayer ) )
634 similarity *= 0.9;
635
636 if( ChamferPositions( aLayer ) != aOther.ChamferPositions( aLayer ) )
637 similarity *= 0.9;
638
639 if( Primitives( aLayer ).size() != aOther.Primitives( aLayer ).size() )
640 similarity *= 0.9;
641
642 if( AnchorShape( aLayer ) != aOther.AnchorShape( aLayer ) )
643 similarity *= 0.9;
644 } );
645
646 if( Drill() != aOther.Drill() )
647 similarity *= 0.9;
648
649 if( DrillShape() != aOther.DrillShape() )
650 similarity *= 0.9;
651
652 if( GetOrientation() != aOther.GetOrientation() )
653 similarity *= 0.9;
654
655 if( ZoneConnection() != aOther.ZoneConnection() )
656 similarity *= 0.9;
657
658 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
659 similarity *= 0.9;
660
661 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
662 similarity *= 0.9;
663
664 if( ThermalGap() != aOther.ThermalGap() )
665 similarity *= 0.9;
666
668 similarity *= 0.9;
669
670 if( Clearance() != aOther.Clearance() )
671 similarity *= 0.9;
672
673 if( SolderMaskMargin() != aOther.SolderMaskMargin() )
674 similarity *= 0.9;
675
676 if( SolderPasteMargin() != aOther.SolderPasteMargin() )
677 similarity *= 0.9;
678
680 similarity *= 0.9;
681
682 if( ThermalGap() != aOther.ThermalGap() )
683 similarity *= 0.9;
684
685 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
686 similarity *= 0.9;
687
688 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
689 similarity *= 0.9;
690
691 if( LayerSet() != aOther.LayerSet() )
692 similarity *= 0.9;
693
694 return similarity;
695}
696
697
698wxString PADSTACK::Name() const
699{
700 // TODO
701 return wxEmptyString;
702}
703
704
706{
707 return m_drill.start;
708}
709
710
712{
713 return m_drill.end;
714}
715
716
717void PADSTACK::FlipLayers( int aCopperLayerCount )
718{
719 switch( m_mode )
720 {
721 case MODE::NORMAL:
722 // Same shape on all layers; nothing to do
723 break;
724
725 case MODE::CUSTOM:
726 {
727 if( aCopperLayerCount > 2 )
728 {
729 int innerCount = ( aCopperLayerCount - 2 );
730 int halfInnerLayerCount = innerCount / 2;
731 PCB_LAYER_ID lastInner
732 = static_cast<PCB_LAYER_ID>( In1_Cu + ( innerCount - 1 ) * 2 );
733 PCB_LAYER_ID midpointInner
734 = static_cast<PCB_LAYER_ID>( In1_Cu + ( halfInnerLayerCount - 1 ) * 2 );
735
736 for( PCB_LAYER_ID layer : LAYER_RANGE( In1_Cu, midpointInner, MAX_CU_LAYERS ) )
737 {
738 auto conjugate =
739 magic_enum::enum_cast<PCB_LAYER_ID>( lastInner - ( layer - In1_Cu ) );
740 wxCHECK2_MSG( conjugate.has_value() && m_copperProps.contains( conjugate.value() ),
741 continue, "Invalid inner layer conjugate!" );
742 std::swap( m_copperProps[layer], m_copperProps[conjugate.value()] );
743 }
744 }
745
747 }
748
750 std::swap( m_copperProps[F_Cu], m_copperProps[B_Cu] );
751 std::swap( m_frontMaskProps, m_backMaskProps );
752 break;
753 }
754}
755
756
758 shape( PAD_SHAPE::CIRCLE ),
759 anchor_shape( PAD_SHAPE::CIRCLE ),
760 round_rect_corner_radius( 0 ),
761 round_rect_radius_ratio( 0.25 ),
762 chamfered_rect_ratio( 0.2 ),
763 chamfered_rect_positions( RECT_NO_CHAMFER )
764{
765}
766
767
769{
770 return shape == aOther.shape && offset == aOther.offset
771 && round_rect_corner_radius == aOther.round_rect_corner_radius
772 && round_rect_radius_ratio == aOther.round_rect_radius_ratio
773 && chamfered_rect_ratio == aOther.chamfered_rect_ratio
774 && chamfered_rect_positions == aOther.chamfered_rect_positions;
775}
776
777
779{
780 if( shape != aOther.shape )
781 return false;
782
783 if( zone_connection != aOther.zone_connection )
784 return false;
785
786 if( thermal_spoke_width != aOther.thermal_spoke_width )
787 return false;
788
789 if( thermal_spoke_angle != aOther.thermal_spoke_angle )
790 return false;
791
792 if( thermal_gap != aOther.thermal_gap )
793 return false;
794
795 if( custom_shapes.size() != aOther.custom_shapes.size() )
796 return false;
797
798 if( !std::equal( custom_shapes.begin(), custom_shapes.end(),
799 aOther.custom_shapes.begin(), aOther.custom_shapes.end(),
800 []( const std::shared_ptr<PCB_SHAPE>& aFirst,
801 const std::shared_ptr<PCB_SHAPE>& aSecond )
802 {
803 return *aFirst == *aSecond;
804 } ) )
805 {
806 return false;
807 }
808
809 return true;
810}
811
812
814{
815 return solder_mask_margin == aOther.solder_mask_margin
816 && solder_paste_margin == aOther.solder_paste_margin
817 && solder_paste_margin_ratio == aOther.solder_paste_margin_ratio
818 && has_solder_mask == aOther.has_solder_mask
819 && has_solder_paste == aOther.has_solder_paste;
820}
821
822
824{
825 return size == aOther.size && shape == aOther.shape
826 && start == aOther.start && end == aOther.end;
827}
828
829
831{
832 if( m_mode == aMode )
833 return;
834
835 switch( aMode )
836 {
837 case MODE::NORMAL:
838 std::erase_if( m_copperProps,
839 []( const auto& aEntry )
840 {
841 const auto& [key, value] = aEntry;
842 return key != ALL_LAYERS;
843 } );
844 break;
845
847 // When coming from normal, these layers may be missing or have junk values
848 if( m_mode == MODE::NORMAL )
849 {
852 }
853
854 break;
855
856 case MODE::CUSTOM:
857 {
858 PCB_LAYER_ID innerLayerTemplate = ( m_mode == MODE::NORMAL ) ? ALL_LAYERS : INNER_LAYERS;
859
861 m_copperProps[layer] = m_copperProps[innerLayerTemplate];
862
863 if( m_mode == MODE::NORMAL )
865
866 break;
867 }
868 }
869
870 m_mode = aMode;
871
872 // Changing mode invalidates cached shapes
873 // TODO(JE) clean this up -- maybe PADSTACK should own shape caches
874 if( PAD* parentPad = dynamic_cast<PAD*>( m_parent ) )
875 parentPad->SetDirty();
876}
877
878
879void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& aMethod ) const
880{
881 switch( Mode() )
882 {
883 case MODE::NORMAL:
884 aMethod( F_Cu );
885 break;
886
888 aMethod( F_Cu );
889 aMethod( INNER_LAYERS );
890 aMethod( B_Cu );
891 break;
892
893 case MODE::CUSTOM:
894 {
896
897 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
898 aMethod( layer );
899
900 break;
901 }
902 }
903}
904
905
906std::vector<PCB_LAYER_ID> PADSTACK::UniqueLayers() const
907{
908 switch( Mode() )
909 {
910 default:
911 case MODE::NORMAL:
912 return { F_Cu };
913
915 return { F_Cu, INNER_LAYERS, B_Cu };
916
917 case MODE::CUSTOM:
918 {
919 std::vector<PCB_LAYER_ID> layers;
921
922 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
923 layers.push_back( layer );
924
925 return layers;
926 }
927 }
928}
929
930
932{
933 switch( static_cast<int>( aLayer ) )
934 {
936 return F_Cu;
937
939 return Mode() == MODE::NORMAL ? F_Cu : B_Cu;
940
941 // For these, just give the front copper geometry, it doesn't matter.
944 case LAYER_PADS:
946 case LAYER_VIA_HOLES:
949 return ALL_LAYERS;
950
951 default:
952 break;
953 }
954
955 switch( Mode() )
956 {
957 case MODE::CUSTOM:
959 {
960 PCB_LAYER_ID boardCuLayer = aLayer;
961
962 if( IsViaCopperLayer( aLayer ) )
963 boardCuLayer = ToLAYER_ID( static_cast<int>( aLayer ) - LAYER_VIA_COPPER_START );
964 else if( IsPadCopperLayer( aLayer ) )
965 boardCuLayer = ToLAYER_ID( static_cast<int>( aLayer ) - LAYER_PAD_COPPER_START );
966 else if( IsClearanceLayer( aLayer ) )
967 boardCuLayer = ToLAYER_ID( static_cast<int>( aLayer ) - LAYER_CLEARANCE_START );
968
969 if( IsFrontLayer( boardCuLayer ) )
970 return F_Cu;
971
972 if( IsBackLayer( boardCuLayer ) )
973 return B_Cu;
974
975 wxASSERT_MSG( IsCopperLayer( boardCuLayer ),
976 wxString::Format( wxT( "Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
977 aLayer ) );
978
980 return INNER_LAYERS;
981
982 // Custom padstack: Clamp to parent board's stackup if present
983 if( m_parent )
984 {
985 LSET boardCopper = m_parent->BoardLayerSet() & LSET::AllCuMask();
986
987 if( boardCopper.Contains( boardCuLayer ) )
988 return boardCuLayer;
989
990 // We're asked for an inner copper layer not present in the board. There is no right
991 // answer here, so fall back on the front shape
992 return ALL_LAYERS;
993 }
994
995 // No parent, just pass through
996 return boardCuLayer;
997 }
998
999 case MODE::NORMAL:
1000 break;
1001 }
1002
1003 return F_Cu;
1004}
1005
1006
1008{
1009 LSET ret;
1010
1011#ifdef DEBUG
1012 if( m_parent && aOther.m_parent
1013 && ( m_mode == MODE::CUSTOM || aOther.m_mode == MODE::CUSTOM ) )
1014 {
1015 wxASSERT_MSG( m_parent->BoardCopperLayerCount() == aOther.m_parent->BoardCopperLayerCount(),
1016 wxT( "Expected both padstacks to have the same board copper layer count" ) );
1017 }
1018#endif
1019
1020 ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer ) { ret.set( aLayer ); } );
1021 aOther.ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer ) { ret.set( aLayer ); } );
1022
1023 return ret;
1024}
1025
1026
1028{
1029 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
1030 // Create on-demand
1031 return m_copperProps[layer];
1032}
1033
1034
1036{
1037 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
1038
1039 wxCHECK_MSG( m_copperProps.contains( layer ), m_copperProps.at( ALL_LAYERS ),
1040 "Attempt to retrieve layer " + std::string( magic_enum::enum_name( layer ) ) + " from a "
1041 "padstack that does not contain it" );
1042
1043 return m_copperProps.at( layer );
1044}
1045
1046
1048{
1049 return CopperLayer( aLayer ).shape.shape;
1050}
1051
1052
1054{
1055 CopperLayer( aLayer ).shape.shape = aShape;
1056}
1057
1058
1059void PADSTACK::SetSize( const VECTOR2I& aSize, PCB_LAYER_ID aLayer )
1060{
1061 // File formats do not enforce that sizes are always positive, but KiCad requires it
1062 VECTOR2I& size = CopperLayer( aLayer ).shape.size;
1063 size.x = std::abs( aSize.x );
1064 size.y = std::abs( aSize.y );
1065}
1066
1067
1069{
1070 return CopperLayer( aLayer ).shape.size;
1071}
1072
1073
1075{
1076 return m_drill.shape;
1077}
1078
1079
1081{
1082 m_drill.shape = aShape;
1083}
1084
1085
1087{
1088 return CopperLayer( aLayer ).shape.offset;
1089}
1090
1091
1093{
1094 return CopperLayer( aLayer ).shape.offset;
1095}
1096
1097
1099{
1100 return CopperLayer( aLayer ).shape.anchor_shape;
1101}
1102
1103
1105{
1106 CopperLayer( aLayer ).shape.anchor_shape = aShape;
1107}
1108
1109
1111{
1112 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
1113}
1114
1115
1117{
1118 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
1119}
1120
1121
1123{
1124 return CopperLayer( aLayer ).shape.round_rect_radius_ratio;
1125}
1126
1127
1129{
1130 CopperLayer( aLayer ).shape.round_rect_radius_ratio = aRatio;
1131}
1132
1133
1135{
1136 const VECTOR2I& size = Size( aLayer );
1137 return KiROUND( std::min( size.x, size.y ) * RoundRectRadiusRatio( aLayer ) );
1138}
1139
1140
1141void PADSTACK::SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer )
1142{
1143 const VECTOR2I& size = Size( aLayer );
1144 int min_r = std::min( size.x, size.y );
1145
1146 if( min_r > 0 )
1147 SetRoundRectRadiusRatio( aRadius / min_r, aLayer );
1148}
1149
1150
1152{
1153 return CopperLayer( aLayer ).shape.chamfered_rect_ratio;
1154}
1155
1156
1157void PADSTACK::SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer )
1158{
1159 CopperLayer( aLayer ).shape.chamfered_rect_ratio = aRatio;
1160}
1161
1162
1164{
1166}
1167
1168
1170{
1172}
1173
1174
1175void PADSTACK::SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer )
1176{
1177 CopperLayer( aLayer ).shape.chamfered_rect_positions = aPositions;
1178}
1179
1180
1181std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer )
1182{
1183 return CopperLayer( aLayer ).clearance;
1184}
1185
1186
1187const std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer ) const
1188{
1189 return CopperLayer( aLayer ).clearance;
1190}
1191
1192
1193std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer )
1194{
1197}
1198
1199
1200const std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer ) const
1201{
1204}
1205
1206
1207std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer )
1208{
1211}
1212
1213
1214const std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer ) const
1215{
1218
1219
1220std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer )
1221{
1224}
1225
1226
1227const std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer ) const
1228{
1231}
1232
1233
1234std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer )
1235{
1236 return CopperLayer( aLayer ).zone_connection;
1237}
1238
1239
1240const std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer ) const
1241{
1242 return CopperLayer( aLayer ).zone_connection;
1243}
1244
1245
1246std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer )
1247{
1248 return CopperLayer( aLayer ).thermal_spoke_width;
1249}
1250
1251
1252const std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer ) const
1253{
1254 return CopperLayer( aLayer ).thermal_spoke_width;
1255}
1256
1257
1258std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer )
1259{
1260 return CopperLayer( aLayer ).thermal_gap;
1261}
1262
1263
1264const std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const
1265{
1266 return CopperLayer( aLayer ).thermal_gap;
1267}
1268
1269
1271{
1272 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1273
1274 return ( defaults.shape.shape == PAD_SHAPE::CIRCLE
1275 || ( defaults.shape.shape == PAD_SHAPE::CUSTOM
1276 && defaults.shape.anchor_shape == PAD_SHAPE::CIRCLE ) )
1277 ? ANGLE_45 : ANGLE_90;
1278}
1279
1280
1282{
1283 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1284
1285 return defaults.thermal_spoke_angle.value_or( DefaultThermalSpokeAngleForShape( aLayer ) );
1286}
1287
1288
1290{
1291 CopperLayer( aLayer ).thermal_spoke_angle = aAngle;
1292}
1293
1294
1295std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer )
1296{
1297 return CopperLayer( aLayer ).custom_shapes;
1298}
1299
1300
1301const std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer ) const
1302{
1303 return CopperLayer( aLayer ).custom_shapes;
1304}
1305
1306
1308{
1309 aShape->SetParent( m_parent );
1310 CopperLayer( aLayer ).custom_shapes.emplace_back( aShape );
1311}
1312
1313
1314void PADSTACK::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1315 PCB_LAYER_ID aLayer )
1316{
1317 for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1318 AddPrimitive( new PCB_SHAPE( *prim ), aLayer );
1319}
1320
1321
1322void PADSTACK::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1323 PCB_LAYER_ID aLayer )
1324{
1325 ClearPrimitives( aLayer );
1326
1327 if( aPrimitivesList.size() )
1328 AppendPrimitives( aPrimitivesList, aLayer );
1329}
1330
1331
1333{
1334 CopperLayer( aLayer ).custom_shapes.clear();
1335}
1336
1337
1338std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
1339{
1340 if( IsFrontLayer( aSide ) )
1342
1343 if( IsBackLayer( aSide ) )
1345
1346 wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" );
1347}
1348
1349
@ NORMAL
Use all material properties from model file.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
int compare(const BASE_SET &other) const
Definition: base_set.h:207
BASE_SET & set(size_t pos)
Definition: base_set.h:116
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
Definition: board_item.cpp:128
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Definition: board_item.cpp:117
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
double AsDegrees() const
Definition: eda_angle.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:562
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition: padstack.h:124
bool operator==(const PADSTACK &aOther) const
Definition: padstack.cpp:115
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const
Definition: padstack.h:326
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1181
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
Definition: padstack.cpp:1307
void ReplacePrimitives(const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList, PCB_LAYER_ID aLayer)
Clears the existing primitive list (freeing the owned shapes) and adds copies of the given shapes to ...
Definition: padstack.cpp:1322
void packCopperLayer(PCB_LAYER_ID aLayer, kiapi::board::types::PadStack &aProto) const
Definition: padstack.cpp:386
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: padstack.cpp:211
MASK_LAYER_PROPS & FrontOuterLayers()
Definition: padstack.h:312
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
Definition: padstack.cpp:614
int RoundRectRadius(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1134
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition: padstack.cpp:1080
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1220
wxString m_customName
! An override for the IPC-7351 padstack name
Definition: padstack.h:460
DRILL_PROPS m_drill
! The primary drill parameters, which also define the start and end layers for through-hole vias and ...
Definition: padstack.h:486
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
Definition: padstack.cpp:879
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1289
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1128
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
Definition: padstack.h:475
void ClearPrimitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1332
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition: padstack.h:307
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
Definition: padstack.cpp:1338
void SetMode(MODE aMode)
Definition: padstack.cpp:830
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1246
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1207
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1193
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1157
const LSET & LayerSet() const
Definition: padstack.h:274
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
Definition: padstack.cpp:931
PADSTACK & operator=(const PADSTACK &aOther)
Definition: padstack.cpp:71
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1053
EDA_ANGLE DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1270
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1110
DRILL_PROPS m_secondaryDrill
! Secondary drill, used to define back-drilling
Definition: padstack.h:489
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1086
MASK_LAYER_PROPS m_backMaskProps
! The overrides applied to back outer technical layers
Definition: padstack.h:473
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1027
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1281
void FlipLayers(int aCopperLayerCount)
Flips the padstack layers in the case that the pad's parent footprint is flipped to the other side of...
Definition: padstack.cpp:717
CUSTOM_SHAPE_ZONE_MODE m_customShapeInZoneMode
How to build the custom shape in zone, to create the clearance area: CUSTOM_SHAPE_ZONE_MODE::OUTLINE ...
Definition: padstack.h:482
bool unpackCopperLayer(const kiapi::board::types::PadStackLayer &aProto)
Definition: padstack.cpp:160
PAD_DRILL_SHAPE DrillShape() const
Definition: padstack.cpp:1074
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1175
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1141
LSET RelevantShapeLayers(const PADSTACK &aOther) const
Returns the set of layers that must be considered if checking one padstack against another.
Definition: padstack.cpp:1007
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1258
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1047
DRILL_PROPS & Drill()
Definition: padstack.h:300
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1104
BOARD_ITEM * m_parent
! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes
Definition: padstack.h:451
PADSTACK(BOARD_ITEM *aParent)
Definition: padstack.cpp:34
std::vector< PCB_LAYER_ID > UniqueLayers() const
Definition: padstack.cpp:906
LSET m_layerSet
! The board layers that this padstack is active on
Definition: padstack.h:457
std::unordered_map< PCB_LAYER_ID, COPPER_LAYER_PROPS > m_copperProps
! The properties applied to copper layers if they aren't overridden
Definition: padstack.h:467
CUSTOM_SHAPE_ZONE_MODE
Definition: padstack.h:158
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
Definition: padstack.cpp:535
PCB_LAYER_ID EndLayer() const
Definition: padstack.cpp:711
int & ChamferPositions(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1163
MASK_LAYER_PROPS m_frontMaskProps
! The overrides applied to front outer technical layers
Definition: padstack.h:470
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1068
MODE
! Copper geometry mode: controls how many unique copper layer shapes this padstack has
Definition: padstack.h:137
@ NORMAL
Shape is the same on all layers.
@ CUSTOM
Shapes can be defined on arbitrary layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
void AppendPrimitives(const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList, PCB_LAYER_ID aLayer)
Appends a copy of each shape in the given list to this padstack's custom shape list.
Definition: padstack.cpp:1314
double RoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1122
UNCONNECTED_LAYER_MODE
! Whether or not to remove the copper shape for unconnected layers
Definition: padstack.h:151
PCB_LAYER_ID StartLayer() const
Definition: padstack.cpp:705
MODE Mode() const
Definition: padstack.h:287
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1098
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:315
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: padstack.cpp:427
wxString Name() const
! Returns the name of this padstack in IPC-7351 format
Definition: padstack.cpp:698
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1059
double ChamferRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1151
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
EDA_ANGLE GetOrientation() const
Definition: padstack.h:293
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition: padstack.h:147
void SetLayerSet(const LSET &aSet)
Definition: padstack.h:276
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1234
MODE m_mode
! The copper layer variation mode this padstack is in
Definition: padstack.h:454
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
Definition: padstack.h:463
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1295
Definition: pad.h:54
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition: eda_angle.h:402
@ LAYER_PAD_FR_NETNAMES
Additional netnames layers (not associated with a PCB layer).
Definition: layer_ids.h:163
@ LAYER_PAD_BK_NETNAMES
Definition: layer_ids.h:164
@ LAYER_PAD_NETNAMES
Definition: layer_ids.h:165
@ LAYER_VIA_NETNAMES
Definition: layer_ids.h:166
bool IsPadCopperLayer(int aLayer)
Definition: layer_ids.h:783
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:682
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:705
#define MAX_CU_LAYERS
Definition: layer_ids.h:140
bool IsClearanceLayer(int aLayer)
Definition: layer_ids.h:795
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:581
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
Definition: layer_ids.h:294
@ LAYER_VIA_HOLEWALLS
Definition: layer_ids.h:260
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition: layer_ids.h:254
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition: layer_ids.h:233
@ LAYER_VIA_COPPER_START
Virtual layers for via copper on a given copper layer.
Definition: layer_ids.h:298
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
Definition: layer_ids.h:302
@ LAYER_VIA_HOLES
Draw via holes (pad holes do not use this layer).
Definition: layer_ids.h:236
@ LAYER_PAD_HOLEWALLS
Definition: layer_ids.h:259
bool IsViaCopperLayer(int aLayer)
Definition: layer_ids.h:789
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ In30_Cu
Definition: layer_ids.h:95
@ B_Cu
Definition: layer_ids.h:65
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ In1_Cu
Definition: layer_ids.h:66
@ F_Cu
Definition: layer_ids.h:64
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:699
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition: api_utils.cpp:84
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition: api_utils.cpp:77
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition: padstack.h:69
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: padstack.h:52
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition: property.h:780
The features of a padstack that can vary between copper layers All parameters are optional; leaving t...
Definition: padstack.h:207
std::optional< ZONE_CONNECTION > zone_connection
Definition: padstack.h:209
std::optional< int > thermal_spoke_width
Definition: padstack.h:210
std::vector< std::shared_ptr< PCB_SHAPE > > custom_shapes
Definition: padstack.h:219
bool operator==(const COPPER_LAYER_PROPS &aOther) const
Definition: padstack.cpp:778
std::optional< EDA_ANGLE > thermal_spoke_angle
Definition: padstack.h:211
std::optional< int > clearance
Definition: padstack.h:213
std::optional< int > thermal_gap
Definition: padstack.h:212
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition: padstack.h:239
PCB_LAYER_ID start
Definition: padstack.h:242
PCB_LAYER_ID end
Definition: padstack.h:243
bool operator==(const DRILL_PROPS &aOther) const
Definition: padstack.cpp:823
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition: padstack.h:240
PAD_DRILL_SHAPE shape
Definition: padstack.h:241
! The features of a padstack that can vary on outer layers.
Definition: padstack.h:227
bool operator==(const MASK_LAYER_PROPS &aOther) const
Definition: padstack.cpp:813
std::optional< int > solder_mask_margin
Definition: padstack.h:228
std::optional< int > solder_paste_margin
Definition: padstack.h:229
std::optional< double > solder_paste_margin_ratio
Definition: padstack.h:230
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition: padstack.h:231
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
Definition: padstack.h:232
! The set of properties that define a pad's shape on a given layer
Definition: padstack.h:165
VECTOR2I trapezoid_delta_size
Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the other.
Definition: padstack.h:188
double round_rect_corner_radius
Definition: padstack.h:179
VECTOR2I offset
Offset of the shape center from the pad center.
Definition: padstack.h:177
bool operator==(const SHAPE_PROPS &aOther) const
Definition: padstack.cpp:768
VECTOR2I size
Size of the shape, or of the anchor pad for custom shape pads.
Definition: padstack.h:168
double chamfered_rect_ratio
Size of chamfer: ratio of smallest of X,Y size.
Definition: padstack.h:181
double round_rect_radius_ratio
Definition: padstack.h:180
PAD_SHAPE shape
Shape of the pad.
Definition: padstack.h:166
PAD_SHAPE anchor_shape
Shape of the anchor when shape == PAD_SHAPE::CUSTOM.
Definition: padstack.h:167