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 <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 = 0;
44 m_copperProps[PADSTACK::ALL_LAYERS].thermal_spoke_angle = ANGLE_45;
45 m_copperProps[PADSTACK::ALL_LAYERS].thermal_gap = 0;
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 GraphicShape& shapeProto : aProto.custom_shapes() )
199 {
200 a.PackFrom( shapeProto );
201 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( m_parent );
202
203 if( shape->Deserialize( a ) )
204 AddPrimitive( shape.release(), layer );
205 }
206
207 return true;
208}
209
210
211bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
212{
213 using namespace kiapi::board::types;
214 PadStack padstack;
215
216 if( !aContainer.UnpackTo( &padstack ) )
217 return false;
218
219 m_mode = FromProtoEnum<MODE>( padstack.type() );
220 SetLayerSet( kiapi::board::UnpackLayerSet( padstack.layers() ) );
221 m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T );
222
223 Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() );
224 Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
225 Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
226
227 for( const PadStackLayer& layer : padstack.copper_layers() )
228 {
229 if( !unpackCopperLayer( layer ) )
230 return false;
231 }
232
233 if( padstack.has_zone_settings() )
234 {
236 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
237
238 if( padstack.zone_settings().has_thermal_spokes() )
239 {
240 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
241
242 CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap();
243 CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width();
244 SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu );
245 }
246 }
247 else
248 {
249 CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED;
253 }
254
256 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
257
258 auto unpackMask =
259 []( const SolderMaskMode& aProto, std::optional<bool>& aDest )
260 {
261 switch( aProto )
262 {
263 case kiapi::board::types::SMM_MASKED:
264 aDest = true;
265 break;
266
267 case kiapi::board::types::SMM_UNMASKED:
268 aDest = false;
269 break;
270
271 default:
272 case kiapi::board::types::SMM_FROM_DESIGN_RULES:
273 aDest = std::nullopt;
274 break;
275 }
276 };
277
278 unpackMask( padstack.front_outer_layers().solder_mask_mode(),
279 FrontOuterLayers().has_solder_mask );
280
281 unpackMask( padstack.back_outer_layers().solder_mask_mode(),
282 BackOuterLayers().has_solder_mask );
283
284 auto unpackPaste =
285 []( const SolderPasteMode& aProto, std::optional<bool>& aDest )
286 {
287 switch( aProto )
288 {
289 case kiapi::board::types::SPM_PASTE:
290 aDest = true;
291 break;
292
293 case kiapi::board::types::SPM_NO_PASTE:
294 aDest = false;
295 break;
296
297 default:
298 case kiapi::board::types::SPM_FROM_DESIGN_RULES:
299 aDest = std::nullopt;
300 break;
301 }
302 };
303
304 unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
305 FrontOuterLayers().has_solder_paste );
306
307 unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
308 BackOuterLayers().has_solder_paste );
309
310 if( padstack.front_outer_layers().has_solder_mask_settings()
311 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
312 {
314 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
315 }
316 else
317 {
318 FrontOuterLayers().solder_mask_margin = std::nullopt;
319 }
320
321 if( padstack.back_outer_layers().has_solder_mask_settings()
322 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
323 {
325 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
326 }
327 else
328 {
329 BackOuterLayers().solder_mask_margin = std::nullopt;
330 }
331
332 if( padstack.front_outer_layers().has_solder_paste_settings()
333 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
334 {
336 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
337 }
338 else
339 {
340 FrontOuterLayers().solder_paste_margin = std::nullopt;
341 }
342
343 if( padstack.back_outer_layers().has_solder_paste_settings()
344 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
345 {
347 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
348 }
349 else
350 {
351 BackOuterLayers().solder_paste_margin = std::nullopt;
352 }
353
354 if( padstack.front_outer_layers().has_solder_paste_settings()
355 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
356 {
358 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
359 }
360 else
361 {
363 }
364
365 if( padstack.back_outer_layers().has_solder_paste_settings()
366 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
367 {
369 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
370 }
371 else
372 {
374 }
375
376
377 return true;
378}
379
380
381void PADSTACK::packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const
382{
383 using namespace kiapi::board::types;
384
385 PadStackLayer* stackLayer = aProto.add_copper_layers();
386
387 stackLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
388 kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( aLayer ) );
389 kiapi::common::PackVector2( *stackLayer->mutable_offset(), Offset( aLayer ) );
390
391 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>( Shape( aLayer ) ) );
392
393 stackLayer->set_custom_anchor_shape(
394 ToProtoEnum<PAD_SHAPE, PadStackShape>( AnchorShape( aLayer ) ) );
395
396 stackLayer->set_chamfer_ratio( CopperLayer( aLayer ).shape.chamfered_rect_ratio );
397 stackLayer->set_corner_rounding_ratio( CopperLayer( aLayer ).shape.round_rect_radius_ratio );
398
399 if( Shape( aLayer ) == PAD_SHAPE::TRAPEZOID )
400 {
401 kiapi::common::PackVector2( *stackLayer->mutable_trapezoid_delta(),
402 TrapezoidDeltaSize( aLayer ) );
403 }
404
405 google::protobuf::Any a;
406
407 for( const std::shared_ptr<PCB_SHAPE>& shape : Primitives( aLayer ) )
408 {
409 shape->Serialize( a );
410 GraphicShape* s = stackLayer->add_custom_shapes();
411 a.UnpackTo( s );
412 }
413
414 const int& corners = CopperLayer( aLayer ).shape.chamfered_rect_positions;
415 stackLayer->mutable_chamfered_corners()->set_top_left( corners & RECT_CHAMFER_TOP_LEFT );
416 stackLayer->mutable_chamfered_corners()->set_top_right( corners & RECT_CHAMFER_TOP_RIGHT );
417 stackLayer->mutable_chamfered_corners()->set_bottom_left( corners & RECT_CHAMFER_BOTTOM_LEFT );
418 stackLayer->mutable_chamfered_corners()->set_bottom_right( corners & RECT_CHAMFER_BOTTOM_RIGHT );
419}
420
421
422void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
423{
424 using namespace kiapi::board::types;
425 PadStack padstack;
426
427 padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
428 kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet );
429 padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
430
431 DrillProperties* drill = padstack.mutable_drill();
432 drill->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( StartLayer() ) );
433 drill->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( EndLayer() ) );
434 kiapi::common::PackVector2( *drill->mutable_diameter(), Drill().size );
435
436 ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer )
437 {
438 packCopperLayer( aLayer, padstack );
439 } );
440
441 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
442 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
443
444 if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() )
445 {
446 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
447 *CopperLayer( ALL_LAYERS ).zone_connection ) );
448 }
449
450 thermalSettings->set_width( CopperLayer( ALL_LAYERS ).thermal_spoke_width.value_or( 0 ) );
451 thermalSettings->set_gap( CopperLayer( ALL_LAYERS ).thermal_gap.value_or( 0 ) );
452 thermalSettings->mutable_angle()->set_value_degrees( ThermalSpokeAngle( F_Cu ).AsDegrees() );
453
454 padstack.set_unconnected_layer_removal(
455 ToProtoEnum<UNCONNECTED_LAYER_MODE, UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
456
457 auto packOptional =
458 []<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
459 ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
460 {
461 if( aVal.has_value() )
462 return *aVal ? aTrueVal : aFalseVal;
463
464 return aNullVal;
465 };
466
467 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
468 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
469
470 frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask,
471 SMM_MASKED, SMM_UNMASKED,
472 SMM_FROM_DESIGN_RULES ) );
473
474 backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask,
475 SMM_MASKED, SMM_UNMASKED,
476 SMM_FROM_DESIGN_RULES ) );
477
478 frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste,
479 SPM_PASTE, SPM_NO_PASTE,
480 SPM_FROM_DESIGN_RULES ) );
481
482 backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste,
483 SPM_PASTE, SPM_NO_PASTE,
484 SPM_FROM_DESIGN_RULES ) );
485
486 if( FrontOuterLayers().solder_mask_margin.has_value() )
487 {
488 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
490 }
491
492 if( BackOuterLayers().solder_mask_margin.has_value() )
493 {
494 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
496 }
497
498 if( FrontOuterLayers().solder_paste_margin.has_value() )
499 {
500 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
502 }
503
504 if( BackOuterLayers().solder_paste_margin.has_value() )
505 {
506 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
508 }
509
511 {
512 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
514 }
515
517 {
518 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
520 }
521
522 aContainer.PackFrom( padstack );
523}
524
525
526int PADSTACK::Compare( const PADSTACK* aPadstackRef, const PADSTACK* aPadstackCmp )
527{
528 int diff;
529
530 auto compareCopperProps =
531 [&]( PCB_LAYER_ID aLayer )
532 {
533 if( ( diff = static_cast<int>( aPadstackRef->Shape( aLayer ) ) -
534 static_cast<int>( aPadstackCmp->Shape( aLayer ) ) ) != 0 )
535 return diff;
536
537 if( ( diff = aPadstackRef->Size( aLayer ).x - aPadstackCmp->Size( aLayer ).x ) != 0 )
538 return diff;
539
540 if( ( diff = aPadstackRef->Size( aLayer ).y - aPadstackCmp->Size( aLayer ).y ) != 0 )
541 return diff;
542
543 if( ( diff = aPadstackRef->Offset( aLayer ).x
544 - aPadstackCmp->Offset( aLayer ).x ) != 0 )
545 return diff;
546
547 if( ( diff = aPadstackRef->Offset( aLayer ).y
548 - aPadstackCmp->Offset( aLayer ).y ) != 0 )
549 return diff;
550
551 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).x
552 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).x )
553 != 0 )
554 {
555 return diff;
556 }
557
558 if( ( diff = aPadstackRef->TrapezoidDeltaSize( aLayer ).y
559 - aPadstackCmp->TrapezoidDeltaSize( aLayer ).y )
560 != 0 )
561 {
562 return diff;
563 }
564
565 if( ( diff = aPadstackRef->RoundRectRadiusRatio( aLayer )
566 - aPadstackCmp->RoundRectRadiusRatio( aLayer ) )
567 != 0 )
568 {
569 return diff;
570 }
571
572 if( ( diff = aPadstackRef->ChamferPositions( aLayer )
573 - aPadstackCmp->ChamferPositions( aLayer ) ) != 0 )
574 return diff;
575
576 if( ( diff = aPadstackRef->ChamferRatio( aLayer )
577 - aPadstackCmp->ChamferRatio( aLayer ) ) != 0 )
578 return diff;
579
580 if( ( diff = static_cast<int>( aPadstackRef->Primitives( aLayer ).size() ) -
581 static_cast<int>( aPadstackCmp->Primitives( aLayer ).size() ) ) != 0 )
582 return diff;
583
584 // @todo: Compare custom pad primitives for pads that have the same number of primitives
585 // here. Currently there is no compare function for PCB_SHAPE objects.
586 return 0;
587 };
588
589 aPadstackRef->ForEachUniqueLayer( compareCopperProps );
590
591 if( ( diff = static_cast<int>( aPadstackRef->DrillShape() ) -
592 static_cast<int>( aPadstackCmp->DrillShape() ) ) != 0 )
593 return diff;
594
595 if( ( diff = aPadstackRef->Drill().size.x - aPadstackCmp->Drill().size.x ) != 0 )
596 return diff;
597
598 if( ( diff = aPadstackRef->Drill().size.y - aPadstackCmp->Drill().size.y ) != 0 )
599 return diff;
600
601 return aPadstackRef->LayerSet().compare( aPadstackCmp->LayerSet() );
602}
603
604
605double PADSTACK::Similarity( const PADSTACK& aOther ) const
606{
607 double similarity = 1.0;
608
610 [&]( PCB_LAYER_ID aLayer )
611 {
612 if( Shape( aLayer ) != aOther.Shape( aLayer ) )
613 similarity *= 0.9;
614
615 if( Size( aLayer ) != aOther.Size( aLayer ) )
616 similarity *= 0.9;
617
618 if( Offset( aLayer ) != aOther.Offset( aLayer ) )
619 similarity *= 0.9;
620
621 if( RoundRectRadiusRatio( aLayer ) != aOther.RoundRectRadiusRatio( aLayer ) )
622 similarity *= 0.9;
623
624 if( ChamferRatio( aLayer ) != aOther.ChamferRatio( aLayer ) )
625 similarity *= 0.9;
626
627 if( ChamferPositions( aLayer ) != aOther.ChamferPositions( aLayer ) )
628 similarity *= 0.9;
629
630 if( Primitives( aLayer ).size() != aOther.Primitives( aLayer ).size() )
631 similarity *= 0.9;
632
633 if( AnchorShape( aLayer ) != aOther.AnchorShape( aLayer ) )
634 similarity *= 0.9;
635 } );
636
637 if( Drill() != aOther.Drill() )
638 similarity *= 0.9;
639
640 if( DrillShape() != aOther.DrillShape() )
641 similarity *= 0.9;
642
643 if( GetOrientation() != aOther.GetOrientation() )
644 similarity *= 0.9;
645
646 if( ZoneConnection() != aOther.ZoneConnection() )
647 similarity *= 0.9;
648
649 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
650 similarity *= 0.9;
651
652 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
653 similarity *= 0.9;
654
655 if( ThermalGap() != aOther.ThermalGap() )
656 similarity *= 0.9;
657
659 similarity *= 0.9;
660
661 if( Clearance() != aOther.Clearance() )
662 similarity *= 0.9;
663
664 if( SolderMaskMargin() != aOther.SolderMaskMargin() )
665 similarity *= 0.9;
666
667 if( SolderPasteMargin() != aOther.SolderPasteMargin() )
668 similarity *= 0.9;
669
671 similarity *= 0.9;
672
673 if( ThermalGap() != aOther.ThermalGap() )
674 similarity *= 0.9;
675
676 if( ThermalSpokeWidth() != aOther.ThermalSpokeWidth() )
677 similarity *= 0.9;
678
679 if( ThermalSpokeAngle() != aOther.ThermalSpokeAngle() )
680 similarity *= 0.9;
681
682 if( LayerSet() != aOther.LayerSet() )
683 similarity *= 0.9;
684
685 return similarity;
686}
687
688
689wxString PADSTACK::Name() const
690{
691 // TODO
692 return wxEmptyString;
693}
694
695
697{
698 return m_drill.start;
699}
700
701
703{
704 return m_drill.end;
705}
706
707
708void PADSTACK::FlipLayers( int aCopperLayerCount )
709{
710 switch( m_mode )
711 {
712 case MODE::NORMAL:
713 // Same shape on all layers; nothing to do
714 break;
715
716 case MODE::CUSTOM:
717 {
718 if( aCopperLayerCount > 2 )
719 {
720 int innerCount = ( aCopperLayerCount - 2 );
721 int halfInnerLayerCount = innerCount / 2;
722 PCB_LAYER_ID lastInner
723 = static_cast<PCB_LAYER_ID>( In1_Cu + ( innerCount - 1 ) * 2 );
724 PCB_LAYER_ID midpointInner
725 = static_cast<PCB_LAYER_ID>( In1_Cu + ( halfInnerLayerCount - 1 ) * 2 );
726
727 for( PCB_LAYER_ID layer : LAYER_RANGE( In1_Cu, midpointInner, MAX_CU_LAYERS ) )
728 {
729 auto conjugate =
730 magic_enum::enum_cast<PCB_LAYER_ID>( lastInner - ( layer - In1_Cu ) );
731 wxCHECK2_MSG( conjugate.has_value() && m_copperProps.contains( conjugate.value() ),
732 continue, "Invalid inner layer conjugate!" );
733 std::swap( m_copperProps[layer], m_copperProps[conjugate.value()] );
734 }
735 }
736
738 }
739
741 std::swap( m_copperProps[F_Cu], m_copperProps[B_Cu] );
742 std::swap( m_frontMaskProps, m_backMaskProps );
743 break;
744 }
745}
746
747
749 shape( PAD_SHAPE::CIRCLE ),
750 anchor_shape( PAD_SHAPE::CIRCLE ),
751 round_rect_corner_radius( 0 ),
752 round_rect_radius_ratio( 0.25 ),
753 chamfered_rect_ratio( 0.2 ),
754 chamfered_rect_positions( RECT_NO_CHAMFER )
755{
756}
757
758
760{
761 return shape == aOther.shape && offset == aOther.offset
762 && round_rect_corner_radius == aOther.round_rect_corner_radius
763 && round_rect_radius_ratio == aOther.round_rect_radius_ratio
764 && chamfered_rect_ratio == aOther.chamfered_rect_ratio
765 && chamfered_rect_positions == aOther.chamfered_rect_positions;
766}
767
768
770{
771 if( shape != aOther.shape )
772 return false;
773
774 if( zone_connection != aOther.zone_connection )
775 return false;
776
777 if( thermal_spoke_width != aOther.thermal_spoke_width )
778 return false;
779
780 if( thermal_spoke_angle != aOther.thermal_spoke_angle )
781 return false;
782
783 if( thermal_gap != aOther.thermal_gap )
784 return false;
785
786 if( custom_shapes.size() != aOther.custom_shapes.size() )
787 return false;
788
789 if( !std::equal( custom_shapes.begin(), custom_shapes.end(),
790 aOther.custom_shapes.begin(), aOther.custom_shapes.end(),
791 []( const std::shared_ptr<PCB_SHAPE>& aFirst,
792 const std::shared_ptr<PCB_SHAPE>& aSecond )
793 {
794 return *aFirst == *aSecond;
795 } ) )
796 {
797 return false;
798 }
799
800 return true;
801}
802
803
805{
806 return solder_mask_margin == aOther.solder_mask_margin
807 && solder_paste_margin == aOther.solder_paste_margin
808 && solder_paste_margin_ratio == aOther.solder_paste_margin_ratio
809 && has_solder_mask == aOther.has_solder_mask
810 && has_solder_paste == aOther.has_solder_paste;
811}
812
813
815{
816 return size == aOther.size && shape == aOther.shape
817 && start == aOther.start && end == aOther.end;
818}
819
820
822{
823 if( m_mode == aMode )
824 return;
825
826 switch( aMode )
827 {
828 case MODE::NORMAL:
829 std::erase_if( m_copperProps,
830 []( const auto& aEntry )
831 {
832 const auto& [key, value] = aEntry;
833 return key != ALL_LAYERS;
834 } );
835 break;
836
838 // When coming from normal, these layers may be missing or have junk values
839 if( m_mode == MODE::NORMAL )
840 {
843 }
844
845 break;
846
847 case MODE::CUSTOM:
848 {
849 PCB_LAYER_ID innerLayerTemplate = ( m_mode == MODE::NORMAL ) ? ALL_LAYERS : INNER_LAYERS;
850
852 m_copperProps[layer] = m_copperProps[innerLayerTemplate];
853
854 if( m_mode == MODE::NORMAL )
856
857 break;
858 }
859 }
860
861 m_mode = aMode;
862
863 // Changing mode invalidates cached shapes
864 // TODO(JE) clean this up -- maybe PADSTACK should own shape caches
865 if( PAD* parentPad = dynamic_cast<PAD*>( m_parent ) )
866 parentPad->SetDirty();
867}
868
869
870void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& aMethod ) const
871{
872 switch( Mode() )
873 {
874 case MODE::NORMAL:
875 aMethod( F_Cu );
876 break;
877
879 aMethod( F_Cu );
880 aMethod( INNER_LAYERS );
881 aMethod( B_Cu );
882 break;
883
884 case MODE::CUSTOM:
885 {
887
888 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
889 aMethod( layer );
890
891 break;
892 }
893 }
894}
895
896
897std::vector<PCB_LAYER_ID> PADSTACK::UniqueLayers() const
898{
899 switch( Mode() )
900 {
901 default:
902 case MODE::NORMAL:
903 return { F_Cu };
904
906 return { F_Cu, INNER_LAYERS, B_Cu };
907
908 case MODE::CUSTOM:
909 {
910 std::vector<PCB_LAYER_ID> layers;
912
913 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) )
914 layers.push_back( layer );
915
916 return layers;
917 }
918 }
919}
920
921
923{
924 switch( static_cast<int>( aLayer ) )
925 {
927 return F_Cu;
928
930 return Mode() == MODE::NORMAL ? F_Cu : B_Cu;
931
932 // For these, just give the front copper geometry, it doesn't matter.
934 case LAYER_PADS:
936 case LAYER_VIA_HOLES:
939 return ALL_LAYERS;
940
941 default:
942 break;
943 }
944
945 switch( Mode() )
946 {
947 case MODE::CUSTOM:
949 {
950 if( IsFrontLayer( aLayer ) )
951 return F_Cu;
952
953 if( IsBackLayer( aLayer ) )
954 return B_Cu;
955
956 wxASSERT_MSG( IsCopperLayer( aLayer ),
957 wxString::Format( wxT( "Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
958 aLayer ) );
959
961 return INNER_LAYERS;
962
963 // Custom padstack: Clamp to parent board's stackup if present
964 if( m_parent )
965 {
966 LSET boardCopper = m_parent->BoardLayerSet() & LSET::AllCuMask();
967
968 if( boardCopper.Contains( aLayer ) )
969 return aLayer;
970
971 // We're asked for an inner copper layer not present in the board. There is no right
972 // answer here, so fall back on the front shape
973 return ALL_LAYERS;
974 }
975
976 // No parent, just pass through
977 return aLayer;
978 }
979
980 case MODE::NORMAL:
981 break;
982 }
983
984 return F_Cu;
985}
986
987
989{
990 LSET ret;
991
992#ifdef DEBUG
993 if( m_parent && aOther.m_parent
994 && ( m_mode == MODE::CUSTOM || aOther.m_mode == MODE::CUSTOM ) )
995 {
996 wxASSERT_MSG( m_parent->BoardCopperLayerCount() == aOther.m_parent->BoardCopperLayerCount(),
997 wxT( "Expected both padstacks to have the same board copper layer count" ) );
998 }
999#endif
1000
1001 ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer ) { ret.set( aLayer ); } );
1002 aOther.ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer ) { ret.set( aLayer ); } );
1003
1004 return ret;
1005}
1006
1007
1009{
1010 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
1011 // Create on-demand
1012 return m_copperProps[layer];
1013}
1014
1015
1017{
1018 PCB_LAYER_ID layer = EffectiveLayerFor( aLayer );
1019
1020 wxCHECK_MSG( m_copperProps.contains( layer ), m_copperProps.at( ALL_LAYERS ),
1021 "Attempt to retrieve layer " + std::string( magic_enum::enum_name( layer ) ) + " from a "
1022 "padstack that does not contain it" );
1023
1024 return m_copperProps.at( layer );
1025}
1026
1027
1029{
1030 return CopperLayer( aLayer ).shape.shape;
1031}
1032
1033
1035{
1036 CopperLayer( aLayer ).shape.shape = aShape;
1037}
1038
1039
1040void PADSTACK::SetSize( const VECTOR2I& aSize, PCB_LAYER_ID aLayer )
1041{
1042 // File formats do not enforce that sizes are always positive, but KiCad requires it
1043 VECTOR2I& size = CopperLayer( aLayer ).shape.size;
1044 size.x = std::abs( aSize.x );
1045 size.y = std::abs( aSize.y );
1046}
1047
1048
1050{
1051 return CopperLayer( aLayer ).shape.size;
1052}
1053
1054
1056{
1057 return m_drill.shape;
1058}
1059
1060
1062{
1063 m_drill.shape = aShape;
1064}
1065
1066
1068{
1069 return CopperLayer( aLayer ).shape.offset;
1070}
1071
1072
1074{
1075 return CopperLayer( aLayer ).shape.offset;
1076}
1077
1078
1080{
1081 return CopperLayer( aLayer ).shape.anchor_shape;
1082}
1083
1084
1086{
1087 CopperLayer( aLayer ).shape.anchor_shape = aShape;
1088}
1089
1090
1092{
1093 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
1094}
1095
1096
1098{
1099 return CopperLayer( aLayer ).shape.trapezoid_delta_size;
1100}
1101
1102
1104{
1105 return CopperLayer( aLayer ).shape.round_rect_radius_ratio;
1106}
1107
1108
1110{
1111 CopperLayer( aLayer ).shape.round_rect_radius_ratio = aRatio;
1112}
1113
1114
1116{
1117 const VECTOR2I& size = Size( aLayer );
1118 return KiROUND( std::min( size.x, size.y ) * RoundRectRadiusRatio( aLayer ) );
1119}
1120
1121
1122void PADSTACK::SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer )
1123{
1124 const VECTOR2I& size = Size( aLayer );
1125 int min_r = std::min( size.x, size.y );
1126
1127 if( min_r > 0 )
1128 SetRoundRectRadiusRatio( aRadius / min_r, aLayer );
1129}
1130
1131
1133{
1134 return CopperLayer( aLayer ).shape.chamfered_rect_ratio;
1135}
1136
1137
1138void PADSTACK::SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer )
1139{
1140 CopperLayer( aLayer ).shape.chamfered_rect_ratio = aRatio;
1141}
1142
1143
1145{
1147}
1148
1149
1151{
1153}
1154
1155
1156void PADSTACK::SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer )
1157{
1158 CopperLayer( aLayer ).shape.chamfered_rect_positions = aPositions;
1159}
1160
1161
1162std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer )
1163{
1164 return CopperLayer( aLayer ).clearance;
1165}
1166
1167
1168const std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer ) const
1169{
1170 return CopperLayer( aLayer ).clearance;
1171}
1172
1173
1174std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer )
1175{
1178}
1179
1180
1181const std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer ) const
1182{
1185}
1186
1187
1188std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer )
1189{
1192}
1193
1194
1195const std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer ) const
1196{
1199
1200
1201std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer )
1202{
1205}
1206
1207
1208const std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer ) const
1209{
1212}
1213
1214
1215std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer )
1216{
1217 return CopperLayer( aLayer ).zone_connection;
1218}
1219
1220
1221const std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer ) const
1222{
1223 return CopperLayer( aLayer ).zone_connection;
1224}
1225
1226
1227std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer )
1228{
1229 return CopperLayer( aLayer ).thermal_spoke_width;
1230}
1231
1232
1233const std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer ) const
1234{
1235 return CopperLayer( aLayer ).thermal_spoke_width;
1236}
1237
1238
1239std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer )
1240{
1241 return CopperLayer( aLayer ).thermal_gap;
1242}
1243
1244
1245const std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const
1246{
1247 return CopperLayer( aLayer ).thermal_gap;
1248}
1249
1250
1252{
1253 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1254
1255 return ( defaults.shape.shape == PAD_SHAPE::CIRCLE
1256 || ( defaults.shape.shape == PAD_SHAPE::CUSTOM
1257 && defaults.shape.anchor_shape == PAD_SHAPE::CIRCLE ) )
1258 ? ANGLE_45 : ANGLE_90;
1259}
1260
1261
1263{
1264 const COPPER_LAYER_PROPS& defaults = CopperLayer( aLayer );
1265
1266 return defaults.thermal_spoke_angle.value_or( DefaultThermalSpokeAngleForShape( aLayer ) );
1267}
1268
1269
1271{
1272 CopperLayer( aLayer ).thermal_spoke_angle = aAngle;
1273}
1274
1275
1276std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer )
1277{
1278 return CopperLayer( aLayer ).custom_shapes;
1279}
1280
1281
1282const std::vector<std::shared_ptr<PCB_SHAPE>>& PADSTACK::Primitives( PCB_LAYER_ID aLayer ) const
1283{
1284 return CopperLayer( aLayer ).custom_shapes;
1285}
1286
1287
1289{
1290 aShape->SetParent( m_parent );
1291 CopperLayer( aLayer ).custom_shapes.emplace_back( aShape );
1292}
1293
1294
1295void PADSTACK::AppendPrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1296 PCB_LAYER_ID aLayer )
1297{
1298 for( const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1299 AddPrimitive( new PCB_SHAPE( *prim ), aLayer );
1300}
1301
1302
1303void PADSTACK::ReplacePrimitives( const std::vector<std::shared_ptr<PCB_SHAPE>>& aPrimitivesList,
1304 PCB_LAYER_ID aLayer )
1305{
1306 ClearPrimitives( aLayer );
1307
1308 if( aPrimitivesList.size() )
1309 AppendPrimitives( aPrimitivesList, aLayer );
1310}
1311
1312
1314{
1315 CopperLayer( aLayer ).custom_shapes.clear();
1316}
1317
1318
1319std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
1320{
1321 if( IsFrontLayer( aSide ) )
1323
1324 if( IsBackLayer( aSide ) )
1326
1327 wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" );
1328}
1329
1330
@ 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:206
BASE_SET & set(size_t pos)
Definition: base_set.h:115
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
Definition: board_item.cpp:128
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Definition: board_item.cpp:117
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
double AsDegrees() const
Definition: eda_angle.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:60
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition: padstack.h:124
bool operator==(const PADSTACK &aOther) const
Definition: padstack.cpp:115
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const
Definition: padstack.h:326
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1162
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
Definition: padstack.cpp:1288
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:1303
void packCopperLayer(PCB_LAYER_ID aLayer, kiapi::board::types::PadStack &aProto) const
Definition: padstack.cpp:381
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: padstack.cpp:211
MASK_LAYER_PROPS & FrontOuterLayers()
Definition: padstack.h:312
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
Definition: padstack.cpp:605
int RoundRectRadius(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1115
void SetDrillShape(PAD_DRILL_SHAPE aShape)
Definition: padstack.cpp:1061
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1201
wxString m_customName
! An override for the IPC-7351 padstack name
Definition: padstack.h:460
DRILL_PROPS m_drill
! The primary drill parameters, which also define the start and end layers for through-hole vias and ...
Definition: padstack.h:486
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
Definition: padstack.cpp:870
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1270
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1109
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
Definition: padstack.h:475
void ClearPrimitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1313
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition: padstack.h:307
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
Definition: padstack.cpp:1319
void SetMode(MODE aMode)
Definition: padstack.cpp:821
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1227
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1188
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1174
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1138
const LSET & LayerSet() const
Definition: padstack.h:274
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
Definition: padstack.cpp:922
PADSTACK & operator=(const PADSTACK &aOther)
Definition: padstack.cpp:71
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1034
EDA_ANGLE DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1251
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1091
DRILL_PROPS m_secondaryDrill
! Secondary drill, used to define back-drilling
Definition: padstack.h:489
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1067
MASK_LAYER_PROPS m_backMaskProps
! The overrides applied to back outer technical layers
Definition: padstack.h:473
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1008
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
Definition: padstack.cpp:1262
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:708
CUSTOM_SHAPE_ZONE_MODE m_customShapeInZoneMode
How to build the custom shape in zone, to create the clearance area: CUSTOM_SHAPE_ZONE_MODE::OUTLINE ...
Definition: padstack.h:482
bool unpackCopperLayer(const kiapi::board::types::PadStackLayer &aProto)
Definition: padstack.cpp:160
PAD_DRILL_SHAPE DrillShape() const
Definition: padstack.cpp:1055
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1156
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1122
LSET RelevantShapeLayers(const PADSTACK &aOther) const
Returns the set of layers that must be considered if checking one padstack against another.
Definition: padstack.cpp:988
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1239
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1028
DRILL_PROPS & Drill()
Definition: padstack.h:300
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1085
BOARD_ITEM * m_parent
! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes
Definition: padstack.h:451
PADSTACK(BOARD_ITEM *aParent)
Definition: padstack.cpp:34
std::vector< PCB_LAYER_ID > UniqueLayers() const
Definition: padstack.cpp:897
LSET m_layerSet
! The board layers that this padstack is active on
Definition: padstack.h:457
std::unordered_map< PCB_LAYER_ID, COPPER_LAYER_PROPS > m_copperProps
! The properties applied to copper layers if they aren't overridden
Definition: padstack.h:467
CUSTOM_SHAPE_ZONE_MODE
Definition: padstack.h:158
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
Definition: padstack.cpp:526
PCB_LAYER_ID EndLayer() const
Definition: padstack.cpp:702
int & ChamferPositions(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1144
MASK_LAYER_PROPS m_frontMaskProps
! The overrides applied to front outer technical layers
Definition: padstack.h:470
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1049
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:1295
double RoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1103
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:696
MODE Mode() const
Definition: padstack.h:287
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1079
MASK_LAYER_PROPS & BackOuterLayers()
Definition: padstack.h:315
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: padstack.cpp:422
wxString Name() const
! Returns the name of this padstack in IPC-7351 format
Definition: padstack.cpp:689
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1040
double ChamferRatio(PCB_LAYER_ID aLayer) const
Definition: padstack.cpp:1132
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
EDA_ANGLE GetOrientation() const
Definition: padstack.h:293
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
Definition: padstack.h:147
void SetLayerSet(const LSET &aSet)
Definition: padstack.h:276
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
Definition: padstack.cpp:1215
MODE m_mode
! The copper layer variation mode this padstack is in
Definition: padstack.h:454
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
Definition: padstack.h:463
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
Definition: padstack.cpp:1276
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
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:621
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:644
#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:531
@ LAYER_VIA_HOLEWALLS
Definition: layer_ids.h:235
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored)
Definition: layer_ids.h:231
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition: layer_ids.h:215
@ LAYER_VIA_HOLES
to draw via holes (pad holes do not use this layer)
Definition: layer_ids.h:216
@ LAYER_PAD_HOLEWALLS
Definition: layer_ids.h:234
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
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)
VECTOR2I UnpackVector2(const types::Vector2 &aInput)
Definition: api_utils.cpp:76
void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
Definition: api_utils.cpp:69
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:763
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:769
std::optional< EDA_ANGLE > thermal_spoke_angle
Definition: padstack.h:211
std::optional< int > clearance
Definition: padstack.h:213
std::optional< int > thermal_gap
Definition: padstack.h:212
! The properties of a padstack drill. Drill position is always the pad position (origin).
Definition: padstack.h:239
PCB_LAYER_ID start
Definition: padstack.h:242
PCB_LAYER_ID end
Definition: padstack.h:243
bool operator==(const DRILL_PROPS &aOther) const
Definition: padstack.cpp:814
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition: padstack.h:240
PAD_DRILL_SHAPE shape
Definition: padstack.h:241
! The features of a padstack that can vary on outer layers.
Definition: padstack.h:227
bool operator==(const MASK_LAYER_PROPS &aOther) const
Definition: padstack.cpp:804
std::optional< int > solder_mask_margin
Definition: padstack.h:228
std::optional< int > solder_paste_margin
Definition: padstack.h:229
std::optional< double > solder_paste_margin_ratio
Definition: padstack.h:230
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
Definition: padstack.h:231
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
Definition: padstack.h:232
! The set of properties that define a pad's shape on a given layer
Definition: padstack.h:165
VECTOR2I trapezoid_delta_size
Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the other.
Definition: padstack.h:188
double round_rect_corner_radius
Definition: padstack.h:179
VECTOR2I offset
Offset of the shape center from the pad center.
Definition: padstack.h:177
bool operator==(const SHAPE_PROPS &aOther) const
Definition: padstack.cpp:759
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