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