26#include <api/board/board_types.pb.h>
29#include <magic_enum.hpp>
65 for( std::shared_ptr<PCB_SHAPE>& shape :
CopperLayer( aLayer ).custom_shapes )
102 if( shape == PAD_SHAPE::CUSTOM )
108 SetShape( PAD_SHAPE::RECTANGLE, aLayer );
147 bool copperMatches =
true;
153 copperMatches =
false;
156 return copperMatches;
163 PCB_LAYER_ID layer = FromProtoEnum<PCB_LAYER_ID, BoardLayer>( aProto.layer() );
172 SetShape( FromProtoEnum<PAD_SHAPE>( aProto.shape() ), layer );
174 SetAnchorShape( FromProtoEnum<PAD_SHAPE>( aProto.custom_anchor_shape() ), layer );
180 if(
Shape( layer ) == PAD_SHAPE::TRAPEZOID && aProto.has_trapezoid_delta() )
183 if( aProto.chamfered_corners().top_left() )
186 if( aProto.chamfered_corners().top_right() )
189 if( aProto.chamfered_corners().bottom_left() )
192 if( aProto.chamfered_corners().bottom_right() )
196 google::protobuf::Any a;
198 for(
const GraphicShape& shapeProto : aProto.custom_shapes() )
200 a.PackFrom( shapeProto );
201 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>(
m_parent );
203 if( shape->Deserialize( a ) )
216 if( !aContainer.UnpackTo( &padstack ) )
219 m_mode = FromProtoEnum<MODE>( padstack.type() );
224 Drill().
start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
225 Drill().
end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
227 for(
const PadStackLayer& layer : padstack.copper_layers() )
233 if( padstack.has_zone_settings() )
236 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
238 if( padstack.zone_settings().has_thermal_spokes() )
240 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
256 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
259 [](
const SolderMaskMode& aProto, std::optional<bool>& aDest )
263 case kiapi::board::types::SMM_MASKED:
267 case kiapi::board::types::SMM_UNMASKED:
272 case kiapi::board::types::SMM_FROM_DESIGN_RULES:
273 aDest = std::nullopt;
278 unpackMask( padstack.front_outer_layers().solder_mask_mode(),
281 unpackMask( padstack.back_outer_layers().solder_mask_mode(),
285 [](
const SolderPasteMode& aProto, std::optional<bool>& aDest )
289 case kiapi::board::types::SPM_PASTE:
293 case kiapi::board::types::SPM_NO_PASTE:
298 case kiapi::board::types::SPM_FROM_DESIGN_RULES:
299 aDest = std::nullopt;
304 unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
307 unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
310 if( padstack.front_outer_layers().has_solder_mask_settings()
311 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
314 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
321 if( padstack.back_outer_layers().has_solder_mask_settings()
322 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
325 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
332 if( padstack.front_outer_layers().has_solder_paste_settings()
333 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
336 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
343 if( padstack.back_outer_layers().has_solder_paste_settings()
344 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
347 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
354 if( padstack.front_outer_layers().has_solder_paste_settings()
355 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
358 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
365 if( padstack.back_outer_layers().has_solder_paste_settings()
366 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
369 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
385 PadStackLayer* stackLayer = aProto.add_copper_layers();
387 stackLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
391 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>(
Shape( aLayer ) ) );
393 stackLayer->set_custom_anchor_shape(
394 ToProtoEnum<PAD_SHAPE, PadStackShape>(
AnchorShape( aLayer ) ) );
396 stackLayer->set_chamfer_ratio(
CopperLayer( aLayer ).shape.chamfered_rect_ratio );
397 stackLayer->set_corner_rounding_ratio(
CopperLayer( aLayer ).shape.round_rect_radius_ratio );
399 if(
Shape( aLayer ) == PAD_SHAPE::TRAPEZOID )
405 google::protobuf::Any a;
407 for(
const std::shared_ptr<PCB_SHAPE>& shape :
Primitives( aLayer ) )
409 shape->Serialize( a );
410 GraphicShape* s = stackLayer->add_custom_shapes();
427 padstack.set_type( ToProtoEnum<MODE, PadStackType>(
m_mode ) );
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() ) );
441 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
442 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
446 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
454 padstack.set_unconnected_layer_removal(
458 []<
typename ProtoEnum>(
const std::optional<bool>& aVal, ProtoEnum aTrueVal,
459 ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
461 if( aVal.has_value() )
462 return *aVal ? aTrueVal : aFalseVal;
467 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
468 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
471 SMM_MASKED, SMM_UNMASKED,
472 SMM_FROM_DESIGN_RULES ) );
475 SMM_MASKED, SMM_UNMASKED,
476 SMM_FROM_DESIGN_RULES ) );
479 SPM_PASTE, SPM_NO_PASTE,
480 SPM_FROM_DESIGN_RULES ) );
483 SPM_PASTE, SPM_NO_PASTE,
484 SPM_FROM_DESIGN_RULES ) );
488 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
494 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
500 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
506 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
512 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
518 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
522 aContainer.PackFrom( padstack );
530 auto compareCopperProps =
533 if( ( diff =
static_cast<int>( aPadstackRef->
Shape( aLayer ) ) -
534 static_cast<int>( aPadstackCmp->
Shape( aLayer ) ) ) != 0 )
537 if( ( diff = aPadstackRef->
Size( aLayer ).
x - aPadstackCmp->
Size( aLayer ).
x ) != 0 )
540 if( ( diff = aPadstackRef->
Size( aLayer ).
y - aPadstackCmp->
Size( aLayer ).
y ) != 0 )
543 if( ( diff = aPadstackRef->
Offset( aLayer ).
x
544 - aPadstackCmp->
Offset( aLayer ).
x ) != 0 )
547 if( ( diff = aPadstackRef->
Offset( aLayer ).
y
548 - aPadstackCmp->
Offset( aLayer ).
y ) != 0 )
580 if( ( diff =
static_cast<int>( aPadstackRef->
Primitives( aLayer ).size() ) -
581 static_cast<int>( aPadstackCmp->
Primitives( aLayer ).size() ) ) != 0 )
591 if( ( diff =
static_cast<int>( aPadstackRef->
DrillShape() ) -
592 static_cast<int>( aPadstackCmp->
DrillShape() ) ) != 0 )
607 double similarity = 1.0;
612 if(
Shape( aLayer ) != aOther.
Shape( aLayer ) )
615 if(
Size( aLayer ) != aOther.
Size( aLayer ) )
692 return wxEmptyString;
718 if( aCopperLayerCount > 2 )
720 int innerCount = ( aCopperLayerCount - 2 );
721 int halfInnerLayerCount = innerCount / 2;
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!" );
751 round_rect_corner_radius( 0 ),
752 round_rect_radius_ratio( 0.25 ),
753 chamfered_rect_ratio( 0.2 ),
761 return shape == aOther.
shape && offset == aOther.
offset
771 if( shape != aOther.
shape )
789 if( !std::equal( custom_shapes.begin(), custom_shapes.end(),
791 [](
const std::shared_ptr<PCB_SHAPE>& aFirst,
792 const std::shared_ptr<PCB_SHAPE>& aSecond )
794 return *aFirst == *aSecond;
816 return size == aOther.
size && shape == aOther.
shape
817 && start == aOther.
start && end == aOther.
end;
830 [](
const auto& aEntry )
832 const auto& [key, value] = aEntry;
866 parentPad->SetDirty();
910 std::vector<PCB_LAYER_ID> layers;
914 layers.push_back( layer );
924 switch(
static_cast<int>( aLayer ) )
957 wxString::Format( wxT(
"Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
968 if( boardCopper.
Contains( aLayer ) )
997 wxT(
"Expected both padstacks to have the same board copper layer count" ) );
1021 "Attempt to retrieve layer " + std::string( magic_enum::enum_name( layer ) ) +
" from a "
1022 "padstack that does not contain it" );
1125 int min_r = std::min( size.
x, size.
y );
1255 return ( defaults.
shape.
shape == PAD_SHAPE::CIRCLE
1256 || ( defaults.
shape.
shape == PAD_SHAPE::CUSTOM
1298 for(
const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1308 if( aPrimitivesList.size() )
1327 wxCHECK_MSG(
false, std::nullopt,
"IsTented expects a front or back layer" );
@ NORMAL
Use all material properties from model file.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
int compare(const BASE_SET &other) const
BASE_SET & set(size_t pos)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual LSET BoardLayerSet() const
Return the LSET for the board that this item resides on.
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Represent basic circle geometry with utility geometry functions.
virtual void SetParent(EDA_ITEM *aParent)
LSET is a set of PCB_LAYER_IDs.
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
bool operator==(const PADSTACK &aOther) const
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const
std::optional< int > & Clearance(PCB_LAYER_ID aLayer=F_Cu)
void AddPrimitive(PCB_SHAPE *aShape, PCB_LAYER_ID aLayer)
Adds a custom shape primitive to the padstack.
void ReplacePrimitives(const std::vector< std::shared_ptr< PCB_SHAPE > > &aPrimitivesList, PCB_LAYER_ID aLayer)
Clears the existing primitive list (freeing the owned shapes) and adds copies of the given shapes to ...
void packCopperLayer(PCB_LAYER_ID aLayer, kiapi::board::types::PadStack &aProto) const
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
MASK_LAYER_PROPS & FrontOuterLayers()
double Similarity(const PADSTACK &aOther) const
Return a measure of how likely the other object is to represent the same object.
int RoundRectRadius(PCB_LAYER_ID aLayer) const
void SetDrillShape(PAD_DRILL_SHAPE aShape)
std::optional< double > & SolderPasteMarginRatio(PCB_LAYER_ID aLayer=F_Cu)
wxString m_customName
! An override for the IPC-7351 padstack name
DRILL_PROPS m_drill
! The primary drill parameters, which also define the start and end layers for through-hole vias and ...
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
void SetThermalSpokeAngle(EDA_ANGLE aAngle, PCB_LAYER_ID aLayer=F_Cu)
void SetRoundRectRadiusRatio(double aRatio, PCB_LAYER_ID aLayer)
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode
void ClearPrimitives(PCB_LAYER_ID aLayer)
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
std::optional< bool > IsTented(PCB_LAYER_ID aSide) const
Checks if this padstack is tented (covered in soldermask) on the given side.
std::optional< int > & ThermalSpokeWidth(PCB_LAYER_ID aLayer=F_Cu)
std::optional< int > & SolderPasteMargin(PCB_LAYER_ID aLayer=F_Cu)
std::optional< int > & SolderMaskMargin(PCB_LAYER_ID aLayer=F_Cu)
void SetChamferRatio(double aRatio, PCB_LAYER_ID aLayer)
const LSET & LayerSet() const
PCB_LAYER_ID EffectiveLayerFor(PCB_LAYER_ID aLayer) const
Determines which geometry layer should be used for the given input layer.
PADSTACK & operator=(const PADSTACK &aOther)
void SetShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
EDA_ANGLE DefaultThermalSpokeAngleForShape(PCB_LAYER_ID aLayer=F_Cu) const
VECTOR2I & TrapezoidDeltaSize(PCB_LAYER_ID aLayer)
DRILL_PROPS m_secondaryDrill
! Secondary drill, used to define back-drilling
VECTOR2I & Offset(PCB_LAYER_ID aLayer)
MASK_LAYER_PROPS m_backMaskProps
! The overrides applied to back outer technical layers
COPPER_LAYER_PROPS & CopperLayer(PCB_LAYER_ID aLayer)
EDA_ANGLE ThermalSpokeAngle(PCB_LAYER_ID aLayer=F_Cu) const
void FlipLayers(int aCopperLayerCount)
Flips the padstack layers in the case that the pad's parent footprint is flipped to the other side of...
CUSTOM_SHAPE_ZONE_MODE m_customShapeInZoneMode
How to build the custom shape in zone, to create the clearance area: CUSTOM_SHAPE_ZONE_MODE::OUTLINE ...
bool unpackCopperLayer(const kiapi::board::types::PadStackLayer &aProto)
PAD_DRILL_SHAPE DrillShape() const
void SetChamferPositions(int aPositions, PCB_LAYER_ID aLayer)
void SetRoundRectRadius(double aRadius, PCB_LAYER_ID aLayer)
LSET RelevantShapeLayers(const PADSTACK &aOther) const
Returns the set of layers that must be considered if checking one padstack against another.
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
PAD_SHAPE Shape(PCB_LAYER_ID aLayer) const
void SetAnchorShape(PAD_SHAPE aShape, PCB_LAYER_ID aLayer)
BOARD_ITEM * m_parent
! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes
PADSTACK(BOARD_ITEM *aParent)
std::vector< PCB_LAYER_ID > UniqueLayers() const
LSET m_layerSet
! The board layers that this padstack is active on
std::unordered_map< PCB_LAYER_ID, COPPER_LAYER_PROPS > m_copperProps
! The properties applied to copper layers if they aren't overridden
static int Compare(const PADSTACK *aPadstackRef, const PADSTACK *aPadstackCmp)
Compare two padstacks and return 0 if they are equal.
PCB_LAYER_ID EndLayer() const
int & ChamferPositions(PCB_LAYER_ID aLayer)
MASK_LAYER_PROPS m_frontMaskProps
! The overrides applied to front outer technical layers
const VECTOR2I & Size(PCB_LAYER_ID aLayer) const
MODE
! Copper geometry mode: controls how many unique copper layer shapes this padstack has
@ 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.
double RoundRectRadiusRatio(PCB_LAYER_ID aLayer) const
UNCONNECTED_LAYER_MODE
! Whether or not to remove the copper shape for unconnected layers
PCB_LAYER_ID StartLayer() const
PAD_SHAPE AnchorShape(PCB_LAYER_ID aLayer) const
MASK_LAYER_PROPS & BackOuterLayers()
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
wxString Name() const
! Returns the name of this padstack in IPC-7351 format
void SetSize(const VECTOR2I &aSize, PCB_LAYER_ID aLayer)
double ChamferRatio(PCB_LAYER_ID aLayer) const
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
EDA_ANGLE GetOrientation() const
static constexpr PCB_LAYER_ID INNER_LAYERS
! The layer identifier to use for "inner layers" on top/inner/bottom padstacks
void SetLayerSet(const LSET &aSet)
std::optional< ZONE_CONNECTION > & ZoneConnection(PCB_LAYER_ID aLayer=F_Cu)
MODE m_mode
! The copper layer variation mode this padstack is in
EDA_ANGLE m_orientation
! The rotation of the pad relative to an outer reference frame
std::vector< std::shared_ptr< PCB_SHAPE > > & Primitives(PCB_LAYER_ID aLayer)
@ RECT_CHAMFER_BOTTOM_RIGHT
@ RECT_CHAMFER_BOTTOM_LEFT
static constexpr EDA_ANGLE ANGLE_0
static constexpr EDA_ANGLE ANGLE_90
static constexpr EDA_ANGLE ANGLE_45
@ LAYER_PAD_FR_NETNAMES
Additional netnames layers (not associated with a PCB layer)
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored)
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
@ LAYER_VIA_HOLES
to draw via holes (pad holes do not use this layer)
PCB_LAYER_ID
A quick note on layer IDs:
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 ...
void PackLayerSet(google::protobuf::RepeatedField< int > &aOutput, const LSET &aLayerSet)
LSET UnpackLayerSet(const google::protobuf::RepeatedField< int > &aProtoLayerSet)
VECTOR2I UnpackVector2(const types::Vector2 &aInput)
void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
#define IMPLEMENT_ENUM_TO_WXANY(type)
The features of a padstack that can vary between copper layers All parameters are optional; leaving t...
std::optional< ZONE_CONNECTION > zone_connection
std::optional< int > thermal_spoke_width
std::vector< std::shared_ptr< PCB_SHAPE > > custom_shapes
bool operator==(const COPPER_LAYER_PROPS &aOther) const
std::optional< EDA_ANGLE > thermal_spoke_angle
std::optional< int > clearance
std::optional< int > thermal_gap
! The properties of a padstack drill. Drill position is always the pad position (origin).
bool operator==(const DRILL_PROPS &aOther) const
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
! The features of a padstack that can vary on outer layers.
bool operator==(const MASK_LAYER_PROPS &aOther) const
std::optional< int > solder_mask_margin
std::optional< int > solder_paste_margin
std::optional< double > solder_paste_margin_ratio
std::optional< bool > has_solder_mask
True if this outer layer has mask (is not tented)
std::optional< bool > has_solder_paste
True if this outer layer has solder paste.
! The set of properties that define a pad's shape on a given layer
int chamfered_rect_positions
VECTOR2I trapezoid_delta_size
Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the other.
double round_rect_corner_radius
VECTOR2I offset
Offset of the shape center from the pad center.
bool operator==(const SHAPE_PROPS &aOther) const
VECTOR2I size
Size of the shape, or of the anchor pad for custom shape pads.
double chamfered_rect_ratio
Size of chamfer: ratio of smallest of X,Y size.
double round_rect_radius_ratio
PAD_SHAPE shape
Shape of the pad.
PAD_SHAPE anchor_shape
Shape of the anchor when shape == PAD_SHAPE::CUSTOM.