KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <jon@craftyjon.com>
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 auto unpackOptional = []<typename ProtoEnum>( const ProtoEnum& aProto,
217 std::optional<bool>& aDest, ProtoEnum aTrueValue,
218 ProtoEnum aFalseValue )
219 {
220 if( aProto == aTrueValue )
221 aDest = true;
222 else if( aProto == aFalseValue )
223 aDest = false;
224 else
225 aDest = std::nullopt;
226 };
227
228 if( !aContainer.UnpackTo( &padstack ) )
229 return false;
230
231 m_mode = FromProtoEnum<MODE>( padstack.type() );
232 SetLayerSet( kiapi::board::UnpackLayerSet( padstack.layers() ) );
233 m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T );
234
235 Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() );
236 Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
237 Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
238 unpackOptional( padstack.drill().capped(), Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
239 unpackOptional( padstack.drill().filled(), Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
240
241 for( const PadStackLayer& layer : padstack.copper_layers() )
242 {
243 if( !unpackCopperLayer( layer ) )
244 return false;
245 }
246
247 CopperLayer( ALL_LAYERS ).thermal_gap = std::nullopt;
249
250 if( padstack.has_zone_settings() )
251 {
253 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
254
255 if( padstack.zone_settings().has_thermal_spokes() )
256 {
257 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
258
259 if( thermals.has_gap() )
260 CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap().value_nm();
261
262 if( thermals.has_width() )
263 CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width().value_nm();
264
265 SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu );
266 }
267 }
268 else
269 {
270 CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED;
272 }
273
275 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
276
277 unpackOptional( padstack.front_outer_layers().solder_mask_mode(),
278 FrontOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
279
280 unpackOptional( padstack.back_outer_layers().solder_mask_mode(),
281 BackOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
282
283 unpackOptional( padstack.front_outer_layers().covering_mode(), FrontOuterLayers().has_covering,
284 VCM_COVERED, VCM_UNCOVERED );
285
286 unpackOptional( padstack.back_outer_layers().covering_mode(), BackOuterLayers().has_covering,
287 VCM_COVERED, VCM_UNCOVERED );
288
289 unpackOptional( padstack.front_outer_layers().plugging_mode(), FrontOuterLayers().has_plugging,
290 VPM_PLUGGED, VPM_UNPLUGGED );
291
292 unpackOptional( padstack.back_outer_layers().plugging_mode(), BackOuterLayers().has_plugging,
293 VPM_PLUGGED, VPM_UNPLUGGED );
294
295 unpackOptional( padstack.front_outer_layers().solder_paste_mode(),
296 FrontOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
297
298 unpackOptional( padstack.back_outer_layers().solder_paste_mode(),
299 BackOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
300
301 if( padstack.front_outer_layers().has_solder_mask_settings()
302 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
303 {
305 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
306 }
307 else
308 {
309 FrontOuterLayers().solder_mask_margin = std::nullopt;
310 }
311
312 if( padstack.back_outer_layers().has_solder_mask_settings()
313 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
314 {
316 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
317 }
318 else
319 {
320 BackOuterLayers().solder_mask_margin = std::nullopt;
321 }
322
323 if( padstack.front_outer_layers().has_solder_paste_settings()
324 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
325 {
327 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
328 }
329 else
330 {
331 FrontOuterLayers().solder_paste_margin = std::nullopt;
332 }
333
334 if( padstack.back_outer_layers().has_solder_paste_settings()
335 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
336 {
338 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
339 }
340 else
341 {
342 BackOuterLayers().solder_paste_margin = std::nullopt;
343 }
344
345 if( padstack.front_outer_layers().has_solder_paste_settings()
346 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
347 {
349 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
350 }
351 else
352 {
354 }
355
356 if( padstack.back_outer_layers().has_solder_paste_settings()
357 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
358 {
360 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
361 }
362 else
363 {
365 }
366
367
368 return true;
369}
370
371
372void PADSTACK::packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const
373{
374 using namespace kiapi::board::types;
375
376 PadStackLayer* stackLayer = aProto.add_copper_layers();
377
378 stackLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
379 kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( aLayer ) );
380 kiapi::common::PackVector2( *stackLayer->mutable_offset(), Offset( aLayer ) );
381
382 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>( Shape( aLayer ) ) );
383
384 stackLayer->set_custom_anchor_shape(
385 ToProtoEnum<PAD_SHAPE, PadStackShape>( AnchorShape( aLayer ) ) );
386
387 stackLayer->set_chamfer_ratio( CopperLayer( aLayer ).shape.chamfered_rect_ratio );
388 stackLayer->set_corner_rounding_ratio( CopperLayer( aLayer ).shape.round_rect_radius_ratio );
389
390 if( Shape( aLayer ) == PAD_SHAPE::TRAPEZOID )
391 {
392 kiapi::common::PackVector2( *stackLayer->mutable_trapezoid_delta(),
393 TrapezoidDeltaSize( aLayer ) );
394 }
395
396 google::protobuf::Any a;
397
398 for( const std::shared_ptr<PCB_SHAPE>& shape : Primitives( aLayer ) )
399 {
400 shape->Serialize( a );
401 BoardGraphicShape* s = stackLayer->add_custom_shapes();
402 a.UnpackTo( s );
403 }
404
405 const int& corners = CopperLayer( aLayer ).shape.chamfered_rect_positions;
406 stackLayer->mutable_chamfered_corners()->set_top_left( corners & RECT_CHAMFER_TOP_LEFT );
407 stackLayer->mutable_chamfered_corners()->set_top_right( corners & RECT_CHAMFER_TOP_RIGHT );
408 stackLayer->mutable_chamfered_corners()->set_bottom_left( corners & RECT_CHAMFER_BOTTOM_LEFT );
409 stackLayer->mutable_chamfered_corners()->set_bottom_right( corners & RECT_CHAMFER_BOTTOM_RIGHT );
410}
411
412
413void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
414{
415 using namespace kiapi::board::types;
416 PadStack padstack;
417
418 auto packOptional = []<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
419 ProtoEnum aFalseVal,
420 ProtoEnum aNullVal ) -> ProtoEnum
421 {
422 if( aVal.has_value() )
423 return *aVal ? aTrueVal : aFalseVal;
424
425 return aNullVal;
426 };
427
428 padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
429 kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet );
430 padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
431
432 DrillProperties* drill = padstack.mutable_drill();
433 drill->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( StartLayer() ) );
434 drill->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( EndLayer() ) );
435 drill->set_filled(
436 packOptional( Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED, VDFM_FROM_DESIGN_RULES ) );
437
438 drill->set_capped(
439 packOptional( Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED, VDCM_FROM_DESIGN_RULES ) );
440
441 kiapi::common::PackVector2( *drill->mutable_diameter(), Drill().size );
442
443 ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer )
444 {
445 packCopperLayer( aLayer, padstack );
446 } );
447
448 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
449 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
450
451 if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() )
452 {
453 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
454 *CopperLayer( ALL_LAYERS ).zone_connection ) );
455 }
456
457 if( std::optional<int> width = CopperLayer( ALL_LAYERS ).thermal_spoke_width )
458 thermalSettings->mutable_width()->set_value_nm( *width );
459
460 if( std::optional<int> gap = CopperLayer( ALL_LAYERS ).thermal_gap )
461 thermalSettings->mutable_gap()->set_value_nm( *gap );
462
463 thermalSettings->mutable_angle()->set_value_degrees( ThermalSpokeAngle( F_Cu ).AsDegrees() );
464
465 padstack.set_unconnected_layer_removal(
466 ToProtoEnum<UNCONNECTED_LAYER_MODE, UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
467
468 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
469 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
470
471 frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask, SMM_MASKED,
472 SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
473
474 backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask, SMM_MASKED,
475 SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
476
477 frontOuter->set_plugging_mode( packOptional( FrontOuterLayers().has_plugging, VPM_PLUGGED,
478 VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
479
480 backOuter->set_plugging_mode( packOptional( BackOuterLayers().has_plugging, VPM_PLUGGED,
481 VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
482
483 frontOuter->set_covering_mode( packOptional( FrontOuterLayers().has_covering, VCM_COVERED,
484 VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
485
486 backOuter->set_covering_mode( packOptional( BackOuterLayers().has_covering, VCM_COVERED,
487 VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
488
489 frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste, SPM_PASTE,
490 SPM_NO_PASTE, SPM_FROM_DESIGN_RULES ) );
491
492 backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste, SPM_PASTE,
493 SPM_NO_PASTE, 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
1349std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const
1350{
1351 if( IsFrontLayer( aSide ) )
1353
1354 if( IsBackLayer( aSide ) )
1356
1357 wxCHECK_MSG( false, std::nullopt, "IsCovered expects a front or back layer" );
1358}
1359
1360std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const
1361{
1362 if( IsFrontLayer( aSide ) )
1364
1365 if( IsBackLayer( aSide ) )
1367
1368 wxCHECK_MSG( false, std::nullopt, "IsPlugged expects a front or back layer" );
1369}
1370
1371std::optional<bool> PADSTACK::IsCapped() const
1372{
1373 return m_drill.is_capped;
1374}
1375
1376std::optional<bool> PADSTACK::IsFilled() const
1377{
1378 return m_drill.is_filled;
1379}
1380
1381
@ 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:261
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:78
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
Definition: board_item.cpp:135
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Definition: board_item.cpp:124
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:110
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:572
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
std::optional< bool > IsFilled() const
Definition: padstack.cpp:1376
bool operator==(const PADSTACK &aOther) const
Definition: padstack.cpp:115
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const
Definition: padstack.h:340
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:372
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:318
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:474
DRILL_PROPS m_drill
! The primary drill parameters, which also define the start and end layers for through-hole vias and ...
Definition: padstack.h:500
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:489
void ClearPrimitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1332
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition: padstack.h:313
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:280
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:503
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:487
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:496
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
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
Definition: padstack.cpp:1360
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:306
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:465
PADSTACK(BOARD_ITEM *aParent)
Definition: padstack.cpp:34
std::optional< bool > IsCapped() const
Definition: padstack.cpp:1371
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:471
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:481
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
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
Definition: padstack.cpp:1349
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:484
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:293
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1098
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:321
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: padstack.cpp:413
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:299
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:282
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:468
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
Definition: padstack.h:477
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:200
@ LAYER_PAD_BK_NETNAMES
Definition: layer_ids.h:201
@ LAYER_PAD_NETNAMES
Definition: layer_ids.h:202
@ LAYER_VIA_NETNAMES
Definition: layer_ids.h:203
bool IsPadCopperLayer(int aLayer)
Definition: layer_ids.h:866
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:765
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:788
#define MAX_CU_LAYERS
Definition: layer_ids.h:176
bool IsClearanceLayer(int aLayer)
Definition: layer_ids.h:878
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
Definition: layer_ids.h:331
@ LAYER_VIA_HOLEWALLS
Definition: layer_ids.h:297
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition: layer_ids.h:291
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition: layer_ids.h:270
@ LAYER_VIA_COPPER_START
Virtual layers for via copper on a given copper layer.
Definition: layer_ids.h:335
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
Definition: layer_ids.h:339
@ LAYER_VIA_HOLES
Draw via holes (pad holes do not use this layer).
Definition: layer_ids.h:273
@ LAYER_PAD_HOLEWALLS
Definition: layer_ids.h:296
bool IsViaCopperLayer(int aLayer)
Definition: layer_ids.h:872
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:723
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:242
PCB_LAYER_ID start
Definition: padstack.h:245
PCB_LAYER_ID end
Definition: padstack.h:246
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:243
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition: padstack.h:249
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition: padstack.h:248
PAD_DRILL_SHAPE shape
Definition: padstack.h:244
! 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< bool > has_covering
True if the pad on this side should have covering.
Definition: padstack.h:234
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:232
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
Definition: padstack.h:233
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition: padstack.h:235
! 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
VECTOR2I end