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 (C) 2024 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 <magic_enum.hpp>
29#include <pad.h>
30#include <pcb_shape.h>
31
32
34 m_parent( aParent ),
35 m_mode( MODE::NORMAL ),
36 m_orientation( ANGLE_0 ),
37 m_unconnectedLayerMode( UNCONNECTED_LAYER_MODE::KEEP_ALL ),
38 m_customShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE::OUTLINE )
39{
41 m_copperProps[PADSTACK::ALL_LAYERS].zone_connection = ZONE_CONNECTION::INHERITED;
42 m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_width = 0;
43 m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_angle = ANGLE_45;
44 m_copperProps[PADSTACK::ALL_LAYERS].thermal_gap = 0;
45
46 m_drill.shape = PAD_DRILL_SHAPE::CIRCLE;
49
50 m_secondaryDrill.shape = PAD_DRILL_SHAPE::UNDEFINED;
53}
54
55
57{
58 *this = aOther;
59}
60
61
63{
64 m_mode = aOther.m_mode;
65 m_layerSet = aOther.m_layerSet;
73 m_drill = aOther.m_drill;
75
76 // Data consistency enforcement logic that used to live in the pad properties dialog
77 // TODO(JE) Should these move to individual property setters, so that they are always
78 // enforced even through the properties panel and API?
79
81 [this]( PCB_LAYER_ID aLayer )
82 {
83 PAD_SHAPE shape = Shape( aLayer );
84
85 // Make sure leftover primitives don't stick around
86 if( shape != PAD_SHAPE::CUSTOM )
87 ClearPrimitives( aLayer );
88
89 // rounded rect pads with radius ratio = 0 are in fact rect pads.
90 // So set the right shape (and perhaps issues with a radius = 0)
91 if( shape == PAD_SHAPE::ROUNDRECT && RoundRectRadiusRatio( aLayer ) == 0.0 )
92 SetShape( PAD_SHAPE::RECTANGLE, aLayer );
93 } );
94
95 return *this;
96}
97
98
99bool PADSTACK::operator==( const PADSTACK& aOther ) const
100{
101 if( m_mode != aOther.m_mode )
102 return false;
103
104 if( m_layerSet != aOther.m_layerSet )
105 return false;
106
107 if( m_customName != aOther.m_customName )
108 return false;
109
110 if( m_orientation != aOther.m_orientation )
111 return false;
112
113 if( m_frontMaskProps != aOther.m_frontMaskProps )
114 return false;
115
116 if( m_backMaskProps != aOther.m_backMaskProps )
117 return false;
118
120 return false;
121
123 return false;
124
125 if( m_drill != aOther.m_drill )
126 return false;
127
128 if( m_secondaryDrill != aOther.m_secondaryDrill )
129 return false;
130
131 bool copperMatches = true;
132
134 [&]( PCB_LAYER_ID aLayer )
135 {
136 if( CopperLayer( aLayer ) != aOther.CopperLayer( aLayer ) )
137 copperMatches = false;
138 } );
139
140 return copperMatches;
141}
142
143
144bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
145{
146 using namespace kiapi::board::types;
147 PadStack padstack;
148
149 if( !aContainer.UnpackTo( &padstack ) )
150 return false;
151
152 m_mode = FromProtoEnum<MODE>( padstack.type() );
153
154 // TODO
156
157 m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T );
158
159 Drill().size = kiapi::common::UnpackVector2( padstack.drill_diameter() );
160 Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.start_layer() );
161 Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.end_layer() );
162
163 // We don't yet support complex padstacks
164 // TODO(JE) Rework for full padstacks
165 if( padstack.layers_size() == 1 )
166 {
167 const PadStackLayer& layer = padstack.layers( 0 );
169 SetLayerSet( kiapi::board::UnpackLayerSet( layer.layers() ) );
170 SetShape( FromProtoEnum<PAD_SHAPE>( layer.shape() ), F_Cu );
171 SetAnchorShape( FromProtoEnum<PAD_SHAPE>( layer.custom_anchor_shape() ), F_Cu );
172
174 props.chamfered_rect_ratio = layer.chamfer_ratio();
175 props.round_rect_radius_ratio = layer.corner_rounding_ratio();
176
177 if( layer.chamfered_corners().top_left() )
179
180 if( layer.chamfered_corners().top_right() )
182
183 if( layer.chamfered_corners().bottom_left() )
185
186 if( layer.chamfered_corners().bottom_right() )
188
190 google::protobuf::Any a;
191
192 for( const GraphicShape& shapeProto : layer.custom_shapes() )
193 {
194 a.PackFrom( shapeProto );
195 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_parent );
196
197 if( shape->Deserialize( a ) )
198 AddPrimitive( shape.release(), F_Cu );
199 }
200
201 if( layer.has_zone_settings() )
202 {
204 FromProtoEnum<ZONE_CONNECTION>( layer.zone_settings().zone_connection() );
205
206 if( layer.zone_settings().has_thermal_spokes() )
207 {
208 const ThermalSpokeSettings& thermals = layer.zone_settings().thermal_spokes();
209
210 CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap();
211 CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width();
212 SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu );
213 }
214 }
215 else
216 {
217 CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED;
221 }
222 }
223
225 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
226
227 auto unpackMask =
228 []( const SolderMaskMode& aProto, std::optional<bool>& aDest )
229 {
230 switch( aProto )
231 {
232 case kiapi::board::types::SMM_MASKED:
233 aDest = true;
234 break;
235
236 case kiapi::board::types::SMM_UNMASKED:
237 aDest = false;
238 break;
239
240 default:
241 case kiapi::board::types::SMM_FROM_DESIGN_RULES:
242 aDest = std::nullopt;
243 break;
244 }
245 };
246
247 unpackMask( padstack.front_outer_layers().solder_mask_mode(),
248 FrontOuterLayers().has_solder_mask );
249
250 unpackMask( padstack.back_outer_layers().solder_mask_mode(),
251 BackOuterLayers().has_solder_mask );
252
253 auto unpackPaste =
254 []( const SolderPasteMode& aProto, std::optional<bool>& aDest )
255 {
256 switch( aProto )
257 {
258 case kiapi::board::types::SPM_PASTE:
259 aDest = true;
260 break;
261
262 case kiapi::board::types::SPM_NO_PASTE:
263 aDest = false;
264 break;
265
266 default:
267 case kiapi::board::types::SPM_FROM_DESIGN_RULES:
268 aDest = std::nullopt;
269 break;
270 }
271 };
272
273 unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
274 FrontOuterLayers().has_solder_paste );
275
276 unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
277 BackOuterLayers().has_solder_paste );
278
279 if( padstack.front_outer_layers().has_solder_mask_settings()
280 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
281 {
283 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
284 }
285 else
286 {
287 FrontOuterLayers().solder_mask_margin = std::nullopt;
288 }
289
290 if( padstack.back_outer_layers().has_solder_mask_settings()
291 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
292 {
294 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
295 }
296 else
297 {
298 BackOuterLayers().solder_mask_margin = std::nullopt;
299 }
300
301 if( padstack.front_outer_layers().has_solder_paste_settings()
302 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
303 {
305 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
306 }
307 else
308 {
309 FrontOuterLayers().solder_paste_margin = std::nullopt;
310 }
311
312 if( padstack.back_outer_layers().has_solder_paste_settings()
313 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
314 {
316 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
317 }
318 else
319 {
320 BackOuterLayers().solder_paste_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_ratio() )
325 {
327 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
328 }
329 else
330 {
332 }
333
334 if( padstack.back_outer_layers().has_solder_paste_settings()
335 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
336 {
338 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
339 }
340 else
341 {
343 }
344
345
346 return true;
347}
348
349
350void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
351{
352 using namespace kiapi::board::types;
353 PadStack padstack;
354
355 padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
356 padstack.set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( StartLayer() ) );
357 padstack.set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( EndLayer() ) );
358 kiapi::common::PackVector2( *padstack.mutable_drill_diameter(), Drill().size );
359 padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
360
361 // TODO(JE) Rework for full padstacks
362 PadStackLayer* stackLayer = padstack.add_layers();
363 kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), LayerSet() );
364 kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( PADSTACK::ALL_LAYERS ) );
365 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>( Shape( ALL_LAYERS ) ) );
366 stackLayer->set_custom_anchor_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>( AnchorShape( ALL_LAYERS ) ) );
367 stackLayer->set_chamfer_ratio( CopperLayer( ALL_LAYERS ).shape.chamfered_rect_ratio );
368 stackLayer->set_corner_rounding_ratio( CopperLayer( ALL_LAYERS ).shape.round_rect_radius_ratio );
369
370 google::protobuf::Any a;
371
372 // TODO(JE) Rework for full padstacks
373 for( const std::shared_ptr<PCB_SHAPE>& shape : Primitives( ALL_LAYERS ) )
374 {
375 shape->Serialize( a );
376 GraphicShape* s = stackLayer->add_custom_shapes();
377 a.UnpackTo( s );
378 }
379
381 stackLayer->mutable_chamfered_corners()->set_top_left( corners & RECT_CHAMFER_TOP_LEFT );
382 stackLayer->mutable_chamfered_corners()->set_top_right( corners & RECT_CHAMFER_TOP_RIGHT );
383 stackLayer->mutable_chamfered_corners()->set_bottom_left( corners & RECT_CHAMFER_BOTTOM_LEFT );
384 stackLayer->mutable_chamfered_corners()->set_bottom_right( corners & RECT_CHAMFER_BOTTOM_RIGHT );
385
386 ZoneConnectionSettings* zoneSettings = stackLayer->mutable_zone_settings();
387 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
388
389 if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() )
390 {
391 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
392 *CopperLayer( ALL_LAYERS ).zone_connection ) );
393 }
394
395 thermalSettings->set_width( CopperLayer( ALL_LAYERS ).thermal_spoke_width.value_or( 0 ) );
396 thermalSettings->set_gap( CopperLayer( ALL_LAYERS ).thermal_gap.value_or( 0 ) );
397 thermalSettings->mutable_angle()->set_value_degrees( ThermalSpokeAngle( F_Cu ).AsDegrees() );
398
399 padstack.set_unconnected_layer_removal(
400 ToProtoEnum<UNCONNECTED_LAYER_MODE, UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
401
402 auto packOptional =
403 []<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
404 ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
405 {
406 if( aVal.has_value() )
407 return *aVal ? aTrueVal : aFalseVal;
408
409 return aNullVal;
410 };
411
412 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
413 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
414
415 frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask,
416 SMM_MASKED, SMM_UNMASKED,
417 SMM_FROM_DESIGN_RULES ) );
418
419 backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask,
420 SMM_MASKED, SMM_UNMASKED,
421 SMM_FROM_DESIGN_RULES ) );
422
423 frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste,
424 SPM_PASTE, SPM_NO_PASTE,
425 SPM_FROM_DESIGN_RULES ) );
426
427 backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste,
428 SPM_PASTE, SPM_NO_PASTE,
429 SPM_FROM_DESIGN_RULES ) );
430
431 if( FrontOuterLayers().solder_mask_margin.has_value() )
432 {
433 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
435 }
436
437 if( BackOuterLayers().solder_mask_margin.has_value() )
438 {
439 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
441 }
442
443 if( FrontOuterLayers().solder_paste_margin.has_value() )
444 {
445 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
447 }
448
449 if( BackOuterLayers().solder_paste_margin.has_value() )
450 {
451 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
453 }
454
456 {
457 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
459 }
460
462 {
463 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
465 }
466
467 aContainer.PackFrom( padstack );
468}
469
470
471int PADSTACK::Compare( const PADSTACK* aPadstackRef, const PADSTACK* aPadstackCmp )
472{
473 int diff;
474
475 auto compareCopperProps =
476 [&]( PCB_LAYER_ID aLayer )
477 {
478 if( ( diff = static_cast<int>( aPadstackRef->Shape( aLayer ) ) -
479 static_cast<int>( aPadstackCmp->Shape( aLayer ) ) ) != 0 )
480 return diff;
481
482 if( ( diff = aPadstackRef->Size( aLayer ).x - aPadstackCmp->Size( aLayer ).x ) != 0 )
483 return diff;
484
485 if( ( diff = aPadstackRef->Size( aLayer ).y - aPadstackCmp->Size( aLayer ).y ) != 0 )
486 return diff;
487
488 if( ( diff = aPadstackRef->Offset( aLayer ).x
489 - aPadstackCmp->Offset( aLayer ).x ) != 0 )
490 return diff;
491
492 if( ( diff = aPadstackRef->Offset( aLayer ).y
493 - aPadstackCmp->Offset( aLayer ).y ) != 0 )
494 return diff;
495
496 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).x
497 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).x )
498 != 0 )
499 {
500 return diff;
501 }
502
503 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).y
504 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).y )
505 != 0 )
506 {
507 return diff;
508 }
509
510 if( ( diff = aPadstackRef->RoundRectRadiusRatio( aLayer )
511 - aPadstackCmp->RoundRectRadiusRatio( aLayer ) )
512 != 0 )
513 {
514 return diff;
515 }
516
517 if( ( diff = aPadstackRef->ChamferPositions( aLayer )
518 - aPadstackCmp->ChamferPositions( aLayer ) ) != 0 )
519 return diff;
520
521 if( ( diff = aPadstackRef->ChamferRatio( aLayer )
522 - aPadstackCmp->ChamferRatio( aLayer ) ) != 0 )
523 return diff;
524
525 if( ( diff = static_cast<int>( aPadstackRef->Primitives( aLayer ).size() ) -
526 static_cast<int>( aPadstackCmp->Primitives( aLayer ).size() ) ) != 0 )
527 return diff;
528
529 // @todo: Compare custom pad primitives for pads that have the same number of primitives
530 // here. Currently there is no compare function for PCB_SHAPE objects.
531 return 0;
532 };
533
534 aPadstackRef->ForEachUniqueLayer( compareCopperProps );
535
536 if( ( diff = static_cast<int>( aPadstackRef->DrillShape() ) -
537 static_cast<int>( aPadstackCmp->DrillShape() ) ) != 0 )
538 return diff;
539
540 if( ( diff = aPadstackRef->Drill().size.x - aPadstackCmp->Drill().size.x ) != 0 )
541 return diff;
542
543 if( ( diff = aPadstackRef->Drill().size.y - aPadstackCmp->Drill().size.y ) != 0 )
544 return diff;
545
546 return aPadstackRef->LayerSet().compare( aPadstackCmp->LayerSet() );
547}
548
549
550double PADSTACK::Similarity( const PADSTACK& aOther ) const
551{
552 double similarity = 1.0;
553
555 [&]( PCB_LAYER_ID aLayer )
556 {
557 if( Shape( aLayer ) != aOther.Shape( aLayer ) )
558 similarity *= 0.9;
559
560 if( Size( aLayer ) != aOther.Size( aLayer ) )
561 similarity *= 0.9;
562
563 if( Offset( aLayer ) != aOther.Offset( aLayer ) )
564 similarity *= 0.9;
565
566 if( RoundRectRadiusRatio( aLayer ) != aOther.RoundRectRadiusRatio( aLayer ) )
567 similarity *= 0.9;
568
569 if( ChamferRatio( aLayer ) != aOther.ChamferRatio( aLayer ) )
570 similarity *= 0.9;
571
572 if( ChamferPositions( aLayer ) != aOther.ChamferPositions( aLayer ) )
573 similarity *= 0.9;
574
575 if( Primitives( aLayer ).size() != aOther.Primitives( aLayer ).size() )
576 similarity *= 0.9;
577
578 if( AnchorShape( aLayer ) != aOther.AnchorShape( aLayer ) )
579 similarity *= 0.9;
580 } );
581
582 if( Drill() != aOther.Drill() )
583 similarity *= 0.9;
584
585 if( DrillShape() != aOther.DrillShape() )
586 similarity *= 0.9;
587
588 if( GetOrientation() != aOther.GetOrientation() )
589 similarity *= 0.9;
590
591 if( ZoneConnection() != aOther.ZoneConnection() )
592 similarity *= 0.9;
593
594 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
595 similarity *= 0.9;
596
597 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
598 similarity *= 0.9;
599
600 if( ThermalGap() != aOther.ThermalGap() )
601 similarity *= 0.9;
602
604 similarity *= 0.9;
605
606 if( Clearance() != aOther.Clearance() )
607 similarity *= 0.9;
608
609 if( SolderMaskMargin() != aOther.SolderMaskMargin() )
610 similarity *= 0.9;
611
612 if( SolderPasteMargin() != aOther.SolderPasteMargin() )
613 similarity *= 0.9;
614
616 similarity *= 0.9;
617
618 if( ThermalGap() != aOther.ThermalGap() )
619 similarity *= 0.9;
620
621 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
622 similarity *= 0.9;
623
624 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
625 similarity *= 0.9;
626
627 if( LayerSet() != aOther.LayerSet() )
628 similarity *= 0.9;
629
630 return similarity;
631}
632
633
634wxString PADSTACK::Name() const
635{
636 // TODO
637 return wxEmptyString;
638}
639
640
642{
643 return m_drill.start;
644}
645
646
648{
649 return m_drill.end;
650}
651
652
654 shape( PAD_SHAPE::CIRCLE ),
655 anchor_shape( PAD_SHAPE::CIRCLE ),
656 round_rect_corner_radius( 0 ),
657 round_rect_radius_ratio( 0.25 ),
658 chamfered_rect_ratio( 0.2 ),
659 chamfered_rect_positions( RECT_NO_CHAMFER )
660{
661}
662
663
665{
666 return shape == aOther.shape && offset == aOther.offset
667 && round_rect_corner_radius == aOther.round_rect_corner_radius
668 && round_rect_radius_ratio == aOther.round_rect_radius_ratio
669 && chamfered_rect_ratio == aOther.chamfered_rect_ratio
670 && chamfered_rect_positions == aOther.chamfered_rect_positions;
671}
672
673
675{
676 if( shape != aOther.shape )
677 return false;
678
679 if( zone_connection != aOther.zone_connection )
680 return false;
681
682 if( thermal_spoke_width != aOther.thermal_spoke_width )
683 return false;
684
685 if( thermal_spoke_angle != aOther.thermal_spoke_angle )
686 return false;
687
688 if( thermal_gap != aOther.thermal_gap )
689 return false;
690
691 if( custom_shapes.size() != aOther.custom_shapes.size() )
692 return false;
693
694 if( !std::equal( custom_shapes.begin(), custom_shapes.end(),
695 aOther.custom_shapes.begin(), aOther.custom_shapes.end(),
696 []( const std::shared_ptr<PCB_SHAPE>& aFirst,
697 const std::shared_ptr<PCB_SHAPE>& aSecond )
698 {
699 return *aFirst == *aSecond;
700 } ) )
701 {
702 return false;
703 }
704
705 return true;
706}
707
708
710{
711 return solder_mask_margin == aOther.solder_mask_margin
712 && solder_paste_margin == aOther.solder_paste_margin
713 && solder_paste_margin_ratio == aOther.solder_paste_margin_ratio
714 && has_solder_mask == aOther.has_solder_mask
715 && has_solder_paste == aOther.has_solder_paste;
716}
717
718
720{
721 return size == aOther.size && shape == aOther.shape
722 && start == aOther.start && end == aOther.end;
723}
724
725
727{
728 if( m_mode == aMode )
729 return;
730
731 m_mode = aMode;
732
733 switch( m_mode )
734 {
735 case MODE::NORMAL:
736 std::erase_if( m_copperProps,
737 []( const auto& aEntry )
738 {
739 const auto& [key, value] = aEntry;
740 return key != ALL_LAYERS;
741 } );
742 break;
743
747 break;
748
749 case MODE::CUSTOM:
752
753 break;
754 }
755
756 // Changing mode invalidates cached shapes
757 // TODO(JE) clean this up -- maybe PADSTACK should own shape caches
758 if( PAD* parentPad = dynamic_cast<PAD*>( m_parent ) )
759 parentPad->SetDirty();
760}
761
762
763void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& aMethod ) const
764{
765 switch( Mode() )
766 {
767 case MODE::NORMAL:
768 aMethod( F_Cu );
769 break;
770
772 aMethod( F_Cu );
773 aMethod( INNER_LAYERS );
774 aMethod( B_Cu );
775 break;
776
777 case MODE::CUSTOM:
779 aMethod( layer );
780
781 break;
782 }
783}
784
785
787{
788 switch( static_cast<int>( aLayer ) )
789 {
791 return F_Cu;
792
794 return Mode() == MODE::NORMAL ? F_Cu : B_Cu;
795
796 // For these, just give the front copper geometry, it doesn't matter.
799 case LAYER_PADS_TH:
801 case LAYER_VIA_HOLES:
804 return ALL_LAYERS;
805
806 default:
807 break;
808 }
809
810 switch( Mode() )
811 {
812 case MODE::CUSTOM:
814 {
815 if( IsFrontLayer( aLayer ) )
816 return F_Cu;
817
818 if( IsBackLayer( aLayer ) )
819 return B_Cu;
820
821 wxASSERT_MSG( IsCopperLayer( aLayer ),
822 wxString::Format( wxT( "Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
823 aLayer ) );
824
825 return Mode() == MODE::CUSTOM ? aLayer : INNER_LAYERS;
826 }
827
828 case MODE::NORMAL:
829 break;
830 }
831
832 return F_Cu;
833}
834
835
837{
838 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
839 // Create on-demand
840 return m_copperProps[layer];
841}
842
843
845{
846 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
847
848 wxCHECK_MSG( m_copperProps.contains( layer ), m_copperProps.at( ALL_LAYERS ),
849 "Attempt to retrieve layer " + std::string( magic_enum::enum_name( layer ) ) + " from a "
850 "padstack that does not contain it" );
851
852 return m_copperProps.at( layer );
853}
854
855
857{
858 return CopperLayer( aLayer ).shape.shape;
859}
860
861
863{
864 CopperLayer( aLayer ).shape.shape = aShape;
865}
866
867
868void PADSTACK::SetSize( const VECTOR2I& aSize, PCB_LAYER_ID aLayer )
869{
870 // File formats do not enforce that sizes are always positive, but KiCad requires it
871 VECTOR2I& size = CopperLayer( aLayer ).shape.size;
872 size.x = std::abs( aSize.x );
873 size.y = std::abs( aSize.y );
874}
875
876
878{
879 return CopperLayer( aLayer ).shape.size;
880}
881
882
884{
885 return m_drill.shape;
886}
887
888
890{
891 m_drill.shape = aShape;
892}
893
894
896{
897 return CopperLayer( aLayer ).shape.offset;
898}
899
900
902{
903 return CopperLayer( aLayer ).shape.offset;
904}
905
906
908{
909 return CopperLayer( aLayer ).shape.anchor_shape;
910}
911
912
914{
915 CopperLayer( aLayer ).shape.anchor_shape = aShape;
916}
917
918
920{
921 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
922}
923
924
926{
927 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
928}
929
930
932{
934}
935
936
938{
939 CopperLayer( aLayer ).shape.round_rect_radius_ratio = aRatio;
940}
941
942
944{
945 const VECTOR2I& size = Size( aLayer );
946 return KiROUND( std::min( size.x, size.y ) * RoundRectRadiusRatio( aLayer ) );
947}
948
949
950void PADSTACK::SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer )
951{
952 const VECTOR2I& size = Size( aLayer );
953 int min_r = std::min( size.x, size.y );
954
955 if( min_r > 0 )
956 SetRoundRectRadiusRatio( aRadius / min_r, aLayer );
957}
958
959
961{
962 return CopperLayer( aLayer ).shape.chamfered_rect_ratio;
963}
964
965
966void PADSTACK::SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer )
967{
968 CopperLayer( aLayer ).shape.chamfered_rect_ratio = aRatio;
969}
970
971
973{
975}
976
977
978const int& PADSTACK::ChamferPositions( PCB_LAYER_ID aLayer ) const
979{
981}
982
983
984void PADSTACK::SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer )
985{
986 CopperLayer( aLayer ).shape.chamfered_rect_positions = aPositions;
987}
988
989
990std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer )
991{
992 return CopperLayer( aLayer ).clearance;
993}
994
995
996const std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer ) const
997{
998 return CopperLayer( aLayer ).clearance;
999}
1000
1001
1002std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer )
1003{
1006}
1007
1008
1009const std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer ) const
1010{
1013}
1014
1015
1016std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer )
1017{
1020}
1021
1022
1023const std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer ) const
1024{
1027
1028
1029std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer )
1030{
1033}
1034
1035
1036const std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer ) const
1037{
1040}
1041
1042
1043std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer )
1044{
1045 return CopperLayer( aLayer ).zone_connection;
1046}
1047
1048
1049const std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer ) const
1050{
1051 return CopperLayer( aLayer ).zone_connection;
1052}
1053
1054
1055std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer )
1056{
1057 return CopperLayer( aLayer ).thermal_spoke_width;
1058}
1059
1060
1061const std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer ) const
1062{
1063 return CopperLayer( aLayer ).thermal_spoke_width;
1064}
1065
1066
1067std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer )
1068{
1069 return CopperLayer( aLayer ).thermal_gap;
1070}
1071
1072
1073const std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const
1074{
1075 return CopperLayer( aLayer ).thermal_gap;
1076}
1077
1078
1080{
1081 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1082
1083 return ( defaults.shape.shape == PAD_SHAPE::CIRCLE
1084 || ( defaults.shape.shape == PAD_SHAPE::CUSTOM
1085 && defaults.shape.anchor_shape == PAD_SHAPE::CIRCLE ) )
1086 ? ANGLE_45 : ANGLE_90;
1087}
1088
1089
1091{
1092 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1093
1094 return defaults.thermal_spoke_angle.value_or( DefaultThermalSpokeAngleForShape( aLayer ) );
1095}
1096
1097
1099{
1100 CopperLayer( aLayer ).thermal_spoke_angle = aAngle;
1101}
1102
1103
1104std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer )
1105{
1106 return CopperLayer( aLayer ).custom_shapes;
1107}
1108
1109
1110const std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer ) const
1111{
1112 return CopperLayer( aLayer ).custom_shapes;
1113}
1114
1115
1117{
1118 CopperLayer( aLayer ).custom_shapes.emplace_back( aShape );
1119}
1120
1121
1122void PADSTACK::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1123 PCB_LAYER_ID aLayer )
1124{
1125 for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1126 AddPrimitive( new PCB_SHAPE( *prim ), aLayer );
1127}
1128
1129
1130void PADSTACK::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1131 PCB_LAYER_ID aLayer )
1132{
1133 ClearPrimitives( aLayer );
1134
1135 if( aPrimitivesList.size() )
1136 AppendPrimitives( aPrimitivesList, aLayer );
1137}
1138
1139
1141{
1142 CopperLayer( aLayer ).custom_shapes.clear();
1143}
1144
1145
1146std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
1147{
1148 if( IsFrontLayer( aSide ) )
1150
1151 if( IsBackLayer( aSide ) )
1153
1154 wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" );
1155}
1156
1157
@ NORMAL
Use all material properties from model file.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
BASE_SET & reset(size_t pos)
Definition: base_set.h:142
int compare(const BASE_SET &other) const
Definition: base_set.h:206
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
double AsDegrees() const
Definition: eda_angle.h:113
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition: padstack.h:118
bool operator==(const PADSTACK &aOther) const
Definition: padstack.cpp:99
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const
Definition: padstack.h:314
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:990
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
Definition: padstack.cpp:1116
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:1130
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: padstack.cpp:144
MASK_LAYER_PROPS & FrontOuterLayers()
Definition: padstack.h:300
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
Definition: padstack.cpp:550
int RoundRectRadius(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:943
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition: padstack.cpp:889
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1029
wxString m_customName
! An override for the IPC-7351 padstack name
Definition: padstack.h:435
DRILL_PROPS m_drill
! The primary drill parameters, which also define the start and end layers for through-hole vias and ...
Definition: padstack.h:461
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:763
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1098
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:937
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
Definition: padstack.h:450
void ClearPrimitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1140
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition: padstack.h:295
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:1146
void SetMode(MODE aMode)
Definition: padstack.cpp:726
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1055
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1016
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1002
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:966
const LSET & LayerSet() const
Definition: padstack.h:268
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
Definition: padstack.cpp:786
PADSTACK & operator=(const PADSTACK &aOther)
Definition: padstack.cpp:62
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:862
EDA_ANGLE DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1079
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:919
DRILL_PROPS m_secondaryDrill
! Secondary drill, used to define back-drilling
Definition: padstack.h:464
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:895
MASK_LAYER_PROPS m_backMaskProps
! The overrides applied to back outer technical layers
Definition: padstack.h:448
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:836
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1090
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:457
PAD_DRILL_SHAPE DrillShape() const
Definition: padstack.cpp:883
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:984
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:950
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1067
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:856
DRILL_PROPS & Drill()
Definition: padstack.h:288
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:913
BOARD_ITEM * m_parent
! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes
Definition: padstack.h:426
PADSTACK(BOARD_ITEM *aParent)
Definition: padstack.cpp:33
LSET m_layerSet
! The board layers that this padstack is active on
Definition: padstack.h:432
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:442
CUSTOM_SHAPE_ZONE_MODE
Definition: padstack.h:152
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
Definition: padstack.cpp:471
PCB_LAYER_ID EndLayer() const
Definition: padstack.cpp:647
int & ChamferPositions(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:972
MASK_LAYER_PROPS m_frontMaskProps
! The overrides applied to front outer technical layers
Definition: padstack.h:445
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:877
MODE
! Copper geometry mode: controls how many unique copper layer shapes this padstack has
Definition: padstack.h:131
@ 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:1122
double RoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:931
UNCONNECTED_LAYER_MODE
! Whether or not to remove the copper shape for unconnected layers
Definition: padstack.h:145
PCB_LAYER_ID StartLayer() const
Definition: padstack.cpp:641
MODE Mode() const
Definition: padstack.h:275
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:907
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:303
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: padstack.cpp:350
wxString Name() const
! Returns the name of this padstack in IPC-7351 format
Definition: padstack.cpp:634
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:868
double ChamferRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:960
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:138
EDA_ANGLE GetOrientation() const
Definition: padstack.h:281
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition: padstack.h:141
void SetLayerSet(const LSET &aSet)
Definition: padstack.h:270
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1043
MODE m_mode
! The copper layer variation mode this padstack is in
Definition: padstack.h:429
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
Definition: padstack.h:438
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1104
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:165
@ LAYER_PAD_BK_NETNAMES
Definition: layer_ids.h:166
@ LAYER_PAD_NETNAMES
Definition: layer_ids.h:167
@ LAYER_VIA_NETNAMES
Definition: layer_ids.h:168
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:622
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:645
#define MAX_CU_LAYERS
Definition: layer_ids.h:140
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:532
@ LAYER_VIA_HOLEWALLS
Definition: layer_ids.h:236
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition: layer_ids.h:216
@ LAYER_PADS_TH
multilayer pads, usually with holes
Definition: layer_ids.h:215
@ LAYER_VIA_HOLES
to draw via holes (pad holes do not use this layer)
Definition: layer_ids.h:217
@ LAYER_PAD_HOLEWALLS
Definition: layer_ids.h:235
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ 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
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
void PackVector2(kiapi::common::types::Vector2 &aOutput, const VECTOR2I aInput)
Definition: api_utils.cpp:69
VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition: api_utils.cpp:75
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:63
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: padstack.h:46
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition: property.h:763
The features of a padstack that can vary between copper layers All parameters are optional; leaving t...
Definition: padstack.h:201
std::optional< ZONE_CONNECTION > zone_connection
Definition: padstack.h:203
std::optional< int > thermal_spoke_width
Definition: padstack.h:204
std::vector< std::shared_ptr< PCB_SHAPE > > custom_shapes
Definition: padstack.h:213
bool operator==(const COPPER_LAYER_PROPS &aOther) const
Definition: padstack.cpp:674
std::optional< EDA_ANGLE > thermal_spoke_angle
Definition: padstack.h:205
std::optional< int > clearance
Definition: padstack.h:207
std::optional< int > thermal_gap
Definition: padstack.h:206
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition: padstack.h:233
PCB_LAYER_ID start
Definition: padstack.h:236
PCB_LAYER_ID end
Definition: padstack.h:237
bool operator==(const DRILL_PROPS &aOther) const
Definition: padstack.cpp:719
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition: padstack.h:234
PAD_DRILL_SHAPE shape
Definition: padstack.h:235
! The features of a padstack that can vary on outer layers.
Definition: padstack.h:221
bool operator==(const MASK_LAYER_PROPS &aOther) const
Definition: padstack.cpp:709
std::optional< int > solder_mask_margin
Definition: padstack.h:222
std::optional< int > solder_paste_margin
Definition: padstack.h:223
std::optional< double > solder_paste_margin_ratio
Definition: padstack.h:224
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition: padstack.h:225
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
Definition: padstack.h:226
! The set of properties that define a pad's shape on a given layer
Definition: padstack.h:159
VECTOR2I trapezoid_delta_size
Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the other.
Definition: padstack.h:182
double round_rect_corner_radius
Definition: padstack.h:173
VECTOR2I offset
Offset of the shape center from the pad center.
Definition: padstack.h:171
bool operator==(const SHAPE_PROPS &aOther) const
Definition: padstack.cpp:664
VECTOR2I size
Size of the shape, or of the anchor pad for custom shape pads.
Definition: padstack.h:162
double chamfered_rect_ratio
Size of chamfer: ratio of smallest of X,Y size.
Definition: padstack.h:175
double round_rect_radius_ratio
Definition: padstack.h:174
PAD_SHAPE shape
Shape of the pad.
Definition: padstack.h:160
PAD_SHAPE anchor_shape
Shape of the anchor when shape == PAD_SHAPE::CUSTOM.
Definition: padstack.h:161