KiCad PCB EDA Suite
Loading...
Searching...
No Matches
padstack.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2024 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <convert_basic_shapes_to_polygon.h> // RECT_CHAMFER_POSITIONS
22#include "padstack.h"
23#include <api/api_enums.h>
24#include <api/api_utils.h>
25#include <api/api_pcb_utils.h>
26#include <api/board/board_types.pb.h>
27#include <layer_range.h>
28#include <macros.h>
29#include <magic_enum.hpp>
30#include <pad.h>
31#include <board.h>
32#include <pcb_shape.h>
33
34
38
39
65
66
68{
69 m_parent = aOther.m_parent;
70 *this = aOther;
71
73 [&]( PCB_LAYER_ID aLayer )
74 {
75 for( std::shared_ptr<PCB_SHAPE>& shape : CopperLayer( aLayer ).custom_shapes )
76 shape->SetParent( m_parent );
77 } );
78}
79
80
82{
83 // NOTE: m_parent is not copied from operator=, because this operator is commonly used to
84 // update the padstack properties, and such an update must not change the parent PAD to point to
85 // the parent of some different padstack.
86
87 m_mode = aOther.m_mode;
88 m_layerSet = aOther.m_layerSet;
96 m_drill = aOther.m_drill;
101
102 // Data consistency enforcement logic that used to live in the pad properties dialog
103 // TODO(JE) Should these move to individual property setters, so that they are always
104 // enforced even through the properties panel and API?
105
107 [&]( PCB_LAYER_ID aLayer )
108 {
109 PAD_SHAPE shape = Shape( aLayer );
110
111 // Make sure leftover primitives don't stick around
112 ClearPrimitives( aLayer );
113
114 // For custom pad shape, duplicate primitives of the pad to copy
115 if( shape == PAD_SHAPE::CUSTOM )
116 ReplacePrimitives( aOther.Primitives( aLayer ), aLayer );
117
118 // rounded rect pads with radius ratio = 0 are in fact rect pads.
119 // So set the right shape (and perhaps issues with a radius = 0)
120 if( shape == PAD_SHAPE::ROUNDRECT && RoundRectRadiusRatio( aLayer ) == 0.0 )
122 } );
123
124 return *this;
125}
126
127
128bool PADSTACK::operator==( const PADSTACK& aOther ) const
129{
130 if( m_mode != aOther.m_mode )
131 return false;
132
133 if( m_layerSet != aOther.m_layerSet )
134 return false;
135
136 if( m_customName != aOther.m_customName )
137 return false;
138
139 if( m_orientation != aOther.m_orientation )
140 return false;
141
142 if( m_frontMaskProps != aOther.m_frontMaskProps )
143 return false;
144
145 if( m_backMaskProps != aOther.m_backMaskProps )
146 return false;
147
149 return false;
150
152 return false;
153
154 if( m_drill != aOther.m_drill )
155 return false;
156
157 if( m_secondaryDrill != aOther.m_secondaryDrill )
158 return false;
159
160 if( m_tertiaryDrill != aOther.m_tertiaryDrill )
161 return false;
162
164 return false;
165
167 return false;
168
169 bool copperMatches = true;
170
172 [&]( PCB_LAYER_ID aLayer )
173 {
174 if( CopperLayer( aLayer ) != aOther.CopperLayer( aLayer ) )
175 copperMatches = false;
176 } );
177
178 return copperMatches;
179}
180
181
182bool PADSTACK::unpackCopperLayer( const kiapi::board::types::PadStackLayer& aProto )
183{
184 using namespace kiapi::board::types;
186
187 if( m_mode == MODE::NORMAL && layer != ALL_LAYERS )
188 return false;
189
190 if( m_mode == MODE::FRONT_INNER_BACK && layer != F_Cu && layer != INNER_LAYERS && layer != B_Cu )
191 return false;
192
193 SetSize( kiapi::common::UnpackVector2( aProto.size() ), layer );
194 SetShape( FromProtoEnum<PAD_SHAPE>( aProto.shape() ), layer );
195 Offset( layer ) = kiapi::common::UnpackVector2( aProto.offset() );
196 SetAnchorShape( FromProtoEnum<PAD_SHAPE>( aProto.custom_anchor_shape() ), layer );
197
198 SHAPE_PROPS& props = CopperLayer( layer ).shape;
199 props.chamfered_rect_ratio = aProto.chamfer_ratio();
200 props.round_rect_radius_ratio = aProto.corner_rounding_ratio();
201
202 if( Shape( layer ) == PAD_SHAPE::TRAPEZOID && aProto.has_trapezoid_delta() )
203 TrapezoidDeltaSize( layer ) = kiapi::common::UnpackVector2( aProto.trapezoid_delta() );
204
205 if( aProto.chamfered_corners().top_left() )
207
208 if( aProto.chamfered_corners().top_right() )
210
211 if( aProto.chamfered_corners().bottom_left() )
213
214 if( aProto.chamfered_corners().bottom_right() )
216
217 ClearPrimitives( layer );
218 google::protobuf::Any a;
219
220 for( const BoardGraphicShape& shapeProto : aProto.custom_shapes() )
221 {
222 a.PackFrom( shapeProto );
223 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_parent );
224
225 if( shape->Deserialize( a ) )
226 AddPrimitive( shape.release(), layer );
227 }
228
229 return true;
230}
231
232
233bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
234{
235 using namespace kiapi::board::types;
236 PadStack padstack;
237
238 auto unpackOptional = []<typename ProtoEnum>( const ProtoEnum& aProto,
239 std::optional<bool>& aDest, ProtoEnum aTrueValue,
240 ProtoEnum aFalseValue )
241 {
242 if( aProto == aTrueValue )
243 aDest = true;
244 else if( aProto == aFalseValue )
245 aDest = false;
246 else
247 aDest = std::nullopt;
248 };
249
250 auto unpackPostMachining = []( const PostMachiningProperties& aProto,
252 {
253 switch( aProto.mode() )
254 {
255 case VDPM_NOT_POST_MACHINED: aDest.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
256 case VDPM_COUNTERBORE: aDest.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
257 case VDPM_COUNTERSINK: aDest.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
258 default: aDest.mode = std::nullopt; break;
259 }
260
261 aDest.size = aProto.size();
262 aDest.depth = aProto.depth();
263 aDest.angle = aProto.angle();
264 };
265
266 if( !aContainer.UnpackTo( &padstack ) )
267 return false;
268
269 m_mode = FromProtoEnum<MODE>( padstack.type() );
270 SetLayerSet( kiapi::board::UnpackLayerSet( padstack.layers() ) );
271 m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T );
272
273 Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() );
274 Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
275 Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
276 unpackOptional( padstack.drill().capped(), Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
277 unpackOptional( padstack.drill().filled(), Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
278
279 if( padstack.has_front_post_machining() )
280 unpackPostMachining( padstack.front_post_machining(), FrontPostMachining() );
281
282 if( padstack.has_back_post_machining() )
283 unpackPostMachining( padstack.back_post_machining(), BackPostMachining() );
284
285 Drill().shape = FromProtoEnum<PAD_DRILL_SHAPE>( padstack.drill().shape() );
286
287 if( padstack.has_secondary_drill() )
288 {
289 const DrillProperties& secondary = padstack.secondary_drill();
290
291 SecondaryDrill().size = kiapi::common::UnpackVector2( secondary.diameter() );
292 SecondaryDrill().start = FromProtoEnum<PCB_LAYER_ID>( secondary.start_layer() );
293 SecondaryDrill().end = FromProtoEnum<PCB_LAYER_ID>( secondary.end_layer() );
294 SecondaryDrill().shape = FromProtoEnum<PAD_DRILL_SHAPE>( secondary.shape() );
295
296 unpackOptional( secondary.capped(), SecondaryDrill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
297 unpackOptional( secondary.filled(), SecondaryDrill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
298 }
299 else
300 {
301 SecondaryDrill().size = { 0, 0 };
305 SecondaryDrill().is_capped = std::nullopt;
306 SecondaryDrill().is_filled = std::nullopt;
307 }
308
309 if( padstack.has_tertiary_drill() )
310 {
311 const DrillProperties& tertiary = padstack.tertiary_drill();
312
313 TertiaryDrill().size = kiapi::common::UnpackVector2( tertiary.diameter() );
314 TertiaryDrill().start = FromProtoEnum<PCB_LAYER_ID>( tertiary.start_layer() );
315 TertiaryDrill().end = FromProtoEnum<PCB_LAYER_ID>( tertiary.end_layer() );
316 TertiaryDrill().shape = FromProtoEnum<PAD_DRILL_SHAPE>( tertiary.shape() );
317
318 unpackOptional( tertiary.capped(), TertiaryDrill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
319 unpackOptional( tertiary.filled(), TertiaryDrill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
320 }
321 else
322 {
323 TertiaryDrill().size = { 0, 0 };
327 TertiaryDrill().is_capped = std::nullopt;
328 TertiaryDrill().is_filled = std::nullopt;
329 }
330
331 for( const PadStackLayer& layer : padstack.copper_layers() )
332 {
333 if( !unpackCopperLayer( layer ) )
334 return false;
335 }
336
337 CopperLayer( ALL_LAYERS ).thermal_gap = std::nullopt;
339
340 if( padstack.has_zone_settings() )
341 {
343 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
344
345 if( padstack.zone_settings().has_thermal_spokes() )
346 {
347 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
348
349 if( thermals.has_gap() )
350 CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap().value_nm();
351
352 if( thermals.has_width() )
353 CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width().value_nm();
354
355 SetThermalSpokeAngle( EDA_ANGLE( thermals.angle().value_degrees(), DEGREES_T ), F_Cu );
356 }
357 }
358 else
359 {
362 }
363
365 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
366
367 unpackOptional( padstack.front_outer_layers().solder_mask_mode(),
368 FrontOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
369
370 unpackOptional( padstack.back_outer_layers().solder_mask_mode(),
371 BackOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
372
373 unpackOptional( padstack.front_outer_layers().covering_mode(), FrontOuterLayers().has_covering,
374 VCM_COVERED, VCM_UNCOVERED );
375
376 unpackOptional( padstack.back_outer_layers().covering_mode(), BackOuterLayers().has_covering,
377 VCM_COVERED, VCM_UNCOVERED );
378
379 unpackOptional( padstack.front_outer_layers().plugging_mode(), FrontOuterLayers().has_plugging,
380 VPM_PLUGGED, VPM_UNPLUGGED );
381
382 unpackOptional( padstack.back_outer_layers().plugging_mode(), BackOuterLayers().has_plugging,
383 VPM_PLUGGED, VPM_UNPLUGGED );
384
385 unpackOptional( padstack.front_outer_layers().solder_paste_mode(),
386 FrontOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
387
388 unpackOptional( padstack.back_outer_layers().solder_paste_mode(),
389 BackOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
390
391 if( padstack.front_outer_layers().has_solder_mask_settings()
392 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
393 {
395 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
396 }
397 else
398 {
399 FrontOuterLayers().solder_mask_margin = std::nullopt;
400 }
401
402 if( padstack.back_outer_layers().has_solder_mask_settings()
403 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
404 {
406 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
407 }
408 else
409 {
410 BackOuterLayers().solder_mask_margin = std::nullopt;
411 }
412
413 if( padstack.front_outer_layers().has_solder_paste_settings()
414 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
415 {
417 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
418 }
419 else
420 {
421 FrontOuterLayers().solder_paste_margin = std::nullopt;
422 }
423
424 if( padstack.back_outer_layers().has_solder_paste_settings()
425 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
426 {
428 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
429 }
430 else
431 {
432 BackOuterLayers().solder_paste_margin = std::nullopt;
433 }
434
435 if( padstack.front_outer_layers().has_solder_paste_settings()
436 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
437 {
439 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
440 }
441 else
442 {
444 }
445
446 if( padstack.back_outer_layers().has_solder_paste_settings()
447 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
448 {
450 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
451 }
452 else
453 {
455 }
456
457
458 return true;
459}
460
461
463{
464 bool hasSecondary = m_secondaryDrill.size.x > 0;
465 bool hasTertiary = m_tertiaryDrill.size.x > 0;
466
467 if( !hasSecondary && !hasTertiary )
469
470 if( hasSecondary && hasTertiary )
472
473 if( hasSecondary )
474 {
475 if( m_secondaryDrill.start == F_Cu )
477
479 }
480
481 if( hasTertiary )
482 {
483 if( m_tertiaryDrill.start == B_Cu )
485
487 }
488
490}
491
492
494{
495 auto initDrill = [this]( DRILL_PROPS& aDrill, PCB_LAYER_ID aStart )
496 {
497 if( aDrill.size.x <= 0 )
498 {
499 aDrill.size = m_drill.size * 1.1; // Backdrill slightly larger than main drill
501 aDrill.start = aStart;
502 aDrill.end = aStart;
503 }
504 else
505 {
506 aDrill.start = aStart;
507 }
508 };
510 {
511 m_tertiaryDrill.size = { 0, 0 };
512 }
513
515 {
516 m_secondaryDrill.size = { 0, 0 };
517 }
518
520 {
521 m_secondaryDrill.start = B_Cu;
522
523 if( m_secondaryDrill.size.x > 0 ) { /* ok */ }
524 else initDrill( m_secondaryDrill, B_Cu );
525 }
526
528 {
529 m_tertiaryDrill.start = F_Cu;
530
531 if( m_tertiaryDrill.size.x > 0 ) { /* ok */ }
532 else initDrill( m_tertiaryDrill, F_Cu );
533 }
534}
535
536
537std::optional<int> PADSTACK::GetBackdrillSize( bool aTop ) const
538{
539 if( m_secondaryDrill.size.x > 0 )
540 {
541 if( aTop && m_secondaryDrill.start == F_Cu ) return m_secondaryDrill.size.x;
542 if( !aTop && m_secondaryDrill.start == B_Cu ) return m_secondaryDrill.size.x;
543 }
544 if( m_tertiaryDrill.size.x > 0 )
545 {
546 if( aTop && m_tertiaryDrill.start == F_Cu ) return m_tertiaryDrill.size.x;
547 if( !aTop && m_tertiaryDrill.start == B_Cu ) return m_tertiaryDrill.size.x;
548 }
549 return std::nullopt;
550}
551
552
553void PADSTACK::SetBackdrillSize( bool aTop, std::optional<int> aSize )
554{
555 DRILL_PROPS* target = nullptr;
556
557 if( aTop )
558 {
559 if( m_secondaryDrill.start == F_Cu ) target = &m_secondaryDrill;
560 else if( m_tertiaryDrill.start == F_Cu ) target = &m_tertiaryDrill;
561 }
562 else
563 {
564 if( m_secondaryDrill.start == B_Cu ) target = &m_secondaryDrill;
565 else if( m_tertiaryDrill.start == B_Cu ) target = &m_tertiaryDrill;
566 }
567
568 if( !target )
569 {
570 if( m_secondaryDrill.size.x <= 0 ) target = &m_secondaryDrill;
571 else if( m_tertiaryDrill.size.x <= 0 ) target = &m_tertiaryDrill;
572 }
573
574 if( !target )
575 {
576 if( aTop ) target = &m_tertiaryDrill;
577 else target = &m_secondaryDrill;
578 }
579
580 if( aSize.has_value() )
581 {
582 target->size = { *aSize, *aSize };
584 target->start = aTop ? F_Cu : B_Cu;
585 if( target->end == UNDEFINED_LAYER ) target->end = UNDEFINED_LAYER;
586 }
587 else
588 {
589 target->size = { 0, 0 };
590 }
591}
592
593
595{
596 if( m_secondaryDrill.size.x > 0 )
597 {
598 if( aTop && m_secondaryDrill.start == F_Cu ) return m_secondaryDrill.end;
599 if( !aTop && m_secondaryDrill.start == B_Cu ) return m_secondaryDrill.end;
600 }
601 if( m_tertiaryDrill.size.x > 0 )
602 {
603 if( aTop && m_tertiaryDrill.start == F_Cu ) return m_tertiaryDrill.end;
604 if( !aTop && m_tertiaryDrill.start == B_Cu ) return m_tertiaryDrill.end;
605 }
606 return UNDEFINED_LAYER;
607}
608
609
611{
612 DRILL_PROPS* target = nullptr;
613
614 if( aTop )
615 {
616 if( m_secondaryDrill.start == F_Cu ) target = &m_secondaryDrill;
617 else if( m_tertiaryDrill.start == F_Cu ) target = &m_tertiaryDrill;
618 }
619 else
620 {
621 if( m_secondaryDrill.start == B_Cu ) target = &m_secondaryDrill;
622 else if( m_tertiaryDrill.start == B_Cu ) target = &m_tertiaryDrill;
623 }
624
625 if( target )
626 {
627 target->end = aLayer;
628 }
629}
630
631void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
632{
633 using namespace kiapi::board::types;
634 PadStack padstack;
635
636 padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
637 kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet );
638 padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
639
640 kiapi::common::PackVector2( *padstack.mutable_drill()->mutable_diameter(), m_drill.size );
641 padstack.mutable_drill()->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_drill.start ) );
642 padstack.mutable_drill()->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_drill.end ) );
643 padstack.mutable_drill()->set_shape( ToProtoEnum<PAD_DRILL_SHAPE, kiapi::board::types::DrillShape>( m_drill.shape ) );
644
645 if( m_drill.is_capped.has_value() )
646 padstack.mutable_drill()->set_capped( m_drill.is_capped.value() ? VDCM_CAPPED : VDCM_UNCAPPED );
647
648 if( m_drill.is_filled.has_value() )
649 padstack.mutable_drill()->set_filled( m_drill.is_filled.value() ? VDFM_FILLED : VDFM_UNFILLED );
650
651 if( m_secondaryDrill.size.x > 0 )
652 {
653 DrillProperties* secondary = padstack.mutable_secondary_drill();
654 kiapi::common::PackVector2( *secondary->mutable_diameter(), m_secondaryDrill.size );
655 secondary->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_secondaryDrill.start ) );
656 secondary->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_secondaryDrill.end ) );
658
659 if( m_secondaryDrill.is_capped.has_value() )
660 secondary->set_capped( m_secondaryDrill.is_capped.value() ? VDCM_CAPPED : VDCM_UNCAPPED );
661
662 if( m_secondaryDrill.is_filled.has_value() )
663 secondary->set_filled( m_secondaryDrill.is_filled.value() ? VDFM_FILLED : VDFM_UNFILLED );
664 }
665
666 if( m_tertiaryDrill.size.x > 0 )
667 {
668 DrillProperties* tertiary = padstack.mutable_tertiary_drill();
669 kiapi::common::PackVector2( *tertiary->mutable_diameter(), m_tertiaryDrill.size );
670 tertiary->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_tertiaryDrill.start ) );
671 tertiary->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( m_tertiaryDrill.end ) );
673
674 if( m_tertiaryDrill.is_capped.has_value() )
675 tertiary->set_capped( m_tertiaryDrill.is_capped.value() ? VDCM_CAPPED : VDCM_UNCAPPED );
676
677 if( m_tertiaryDrill.is_filled.has_value() )
678 tertiary->set_filled( m_tertiaryDrill.is_filled.value() ? VDFM_FILLED : VDFM_UNFILLED );
679 }
680
681 auto packPostMachining = []( const PADSTACK::POST_MACHINING_PROPS& aProps,
682 PostMachiningProperties* aProto )
683 {
684 if( aProps.mode.has_value() )
685 {
686 switch( aProps.mode.value() )
687 {
688 case PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED: aProto->set_mode( VDPM_NOT_POST_MACHINED ); break;
689 case PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE: aProto->set_mode( VDPM_COUNTERBORE ); break;
690 case PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK: aProto->set_mode( VDPM_COUNTERSINK ); break;
691 default: break;
692 }
693 }
694
695 aProto->set_size( aProps.size );
696 aProto->set_depth( aProps.depth );
697 aProto->set_angle( aProps.angle );
698 };
699
700 if( m_frontPostMachining.mode.has_value() || m_frontPostMachining.size > 0 )
701 packPostMachining( m_frontPostMachining, padstack.mutable_front_post_machining() );
702
703 if( m_backPostMachining.mode.has_value() || m_backPostMachining.size > 0 )
704 packPostMachining( m_backPostMachining, padstack.mutable_back_post_machining() );
705
707 [&]( PCB_LAYER_ID aLayer )
708 {
709 PadStackLayer* layer = padstack.add_copper_layers();
710 const COPPER_LAYER_PROPS& props = CopperLayer( aLayer );
711
712 layer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
713 kiapi::common::PackVector2( *layer->mutable_size(), props.shape.size );
715 kiapi::common::PackVector2( *layer->mutable_offset(), props.shape.offset );
716 layer->set_custom_anchor_shape( ToProtoEnum<PAD_SHAPE, kiapi::board::types::PadStackShape>( props.shape.anchor_shape ) );
717 layer->set_chamfer_ratio( props.shape.chamfered_rect_ratio );
718 layer->set_corner_rounding_ratio( props.shape.round_rect_radius_ratio );
719
720 if( props.shape.shape == PAD_SHAPE::TRAPEZOID )
721 kiapi::common::PackVector2( *layer->mutable_trapezoid_delta(), props.shape.trapezoid_delta_size );
722
724 layer->mutable_chamfered_corners()->set_top_left( true );
725
727 layer->mutable_chamfered_corners()->set_top_right( true );
728
730 layer->mutable_chamfered_corners()->set_bottom_left( true );
731
733 layer->mutable_chamfered_corners()->set_bottom_right( true );
734
735 for( const std::shared_ptr<PCB_SHAPE>& shape : props.custom_shapes )
736 {
737 google::protobuf::Any a;
738 shape->Serialize( a );
739 a.UnpackTo( layer->add_custom_shapes() );
740 }
741 } );
742
743 if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() )
744 {
745 padstack.mutable_zone_settings()->set_zone_connection(
747 }
748
749 if( CopperLayer( ALL_LAYERS ).thermal_gap.has_value() )
750 {
751 padstack.mutable_zone_settings()->mutable_thermal_spokes()->mutable_gap()->set_value_nm(
752 CopperLayer( ALL_LAYERS ).thermal_gap.value() );
753 }
754
755 if( CopperLayer( ALL_LAYERS ).thermal_spoke_width.has_value() )
756 {
757 padstack.mutable_zone_settings()->mutable_thermal_spokes()->mutable_width()->set_value_nm(
758 CopperLayer( ALL_LAYERS ).thermal_spoke_width.value() );
759 }
760
761 if( CopperLayer( ALL_LAYERS ).thermal_spoke_angle.has_value() )
762 {
763 padstack.mutable_zone_settings()->mutable_thermal_spokes()->mutable_angle()->set_value_degrees(
764 CopperLayer( ALL_LAYERS ).thermal_spoke_angle.value().AsDegrees() );
765 }
766
767 padstack.set_unconnected_layer_removal( ToProtoEnum<UNCONNECTED_LAYER_MODE,
768 kiapi::board::types::UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
769
770 if( FrontOuterLayers().has_solder_mask.has_value() )
771 {
772 padstack.mutable_front_outer_layers()->set_solder_mask_mode(
773 FrontOuterLayers().has_solder_mask.value() ? SMM_MASKED : SMM_UNMASKED );
774 }
775
776 if( BackOuterLayers().has_solder_mask.has_value() )
777 {
778 padstack.mutable_back_outer_layers()->set_solder_mask_mode(
779 BackOuterLayers().has_solder_mask.value() ? SMM_MASKED : SMM_UNMASKED );
780 }
781
782 if( FrontOuterLayers().has_covering.has_value() )
783 {
784 padstack.mutable_front_outer_layers()->set_covering_mode(
785 FrontOuterLayers().has_covering.value() ? VCM_COVERED : VCM_UNCOVERED );
786 }
787
788 if( BackOuterLayers().has_covering.has_value() )
789 {
790 padstack.mutable_back_outer_layers()->set_covering_mode(
791 BackOuterLayers().has_covering.value() ? VCM_COVERED : VCM_UNCOVERED );
792 }
793
794 if( FrontOuterLayers().has_plugging.has_value() )
795 {
796 padstack.mutable_front_outer_layers()->set_plugging_mode(
797 FrontOuterLayers().has_plugging.value() ? VPM_PLUGGED : VPM_UNPLUGGED );
798 }
799
800 if( BackOuterLayers().has_plugging.has_value() )
801 {
802 padstack.mutable_back_outer_layers()->set_plugging_mode(
803 BackOuterLayers().has_plugging.value() ? VPM_PLUGGED : VPM_UNPLUGGED );
804 }
805
806 if( FrontOuterLayers().has_solder_paste.has_value() )
807 {
808 padstack.mutable_front_outer_layers()->set_solder_paste_mode(
809 FrontOuterLayers().has_solder_paste.value() ? SPM_PASTE : SPM_NO_PASTE );
810 }
811
812 if( BackOuterLayers().has_solder_paste.has_value() )
813 {
814 padstack.mutable_back_outer_layers()->set_solder_paste_mode(
815 BackOuterLayers().has_solder_paste.value() ? SPM_PASTE : SPM_NO_PASTE );
816 }
817
818 if( FrontOuterLayers().solder_mask_margin.has_value() )
819 {
820 padstack.mutable_front_outer_layers()->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
821 FrontOuterLayers().solder_mask_margin.value() );
822 }
823
824 if( BackOuterLayers().solder_mask_margin.has_value() )
825 {
826 padstack.mutable_back_outer_layers()->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
827 BackOuterLayers().solder_mask_margin.value() );
828 }
829
830 if( FrontOuterLayers().solder_paste_margin.has_value() )
831 {
832 padstack.mutable_front_outer_layers()->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
833 FrontOuterLayers().solder_paste_margin.value() );
834 }
835
836 if( BackOuterLayers().solder_paste_margin.has_value() )
837 {
838 padstack.mutable_back_outer_layers()->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
839 BackOuterLayers().solder_paste_margin.value() );
840 }
841
842 if( FrontOuterLayers().solder_paste_margin_ratio.has_value() )
843 {
844 padstack.mutable_front_outer_layers()->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
845 FrontOuterLayers().solder_paste_margin_ratio.value() );
846 }
847
848 if( BackOuterLayers().solder_paste_margin_ratio.has_value() )
849 {
850 padstack.mutable_back_outer_layers()->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
851 BackOuterLayers().solder_paste_margin_ratio.value() );
852 }
853
854 aContainer.PackFrom( padstack );
855}
856
857
858void PADSTACK::SetSize( const VECTOR2I& aSize, PCB_LAYER_ID aLayer )
859{
860 VECTOR2I size = aSize;
861
862 if( size.x < 0 )
863 size.x = 0;
864
865 if( size.y < 0 )
866 size.y = 0;
867
868 CopperLayer( aLayer ).shape.size = size;
869}
870
871
873{
874 return CopperLayer( aLayer ).shape.size;
875}
876
877
879{
880 return CopperLayer( aLayer ).shape.shape;
881}
882
883
885{
886 CopperLayer( aLayer ).shape.shape = aShape;
887}
888
889
891{
892 return m_drill.shape;
893}
894
895
897{
898 m_drill.shape = aShape;
899}
900
901
903{
904 return CopperLayer( aLayer ).shape.offset;
905}
906
907
909{
910 return CopperLayer( aLayer ).shape.offset;
911}
912
913
915{
916 return CopperLayer( aLayer ).shape.anchor_shape;
917}
918
919
921{
922 CopperLayer( aLayer ).shape.anchor_shape = aShape;
923}
924
925
930
931
933{
934 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
935}
936
937
939{
941}
942
943
945{
946 CopperLayer( aLayer ).shape.round_rect_radius_ratio = aRatio;
947}
948
949
951{
952 const VECTOR2I& size = Size( aLayer );
953 return KiROUND( std::min( size.x, size.y ) * RoundRectRadiusRatio( aLayer ) );
954}
955
956
957void PADSTACK::SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer )
958{
959 const VECTOR2I& size = Size( aLayer );
960 int min_r = std::min( size.x, size.y );
961
962 if( min_r > 0 )
963 SetRoundRectRadiusRatio( aRadius / min_r, aLayer );
964}
965
966
968{
969 return CopperLayer( aLayer ).shape.chamfered_rect_ratio;
970}
971
972
973void PADSTACK::SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer )
974{
975 CopperLayer( aLayer ).shape.chamfered_rect_ratio = aRatio;
976}
977
978
983
984
985const int& PADSTACK::ChamferPositions( PCB_LAYER_ID aLayer ) const
986{
988}
989
990
991void PADSTACK::SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer )
992{
993 CopperLayer( aLayer ).shape.chamfered_rect_positions = aPositions;
994}
995
996
997std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer )
998{
999 return CopperLayer( aLayer ).clearance;
1000}
1001
1002
1003const std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer ) const
1004{
1005 return CopperLayer( aLayer ).clearance;
1006}
1007
1008
1009std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer )
1010{
1011 if( IsFrontLayer( aLayer ) )
1013 else if( IsBackLayer( aLayer ) )
1015 else
1016 return FrontOuterLayers().solder_mask_margin; // Should not happen
1017}
1018
1019
1020const std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer ) const
1021{
1022 if( IsFrontLayer( aLayer ) )
1024 else if( IsBackLayer( aLayer ) )
1026 else
1027 return FrontOuterLayers().solder_mask_margin; // Should not happen
1028}
1029
1030
1031std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer )
1032{
1033 if( IsFrontLayer( aLayer ) )
1035 else if( IsBackLayer( aLayer ) )
1037 else
1038 return FrontOuterLayers().solder_paste_margin; // Should not happen
1039}
1040
1041
1042const std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer ) const
1043{
1044 if( IsFrontLayer( aLayer ) )
1046 else if( IsBackLayer( aLayer ) )
1048 else
1049 return FrontOuterLayers().solder_paste_margin; // Should not happen
1050}
1051
1052
1053std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer )
1054{
1055 if( IsFrontLayer( aLayer ) )
1057 else if( IsBackLayer( aLayer ) )
1059 else
1060 return FrontOuterLayers().solder_paste_margin_ratio; // Should not happen
1061}
1062
1063
1064const std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer ) const
1065{
1066 if( IsFrontLayer( aLayer ) )
1068 else if( IsBackLayer( aLayer ) )
1070 else
1071 return FrontOuterLayers().solder_paste_margin_ratio; // Should not happen
1072}
1073
1074
1075std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer )
1076{
1077 return CopperLayer( aLayer ).zone_connection;
1078}
1079
1080
1081const std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer ) const
1082{
1083 return CopperLayer( aLayer ).zone_connection;
1084}
1085
1086
1087std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer )
1088{
1089 return CopperLayer( aLayer ).thermal_spoke_width;
1090}
1091
1092
1093const std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer ) const
1094{
1095 return CopperLayer( aLayer ).thermal_spoke_width;
1096}
1097
1098
1099std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer )
1100{
1101 return CopperLayer( aLayer ).thermal_gap;
1102}
1103
1104
1105const std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const
1106{
1107 return CopperLayer( aLayer ).thermal_gap;
1108}
1109
1110
1112{
1113 if( Shape( aLayer ) == PAD_SHAPE::OVAL || Shape( aLayer ) == PAD_SHAPE::RECTANGLE
1114 || Shape( aLayer ) == PAD_SHAPE::ROUNDRECT || Shape( aLayer ) == PAD_SHAPE::CHAMFERED_RECT )
1115 {
1116 return ANGLE_90;
1117 }
1118
1119 return ANGLE_45;
1120}
1121
1122
1124{
1125 if( CopperLayer( aLayer ).thermal_spoke_angle.has_value() )
1126 return CopperLayer( aLayer ).thermal_spoke_angle.value();
1127
1128 return DefaultThermalSpokeAngleForShape( aLayer );
1129}
1130
1131
1133{
1134 CopperLayer( aLayer ).thermal_spoke_angle = aAngle;
1135}
1136
1137
1138std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer )
1139{
1140 return CopperLayer( aLayer ).custom_shapes;
1141}
1142
1143
1144const std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer ) const
1145{
1146 return CopperLayer( aLayer ).custom_shapes;
1147}
1148
1149
1151{
1152 CopperLayer( aLayer ).custom_shapes.emplace_back( aShape );
1153}
1154
1155
1156void PADSTACK::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aList,
1157 PCB_LAYER_ID aLayer )
1158{
1159 std::vector<std::shared_ptr<PCB_SHAPE>>& list = CopperLayer( aLayer ).custom_shapes;
1160
1161 for( const std::shared_ptr<PCB_SHAPE>& item : aList )
1162 {
1163 PCB_SHAPE* new_shape = static_cast<PCB_SHAPE*>( item->Clone() );
1164 new_shape->SetParent( m_parent );
1165 list.emplace_back( new_shape );
1166 }
1167}
1168
1169
1170void PADSTACK::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aList,
1171 PCB_LAYER_ID aLayer )
1172{
1173 ClearPrimitives( aLayer );
1174 AppendPrimitives( aList, aLayer );
1175}
1176
1177
1179{
1180 CopperLayer( aLayer ).custom_shapes.clear();
1181}
1182
1183
1185{
1186 if( m_mode == MODE::NORMAL )
1187 return m_copperProps[ALL_LAYERS];
1188
1190 {
1191 if( IsFrontLayer( aLayer ) )
1192 return m_copperProps[F_Cu];
1193 else if( IsBackLayer( aLayer ) )
1194 return m_copperProps[B_Cu];
1195 else
1197 }
1198
1199 return m_copperProps[aLayer];
1200}
1201
1202
1204{
1205 if( m_mode == MODE::NORMAL )
1206 return m_copperProps.at( ALL_LAYERS );
1207
1209 {
1210 if( IsFrontLayer( aLayer ) )
1211 return m_copperProps.at( F_Cu );
1212 else if( IsBackLayer( aLayer ) )
1213 return m_copperProps.at( B_Cu );
1214 else
1215 return m_copperProps.at( INNER_LAYERS );
1216 }
1217
1218 if( m_copperProps.count( aLayer ) )
1219 return m_copperProps.at( aLayer );
1220
1221 return m_copperProps.at( ALL_LAYERS );
1222}
1223
1224
1225void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& aMethod ) const
1226{
1227 if( m_mode == MODE::NORMAL )
1228 {
1229 aMethod( ALL_LAYERS );
1230 }
1231 else if( m_mode == MODE::FRONT_INNER_BACK )
1232 {
1233 aMethod( F_Cu );
1234 aMethod( INNER_LAYERS );
1235 aMethod( B_Cu );
1236 }
1237 else
1238 {
1239 for( const auto& [layer, props] : m_copperProps )
1240 aMethod( layer );
1241 }
1242}
1243
1244
1245std::vector<PCB_LAYER_ID> PADSTACK::UniqueLayers() const
1246{
1247 std::vector<PCB_LAYER_ID> layers;
1248
1249 ForEachUniqueLayer( [&]( PCB_LAYER_ID layer ) { layers.push_back( layer ); } );
1250
1251 return layers;
1252}
1253
1254
1256{
1257 if( m_mode == MODE::NORMAL )
1258 return ALL_LAYERS;
1259
1261 {
1262 if( IsFrontLayer( aLayer ) )
1263 return F_Cu;
1264 else if( IsBackLayer( aLayer ) )
1265 return B_Cu;
1266 else
1267 return INNER_LAYERS;
1268 }
1269
1270 if( m_copperProps.count( aLayer ) )
1271 return aLayer;
1272
1273 return ALL_LAYERS;
1274}
1275
1276
1278{
1279 LSET layers;
1280
1281 if( m_mode == MODE::NORMAL && aOther.m_mode == MODE::NORMAL )
1282 {
1283 layers.set( ALL_LAYERS );
1284 }
1285 else
1286 {
1287 ForEachUniqueLayer( [&]( PCB_LAYER_ID layer ) { layers.set( layer ); } );
1288 aOther.ForEachUniqueLayer( [&]( PCB_LAYER_ID layer ) { layers.set( layer ); } );
1289 }
1290
1291 return layers;
1292}
1293
1294
1295std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
1296{
1297 if( IsFrontLayer( aSide ) )
1298 return FrontOuterLayers().has_solder_mask.has_value() ?
1299 std::optional<bool>( !FrontOuterLayers().has_solder_mask.value() ) : std::nullopt;
1300 else if( IsBackLayer( aSide ) )
1301 return BackOuterLayers().has_solder_mask.has_value() ?
1302 std::optional<bool>( !BackOuterLayers().has_solder_mask.value() ) : std::nullopt;
1303 else
1304 return std::nullopt;
1305}
1306
1307
1308std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const
1309{
1310 if( IsFrontLayer( aSide ) )
1312 else if( IsBackLayer( aSide ) )
1314 else
1315 return std::nullopt;
1316}
1317
1318
1319std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const
1320{
1321 if( IsFrontLayer( aSide ) )
1323 else if( IsBackLayer( aSide ) )
1325 else
1326 return std::nullopt;
1327}
1328
1329
1330std::optional<bool> PADSTACK::IsCapped() const
1331{
1332 return m_drill.is_capped;
1333}
1334
1335
1336std::optional<bool> PADSTACK::IsFilled() const
1337{
1338 return m_drill.is_filled;
1339}
1340
1341
1342int PADSTACK::Compare( const PADSTACK* aLeft, const PADSTACK* aRight )
1343{
1344 // TODO: Implement full comparison
1345 if( aLeft == aRight )
1346 return 0;
1347
1348 return 1;
1349}
1350
1351
1352double PADSTACK::Similarity( const PADSTACK& aOther ) const
1353{
1354 // TODO: Implement similarity
1355 return 0.0;
1356}
1357
1358
1359void PADSTACK::FlipLayers( int aLayerCount )
1360{
1361 // TODO: Implement FlipLayers
1362}
1363
1364
1366{
1367 return m_drill.start;
1368}
1369
1370
1372{
1373 return m_drill.end;
1374}
1375
1376
1377wxString PADSTACK::Name() const
1378{
1379 return m_customName;
1380}
1381
1382
1386 size( 0, 0 ),
1387 offset( 0, 0 ),
1389 chamfered_rect_ratio( 0.0 ),
1391 trapezoid_delta_size( 0, 0 )
1392{
1393}
1394
1395
1397{
1398 return shape == aOther.shape &&
1399 anchor_shape == aOther.anchor_shape &&
1400 size == aOther.size &&
1401 offset == aOther.offset &&
1406}
1407
1408
1410{
1411 if( !( shape == aOther.shape ) ) return false;
1412 if( zone_connection != aOther.zone_connection ) return false;
1413 if( thermal_spoke_width != aOther.thermal_spoke_width ) return false;
1414 if( thermal_spoke_angle != aOther.thermal_spoke_angle ) return false;
1415 if( thermal_gap != aOther.thermal_gap ) return false;
1416 if( clearance != aOther.clearance ) return false;
1417
1418 if( custom_shapes.size() != aOther.custom_shapes.size() ) return false;
1419
1420 // Deep compare of shapes?
1421 // For now, just check pointers or size
1422 return true;
1423}
1424
1425
1436
1437
1439{
1440 return size == aOther.size &&
1441 shape == aOther.shape &&
1442 start == aOther.start &&
1443 end == aOther.end &&
1444 is_filled == aOther.is_filled &&
1445 is_capped == aOther.is_capped;
1446}
1447
1448
1450{
1451 return mode == aOther.mode &&
1452 size == aOther.size &&
1453 depth == aOther.depth &&
1454 angle == aOther.angle;
1455}
1456
1457
1459{
1460 m_mode = aMode;
1461}
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
Definition api_enums.cpp:97
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:36
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
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:83
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
std::optional< bool > IsFilled() const
bool operator==(const PADSTACK &aOther) const
Definition padstack.cpp:128
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition padstack.cpp:997
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
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 ...
void SetBackdrillMode(BACKDRILL_MODE aMode)
Definition padstack.cpp:493
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition padstack.cpp:233
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:356
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
int RoundRectRadius(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:950
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition padstack.cpp:896
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
wxString m_customName
! An override for the IPC-7351 padstack name
Definition padstack.h:521
void SetBackdrillSize(bool aTop, std::optional< int > aSize)
Definition padstack.cpp:553
DRILL_PROPS m_drill
!
Definition padstack.h:547
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::...
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition padstack.cpp:944
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
Definition padstack.h:536
void ClearPrimitives(PCB_LAYER_ID aLayer)
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition padstack.h:351
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
void SetMode(MODE aMode)
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
POST_MACHINING_PROPS m_backPostMachining
Definition padstack.h:556
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition padstack.cpp:973
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
PADSTACK & operator=(const PADSTACK &aOther)
Definition padstack.cpp:81
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition padstack.cpp:884
EDA_ANGLE DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
Definition padstack.cpp:926
DRILL_PROPS m_secondaryDrill
! Secondary drill, used to define back-drilling starting from the bottom side
Definition padstack.h:550
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition padstack.cpp:902
MASK_LAYER_PROPS m_backMaskProps
! The overrides applied to back outer technical layers
Definition padstack.h:534
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
void FlipLayers(int aCopperLayerCount)
Flips the padstack layers in the case that the pad's parent footprint is flipped to the other side of...
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:543
bool unpackCopperLayer(const kiapi::board::types::PadStackLayer &aProto)
Definition padstack.cpp:182
PAD_DRILL_SHAPE DrillShape() const
Definition padstack.cpp:890
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition padstack.cpp:991
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:344
DRILL_PROPS m_tertiaryDrill
! Tertiary drill, used to define back-drilling starting from the top side
Definition padstack.h:553
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
Definition padstack.cpp:957
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
LSET RelevantShapeLayers(const PADSTACK &aOther) const
Returns the set of layers that must be considered if checking one padstack against another.
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:341
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:878
DRILL_PROPS & Drill()
Definition padstack.h:335
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition padstack.cpp:920
BOARD_ITEM * m_parent
! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes
Definition padstack.h:512
PADSTACK(BOARD_ITEM *aParent)
Definition padstack.cpp:40
std::optional< bool > IsCapped() const
std::vector< PCB_LAYER_ID > UniqueLayers() const
void SetBackdrillEndLayer(bool aTop, PCB_LAYER_ID aLayer)
Definition padstack.cpp:610
LSET m_layerSet
! The board layers that this padstack is active on
Definition padstack.h:518
std::optional< int > GetBackdrillSize(bool aTop) const
Definition padstack.cpp:537
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:528
BACKDRILL_MODE GetBackdrillMode() const
Definition padstack.cpp:462
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
PCB_LAYER_ID EndLayer() const
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) const
int & ChamferPositions(PCB_LAYER_ID aLayer)
Definition padstack.cpp:979
MASK_LAYER_PROPS m_frontMaskProps
! The overrides applied to front outer technical layers
Definition padstack.h:531
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:872
MODE
! Copper geometry mode: controls how many unique copper layer shapes this padstack has
Definition padstack.h:170
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:172
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.
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:338
double RoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:938
PCB_LAYER_ID GetBackdrillEndLayer(bool aTop) const
Definition padstack.cpp:594
PCB_LAYER_ID StartLayer() const
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:347
POST_MACHINING_PROPS m_frontPostMachining
Definition padstack.h:555
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:914
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:359
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition padstack.cpp:631
wxString Name() const
! Returns the name of this padstack in IPC-7351 format
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition padstack.cpp:858
double ChamferRatio(PCB_LAYER_ID aLayer) const
Definition padstack.cpp:967
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition padstack.h:180
void SetLayerSet(const LSET &aSet)
Definition padstack.h:311
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
@ NORMAL
Padstack for a footprint pad.
Definition padstack.h:163
MODE m_mode
! The copper layer variation mode this padstack is in
Definition padstack.h:515
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
Definition padstack.h:524
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:780
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:803
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
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
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:86
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition api_utils.cpp:79
PAD_DRILL_POST_MACHINING_MODE
Definition padstack.h:76
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition padstack.h:69
CUSTOM_SHAPE_ZONE_MODE
Definition padstack.h:137
BACKDRILL_MODE
Definition padstack.h:84
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:52
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
UNCONNECTED_LAYER_MODE
Definition padstack.h:128
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:821
The features of a padstack that can vary between copper layers All parameters are optional; leaving t...
Definition padstack.h:226
std::optional< ZONE_CONNECTION > zone_connection
Definition padstack.h:228
std::optional< int > thermal_spoke_width
Definition padstack.h:229
std::vector< std::shared_ptr< PCB_SHAPE > > custom_shapes
Definition padstack.h:238
bool operator==(const COPPER_LAYER_PROPS &aOther) const
std::optional< EDA_ANGLE > thermal_spoke_angle
Definition padstack.h:230
std::optional< int > clearance
Definition padstack.h:232
std::optional< int > thermal_gap
Definition padstack.h:231
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition padstack.h:261
PCB_LAYER_ID start
Definition padstack.h:264
PCB_LAYER_ID end
Definition padstack.h:265
bool operator==(const DRILL_PROPS &aOther) const
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:262
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:268
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:267
PAD_DRILL_SHAPE shape
Definition padstack.h:263
bool operator==(const MASK_LAYER_PROPS &aOther) const
std::optional< int > solder_mask_margin
Definition padstack.h:247
std::optional< bool > has_covering
True if the pad on this side should have covering.
Definition padstack.h:253
std::optional< int > solder_paste_margin
Definition padstack.h:248
std::optional< double > solder_paste_margin_ratio
Definition padstack.h:249
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition padstack.h:251
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
Definition padstack.h:252
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition padstack.h:254
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:275
bool operator==(const POST_MACHINING_PROPS &aOther) const
VECTOR2I trapezoid_delta_size
Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the other.
Definition padstack.h:207
VECTOR2I offset
Offset of the shape center from the pad center.
Definition padstack.h:197
bool operator==(const SHAPE_PROPS &aOther) const
VECTOR2I size
Size of the shape, or of the anchor pad for custom shape pads.
Definition padstack.h:188
double chamfered_rect_ratio
Size of chamfer: ratio of smallest of X,Y size.
Definition padstack.h:200
double round_rect_radius_ratio
Definition padstack.h:199
PAD_SHAPE shape
Shape of the pad.
Definition padstack.h:186
PAD_SHAPE anchor_shape
Shape of the anchor when shape == PAD_SHAPE::CUSTOM.
Definition padstack.h:187
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695