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 BoardGraphicShape& 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() )
236 if( padstack.has_zone_settings() )
239 FromProtoEnum<ZONE_CONNECTION>( padstack.zone_settings().zone_connection() );
241 if( padstack.zone_settings().has_thermal_spokes() )
243 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
245 if( thermals.has_gap() )
248 if( thermals.has_width() )
261 FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
264 [](
const SolderMaskMode& aProto, std::optional<bool>& aDest )
268 case kiapi::board::types::SMM_MASKED:
272 case kiapi::board::types::SMM_UNMASKED:
277 case kiapi::board::types::SMM_FROM_DESIGN_RULES:
278 aDest = std::nullopt;
283 unpackMask( padstack.front_outer_layers().solder_mask_mode(),
286 unpackMask( padstack.back_outer_layers().solder_mask_mode(),
290 [](
const SolderPasteMode& aProto, std::optional<bool>& aDest )
294 case kiapi::board::types::SPM_PASTE:
298 case kiapi::board::types::SPM_NO_PASTE:
303 case kiapi::board::types::SPM_FROM_DESIGN_RULES:
304 aDest = std::nullopt;
309 unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
312 unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
315 if( padstack.front_outer_layers().has_solder_mask_settings()
316 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
319 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
326 if( padstack.back_outer_layers().has_solder_mask_settings()
327 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
330 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
337 if( padstack.front_outer_layers().has_solder_paste_settings()
338 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
341 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
348 if( padstack.back_outer_layers().has_solder_paste_settings()
349 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
352 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
359 if( padstack.front_outer_layers().has_solder_paste_settings()
360 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
363 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
370 if( padstack.back_outer_layers().has_solder_paste_settings()
371 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
374 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
390 PadStackLayer* stackLayer = aProto.add_copper_layers();
392 stackLayer->set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( aLayer ) );
396 stackLayer->set_shape( ToProtoEnum<PAD_SHAPE, PadStackShape>(
Shape( aLayer ) ) );
398 stackLayer->set_custom_anchor_shape(
399 ToProtoEnum<PAD_SHAPE, PadStackShape>(
AnchorShape( aLayer ) ) );
401 stackLayer->set_chamfer_ratio(
CopperLayer( aLayer ).shape.chamfered_rect_ratio );
402 stackLayer->set_corner_rounding_ratio(
CopperLayer( aLayer ).shape.round_rect_radius_ratio );
404 if(
Shape( aLayer ) == PAD_SHAPE::TRAPEZOID )
410 google::protobuf::Any a;
412 for(
const std::shared_ptr<PCB_SHAPE>& shape :
Primitives( aLayer ) )
414 shape->Serialize( a );
415 BoardGraphicShape* s = stackLayer->add_custom_shapes();
432 padstack.set_type( ToProtoEnum<MODE, PadStackType>(
m_mode ) );
436 DrillProperties* drill = padstack.mutable_drill();
437 drill->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>(
StartLayer() ) );
438 drill->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>(
EndLayer() ) );
446 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
447 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
451 zoneSettings->set_zone_connection( ToProtoEnum<ZONE_CONNECTION, ZoneConnectionStyle>(
456 thermalSettings->mutable_width()->set_value_nm( *width );
459 thermalSettings->mutable_gap()->set_value_nm( *gap );
463 padstack.set_unconnected_layer_removal(
467 []<
typename ProtoEnum>(
const std::optional<bool>& aVal, ProtoEnum aTrueVal,
468 ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
470 if( aVal.has_value() )
471 return *aVal ? aTrueVal : aFalseVal;
476 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
477 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
480 SMM_MASKED, SMM_UNMASKED,
481 SMM_FROM_DESIGN_RULES ) );
484 SMM_MASKED, SMM_UNMASKED,
485 SMM_FROM_DESIGN_RULES ) );
488 SPM_PASTE, SPM_NO_PASTE,
489 SPM_FROM_DESIGN_RULES ) );
492 SPM_PASTE, SPM_NO_PASTE,
493 SPM_FROM_DESIGN_RULES ) );
497 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
503 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
509 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
515 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
521 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
527 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
531 aContainer.PackFrom( padstack );
539 auto compareCopperProps =
542 if( ( diff =
static_cast<int>( aPadstackRef->
Shape( aLayer ) ) -
543 static_cast<int>( aPadstackCmp->
Shape( aLayer ) ) ) != 0 )
546 if( ( diff = aPadstackRef->
Size( aLayer ).
x - aPadstackCmp->
Size( aLayer ).
x ) != 0 )
549 if( ( diff = aPadstackRef->
Size( aLayer ).
y - aPadstackCmp->
Size( aLayer ).
y ) != 0 )
552 if( ( diff = aPadstackRef->
Offset( aLayer ).
x
553 - aPadstackCmp->
Offset( aLayer ).
x ) != 0 )
556 if( ( diff = aPadstackRef->
Offset( aLayer ).
y
557 - aPadstackCmp->
Offset( aLayer ).
y ) != 0 )
589 if( ( diff =
static_cast<int>( aPadstackRef->
Primitives( aLayer ).size() ) -
590 static_cast<int>( aPadstackCmp->
Primitives( aLayer ).size() ) ) != 0 )
600 if( ( diff =
static_cast<int>( aPadstackRef->
DrillShape() ) -
601 static_cast<int>( aPadstackCmp->
DrillShape() ) ) != 0 )
616 double similarity = 1.0;
621 if(
Shape( aLayer ) != aOther.
Shape( aLayer ) )
624 if(
Size( aLayer ) != aOther.
Size( aLayer ) )
701 return wxEmptyString;
727 if( aCopperLayerCount > 2 )
729 int innerCount = ( aCopperLayerCount - 2 );
730 int halfInnerLayerCount = innerCount / 2;
739 magic_enum::enum_cast<PCB_LAYER_ID>( lastInner - ( layer -
In1_Cu ) );
740 wxCHECK2_MSG( conjugate.has_value() &&
m_copperProps.contains( conjugate.value() ),
741 continue,
"Invalid inner layer conjugate!" );
760 round_rect_corner_radius( 0 ),
761 round_rect_radius_ratio( 0.25 ),
762 chamfered_rect_ratio( 0.2 ),
770 return shape == aOther.
shape && offset == aOther.
offset
780 if( shape != aOther.
shape )
798 if( !std::equal( custom_shapes.begin(), custom_shapes.end(),
800 [](
const std::shared_ptr<PCB_SHAPE>& aFirst,
801 const std::shared_ptr<PCB_SHAPE>& aSecond )
803 return *aFirst == *aSecond;
825 return size == aOther.
size && shape == aOther.
shape
826 && start == aOther.
start && end == aOther.
end;
839 [](
const auto& aEntry )
841 const auto& [key, value] = aEntry;
875 parentPad->SetDirty();
919 std::vector<PCB_LAYER_ID> layers;
923 layers.push_back( layer );
933 switch(
static_cast<int>( aLayer ) )
976 wxString::Format( wxT(
"Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
987 if( boardCopper.
Contains( boardCuLayer ) )
1016 wxT(
"Expected both padstacks to have the same board copper layer count" ) );
1040 "Attempt to retrieve layer " + std::string( magic_enum::enum_name( layer ) ) +
" from a "
1041 "padstack that does not contain it" );
1144 int min_r = std::min( size.
x, size.
y );
1274 return ( defaults.
shape.
shape == PAD_SHAPE::CIRCLE
1275 || ( defaults.
shape.
shape == PAD_SHAPE::CUSTOM
1317 for(
const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1327 if( aPrimitivesList.size() )
1346 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 IsPadCopperLayer(int aLayer)
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 IsClearanceLayer(int aLayer)
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
@ LAYER_VIA_COPPER_START
Virtual layers for via copper on a given copper layer.
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
@ LAYER_VIA_HOLES
Draw via holes (pad holes do not use this layer).
bool IsViaCopperLayer(int aLayer)
PCB_LAYER_ID
A quick note on layer IDs:
PCB_LAYER_ID ToLAYER_ID(int aLayer)
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)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput)
KICOMMON_API 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.