26#include <api/board/board_types.pb.h>
29#include <magic_enum.hpp>
66 for( std::shared_ptr<PCB_SHAPE>& shape :
CopperLayer( aLayer ).custom_shapes )
148 bool copperMatches =
true;
154 copperMatches =
false;
157 return copperMatches;
184 if( aProto.chamfered_corners().top_left() )
187 if( aProto.chamfered_corners().top_right() )
190 if( aProto.chamfered_corners().bottom_left() )
193 if( aProto.chamfered_corners().bottom_right() )
197 google::protobuf::Any a;
199 for(
const BoardGraphicShape& shapeProto : aProto.custom_shapes() )
201 a.PackFrom( shapeProto );
202 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>(
m_parent );
204 if( shape->Deserialize( a ) )
217 auto unpackOptional = []<
typename ProtoEnum>(
const ProtoEnum& aProto,
218 std::optional<bool>& aDest, ProtoEnum aTrueValue,
219 ProtoEnum aFalseValue )
221 if( aProto == aTrueValue )
223 else if( aProto == aFalseValue )
226 aDest = std::nullopt;
229 if( !aContainer.UnpackTo( &padstack ) )
239 unpackOptional( padstack.drill().capped(),
Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
240 unpackOptional( padstack.drill().filled(),
Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
242 for(
const PadStackLayer& layer : padstack.copper_layers() )
251 if( padstack.has_zone_settings() )
256 if( padstack.zone_settings().has_thermal_spokes() )
258 const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes();
260 if( thermals.has_gap() )
263 if( thermals.has_width() )
278 unpackOptional( padstack.front_outer_layers().solder_mask_mode(),
281 unpackOptional( padstack.back_outer_layers().solder_mask_mode(),
284 unpackOptional( padstack.front_outer_layers().covering_mode(),
FrontOuterLayers().has_covering,
285 VCM_COVERED, VCM_UNCOVERED );
287 unpackOptional( padstack.back_outer_layers().covering_mode(),
BackOuterLayers().has_covering,
288 VCM_COVERED, VCM_UNCOVERED );
290 unpackOptional( padstack.front_outer_layers().plugging_mode(),
FrontOuterLayers().has_plugging,
291 VPM_PLUGGED, VPM_UNPLUGGED );
293 unpackOptional( padstack.back_outer_layers().plugging_mode(),
BackOuterLayers().has_plugging,
294 VPM_PLUGGED, VPM_UNPLUGGED );
296 unpackOptional( padstack.front_outer_layers().solder_paste_mode(),
299 unpackOptional( padstack.back_outer_layers().solder_paste_mode(),
302 if( padstack.front_outer_layers().has_solder_mask_settings()
303 && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
306 padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
313 if( padstack.back_outer_layers().has_solder_mask_settings()
314 && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() )
317 padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm();
324 if( padstack.front_outer_layers().has_solder_paste_settings()
325 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() )
328 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
335 if( padstack.back_outer_layers().has_solder_paste_settings()
336 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() )
339 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm();
346 if( padstack.front_outer_layers().has_solder_paste_settings()
347 && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
350 padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
357 if( padstack.back_outer_layers().has_solder_paste_settings()
358 && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() )
361 padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value();
377 PadStackLayer* stackLayer = aProto.add_copper_layers();
385 stackLayer->set_custom_anchor_shape(
388 stackLayer->set_chamfer_ratio(
CopperLayer( aLayer ).shape.chamfered_rect_ratio );
389 stackLayer->set_corner_rounding_ratio(
CopperLayer( aLayer ).shape.round_rect_radius_ratio );
397 google::protobuf::Any a;
399 for(
const std::shared_ptr<PCB_SHAPE>& shape :
Primitives( aLayer ) )
401 shape->Serialize( a );
402 BoardGraphicShape* s = stackLayer->add_custom_shapes();
419 auto packOptional = []<
typename ProtoEnum>(
const std::optional<bool>& aVal, ProtoEnum aTrueVal,
421 ProtoEnum aNullVal ) -> ProtoEnum
423 if( aVal.has_value() )
424 return *aVal ? aTrueVal : aFalseVal;
431 padstack.mutable_angle()->set_value_degrees(
m_orientation.AsDegrees() );
433 DrillProperties* drill = padstack.mutable_drill();
437 packOptional(
Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED, VDFM_FROM_DESIGN_RULES ) );
440 packOptional(
Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED, VDCM_FROM_DESIGN_RULES ) );
449 ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings();
450 ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes();
459 thermalSettings->mutable_width()->set_value_nm( *width );
462 thermalSettings->mutable_gap()->set_value_nm( *gap );
466 padstack.set_unconnected_layer_removal(
469 PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
470 PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
472 frontOuter->set_solder_mask_mode( packOptional(
FrontOuterLayers().has_solder_mask, SMM_MASKED,
473 SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
475 backOuter->set_solder_mask_mode( packOptional(
BackOuterLayers().has_solder_mask, SMM_MASKED,
476 SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
478 frontOuter->set_plugging_mode( packOptional(
FrontOuterLayers().has_plugging, VPM_PLUGGED,
479 VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
481 backOuter->set_plugging_mode( packOptional(
BackOuterLayers().has_plugging, VPM_PLUGGED,
482 VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
484 frontOuter->set_covering_mode( packOptional(
FrontOuterLayers().has_covering, VCM_COVERED,
485 VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
487 backOuter->set_covering_mode( packOptional(
BackOuterLayers().has_covering, VCM_COVERED,
488 VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
490 frontOuter->set_solder_paste_mode( packOptional(
FrontOuterLayers().has_solder_paste, SPM_PASTE,
491 SPM_NO_PASTE, SPM_FROM_DESIGN_RULES ) );
493 backOuter->set_solder_paste_mode( packOptional(
BackOuterLayers().has_solder_paste, SPM_PASTE,
494 SPM_NO_PASTE, SPM_FROM_DESIGN_RULES ) );
498 frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
504 backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm(
510 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
516 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm(
522 frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
528 backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value(
532 aContainer.PackFrom( padstack );
540 auto compareCopperProps =
543 if( ( diff =
static_cast<int>( aPadstackRef->
Shape( aLayer ) ) -
544 static_cast<int>( aPadstackCmp->
Shape( aLayer ) ) ) != 0 )
547 if( ( diff = aPadstackRef->
Size( aLayer ).
x - aPadstackCmp->
Size( aLayer ).
x ) != 0 )
550 if( ( diff = aPadstackRef->
Size( aLayer ).
y - aPadstackCmp->
Size( aLayer ).
y ) != 0 )
553 if( ( diff = aPadstackRef->
Offset( aLayer ).
x
554 - aPadstackCmp->
Offset( aLayer ).
x ) != 0 )
557 if( ( diff = aPadstackRef->
Offset( aLayer ).
y
558 - aPadstackCmp->
Offset( aLayer ).
y ) != 0 )
590 if( ( diff =
static_cast<int>( aPadstackRef->
Primitives( aLayer ).size() ) -
591 static_cast<int>( aPadstackCmp->
Primitives( aLayer ).size() ) ) != 0 )
603 if( ( diff =
static_cast<int>( aPadstackRef->
DrillShape() ) -
604 static_cast<int>( aPadstackCmp->
DrillShape() ) ) != 0 )
619 double similarity = 1.0;
624 if(
Shape( aLayer ) != aOther.
Shape( aLayer ) )
627 if(
Size( aLayer ) != aOther.
Size( aLayer ) )
704 return wxEmptyString;
730 if( aCopperLayerCount > 2 )
732 int innerCount = ( aCopperLayerCount - 2 );
733 int halfInnerLayerCount = innerCount / 2;
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!" );
803 [](
const std::shared_ptr<PCB_SHAPE>& aFirst,
804 const std::shared_ptr<PCB_SHAPE>& aSecond )
806 return *aFirst == *aSecond;
842 [](
const auto& aEntry )
844 const auto& [key, value] = aEntry;
878 parentPad->SetDirty();
922 std::vector<PCB_LAYER_ID> layers;
926 layers.push_back( layer );
936 switch(
static_cast<int>( aLayer ) )
987 wxString::Format( wxT(
"Unhandled layer %d in PADSTACK::EffectiveLayerFor" ),
996 if(
board && !
board->GetEnabledLayers().Contains( boardCuLayer ) )
1003 if( !
board->IsFootprintHolder() )
1004 wxFAIL_MSG(
"Asked for inner padstack layer not present on the board" );
1009 return boardCuLayer;
1029 wxT(
"Expected both padstacks to have the same board copper layer count" ) );
1055 wxString::Format( wxT(
"Attempt to retrieve layer %d from a padstack that does not contain it" ),
1159 int min_r = std::min( size.
x, size.
y );
1332 for(
const std::shared_ptr<PCB_SHAPE>& prim : aPrimitivesList )
1342 if( aPrimitivesList.size() )
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
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 int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
Information pertinent to a Pcbnew printed circuit board.
virtual void SetParent(EDA_ITEM *aParent)
LSET is a set of PCB_LAYER_IDs.
std::optional< bool > IsFilled() const
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
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)
std::optional< bool > IsPlugged(PCB_LAYER_ID aSide) const
LSET RelevantShapeLayers(const PADSTACK &aOther) const
Returns the set of layers that must be considered if checking one padstack against another.
std::optional< int > & ThermalGap(PCB_LAYER_ID aLayer=F_Cu)
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::optional< bool > IsCapped() const
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
std::optional< bool > IsCovered(PCB_LAYER_ID aSide) 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)
@ NORMAL
Padstack for a footprint pad.
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_LOCKED_ITEM_SHADOW
Shadow layer for locked items.
@ 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)
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.
VECTOR2< int32_t > VECTOR2I