From 284a760af74d906c9e420bb29a948ffb3f901b03 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sat, 20 Jul 2024 17:47:59 -0400 Subject: [PATCH] Move more properties into PADSTACK --- api/proto/board/board_types.proto | 111 ++++++++++---- pcbnew/footprint.cpp | 41 ++--- pcbnew/pad.cpp | 51 +------ pcbnew/padstack.cpp | 240 +++++++++++++++++++++++++++++- pcbnew/padstack.h | 1 + 5 files changed, 338 insertions(+), 106 deletions(-) diff --git a/api/proto/board/board_types.proto b/api/proto/board/board_types.proto index ed75c4c595..a605419b00 100644 --- a/api/proto/board/board_types.proto +++ b/api/proto/board/board_types.proto @@ -183,6 +183,39 @@ message ChamferedRectCorners bool bottom_right = 4; } +// enum class ZONE_CONNECTION +enum ZoneConnectionStyle +{ + ZCS_UNKNOWN = 0; + ZCS_INHERITED = 1; + ZCS_NONE = 2; + ZCS_THERMAL = 3; + ZCS_FULL = 4; + ZCS_PTH_THERMAL = 5; // Thermal reliefs for plated through holes, solid for SMD pads +} + +message ZoneConnectionSettings +{ + ZoneConnectionStyle zone_connection = 1; + + ThermalSpokeSettings thermal_spokes = 2; +} + +message SolderMaskOverrides +{ + // Solder mask expansion/contraction + kiapi.common.types.Distance solder_mask_margin = 1; +} + +message SolderPasteOverrides +{ + // Solder paste expansion/contraction + kiapi.common.types.Distance solder_paste_margin = 1; + + // Solder paste expansion/contraction ratio + kiapi.common.types.Ratio solder_paste_margin_ratio = 2; +} + // The defintion of a padstack on a single layer message PadStackLayer { @@ -209,6 +242,33 @@ message PadStackLayer // If shape == PSS_CUSTOM, defines the shape of the anchor (only PSS_CIRCLE and PSS_RECTANGLE supported at present) PadStackShape custom_anchor_shape = 8; + + ZoneConnectionSettings zone_settings = 9; +} + +enum SolderMaskMode +{ + SMM_UNKOWN = 0; + SMM_MASKED = 1; + SMM_UNMASKED = 2; + SMM_FROM_DESIGN_RULES = 3; +} + +enum SolderPasteMode +{ + SPM_UNKOWN = 0; + SPM_PASTE = 1; + SPM_NO_PASTE = 2; + SPM_FROM_DESIGN_RULES = 3; +} + +// Properties for the outer technical layers of a padstack on the top or bottom of the stack +message PadStackOuterLayer +{ + SolderMaskMode solder_mask_mode = 1; + SolderPasteMode solder_paste_mode = 2; + SolderMaskOverrides solder_mask_settings = 3; + SolderPasteOverrides solder_paste_settings = 4; } // A pad stack definition for a multilayer pad or via. @@ -234,6 +294,12 @@ message PadStack // The overall rotation of this padstack (affects all layers) kiapi.common.types.Angle angle = 7; + + // Solder mask and paste settings for the front + PadStackOuterLayer front_outer_layers = 8; + + // Solder mask and paste settings for the back + PadStackOuterLayer back_outer_layers = 9; } enum ViaType @@ -378,9 +444,8 @@ message Pad // A pad's position is always relative to the parent footprint's origin kiapi.common.types.Vector2 position = 7; - DesignRuleOverrides overrides = 8; - - ThermalSpokeSettings thermal_spokes = 9; + // Copper-to-copper clearance override + kiapi.common.types.Distance copper_clearance_override = 8; } // Copper zone, non-copper zone, or rule area @@ -443,46 +508,26 @@ message FootprintAttributes FootprintMountingStyle mounting_style = 8; } -// enum class ZONE_CONNECTION -enum ZoneConnectionStyle -{ - ZCS_UNKNOWN = 0; - ZCS_INHERITED = 1; - ZCS_NONE = 2; - ZCS_THERMAL = 3; - ZCS_FULL = 4; - ZCS_PTH_THERMAL = 5; // Thermal reliefs for plated through holes, solid for SMD pads -} - -message DesignRuleOverrides -{ - // Copper-to-copper clearance override - kiapi.common.types.Distance clearance = 1; - - // Solder mask expansion/contraction - kiapi.common.types.Distance solder_mask_margin = 2; - - // Solder paste expansion/contraction - kiapi.common.types.Distance solder_paste_margin = 3; - - // Solder paste expansion/contraction ratio - kiapi.common.types.Ratio solder_paste_margin_ratio = 4; - - ZoneConnectionStyle zone_connection = 5; -} - message NetTieDefinition { repeated string pad_number = 1; } +message FootprintDesignRuleOverrides +{ + SolderMaskOverrides solder_mask = 1; + SolderPasteOverrides solder_paste = 2; + kiapi.common.types.Distance copper_clearance = 3; + ZoneConnectionStyle zone_connection = 4; +} + // A footprint definition (i.e. what would be in a library) message Footprint { kiapi.common.types.LibraryIdentifier id = 1; kiapi.common.types.Vector2 anchor = 2; FootprintAttributes attributes = 3; - DesignRuleOverrides overrides = 4; + FootprintDesignRuleOverrides overrides = 4; repeated NetTieDefinition net_ties = 5; repeated BoardLayer private_layers = 6; @@ -511,5 +556,5 @@ message FootprintInstance Field description_field = 10; FootprintAttributes attributes = 11; - DesignRuleOverrides overrides = 12; + FootprintDesignRuleOverrides overrides = 12; } diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 8d4f0f17da..46d024ce94 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -308,19 +308,19 @@ void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const // TODO: serialize library mandatory fields - kiapi::board::types::DesignRuleOverrides* overrides = def->mutable_overrides(); + kiapi::board::types::FootprintDesignRuleOverrides* overrides = def->mutable_overrides(); if( GetLocalClearance().has_value() ) - overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() ); + overrides->mutable_copper_clearance()->set_value_nm( *GetLocalClearance() ); if( GetLocalSolderMaskMargin().has_value() ) - overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() ); + overrides->mutable_solder_mask()->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() ); if( GetLocalSolderPasteMargin().has_value() ) - overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() ); + overrides->mutable_solder_paste()->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() ); if( GetLocalSolderPasteMarginRatio().has_value() ) - overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() ); + overrides->mutable_solder_paste()->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() ); overrides->set_zone_connection( ToProtoEnum( overrides.zone_connection() ) ); diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index 23d47506d4..e60fe0ca94 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -150,28 +150,8 @@ void PAD::Serialize( google::protobuf::Any &aContainer ) const m_padStack.Serialize( padStackMsg ); padStackMsg.UnpackTo( pad.mutable_pad_stack() ); - DesignRuleOverrides* overrides = pad.mutable_overrides(); - if( GetLocalClearance().has_value() ) - overrides->mutable_clearance()->set_value_nm( *GetLocalClearance() ); - - if( GetLocalSolderMaskMargin().has_value() ) - overrides->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() ); - - if( GetLocalSolderPasteMargin().has_value() ) - overrides->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() ); - - if( GetLocalSolderPasteMarginRatio().has_value() ) - overrides->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() ); - - overrides->set_zone_connection( - ToProtoEnum( GetLocalZoneConnection() ) ); - - ThermalSpokeSettings* thermals = pad.mutable_thermal_spokes(); - - thermals->set_width( GetThermalSpokeWidth() ); - thermals->set_gap( GetThermalGap() ); - thermals->mutable_angle()->set_value_degrees( GetThermalSpokeAngleDegrees() ); + pad.mutable_copper_clearance_override()->set_value_nm( *GetLocalClearance() ); aContainer.PackFrom( pad ); } @@ -196,36 +176,11 @@ bool PAD::Deserialize( const google::protobuf::Any &aContainer ) SetLayer( m_padStack.StartLayer() ); - const kiapi::board::types::DesignRuleOverrides& overrides = pad.overrides(); - - if( overrides.has_clearance() ) - SetLocalClearance( overrides.clearance().value_nm() ); + if( pad.has_copper_clearance_override() ) + SetLocalClearance( pad.copper_clearance_override().value_nm() ); else SetLocalClearance( std::nullopt ); - if( overrides.has_solder_mask_margin() ) - SetLocalSolderMaskMargin( overrides.solder_mask_margin().value_nm() ); - else - SetLocalSolderMaskMargin( std::nullopt ); - - if( overrides.has_solder_paste_margin() ) - SetLocalSolderPasteMargin( overrides.solder_paste_margin().value_nm() ); - else - SetLocalSolderPasteMargin( std::nullopt ); - - if( overrides.has_solder_paste_margin_ratio() ) - SetLocalSolderPasteMarginRatio( overrides.solder_paste_margin_ratio().value() ); - else - SetLocalSolderPasteMarginRatio( std::nullopt ); - - SetLocalZoneConnection( FromProtoEnum( overrides.zone_connection() ) ); - - const kiapi::board::types::ThermalSpokeSettings& thermals = pad.thermal_spokes(); - - SetThermalGap( thermals.gap() ); - SetThermalSpokeWidth( thermals.width() ); - SetThermalSpokeAngleDegrees( thermals.angle().value_degrees() ); - return true; } diff --git a/pcbnew/padstack.cpp b/pcbnew/padstack.cpp index 5a6745eb17..37dabde3fa 100644 --- a/pcbnew/padstack.cpp +++ b/pcbnew/padstack.cpp @@ -36,9 +36,9 @@ PADSTACK::PADSTACK( BOARD_ITEM* aParent ) : { m_defaultCopperProps.shape = SHAPE_PROPS(); m_defaultCopperProps.zone_connection = ZONE_CONNECTION::INHERITED; - m_defaultCopperProps.thermal_spoke_width = std::nullopt; + m_defaultCopperProps.thermal_spoke_width = 0; m_defaultCopperProps.thermal_spoke_angle = ANGLE_45; - m_defaultCopperProps.thermal_gap = std::nullopt; + m_defaultCopperProps.thermal_gap = 0; m_drill.shape = PAD_DRILL_SHAPE::CIRCLE; m_drill.start = F_Cu; @@ -141,11 +141,152 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer ) if( shape->Deserialize( a ) ) AddPrimitive( shape.release() ); } + + if( layer.has_zone_settings() ) + { + CopperLayerDefaults().zone_connection = + FromProtoEnum( layer.zone_settings().zone_connection() ); + + if( layer.zone_settings().has_thermal_spokes() ) + { + const ThermalSpokeSettings& thermals = layer.zone_settings().thermal_spokes(); + + CopperLayerDefaults().thermal_gap = thermals.gap(); + CopperLayerDefaults().thermal_spoke_width = thermals.width(); + SetThermalSpokeAngle( thermals.angle().value_degrees() ); + } + } + else + { + CopperLayerDefaults().zone_connection = ZONE_CONNECTION::INHERITED; + CopperLayerDefaults().thermal_gap = 0; + CopperLayerDefaults().thermal_spoke_width = 0; + CopperLayerDefaults().thermal_spoke_angle = DefaultThermalSpokeAngleForShape(); + } } SetUnconnectedLayerMode( FromProtoEnum( padstack.unconnected_layer_removal() ) ); + auto unpackMask = + []( const SolderMaskMode& aProto, std::optional& aDest ) + { + switch( aProto ) + { + case kiapi::board::types::SMM_MASKED: + aDest = true; + break; + + case kiapi::board::types::SMM_UNMASKED: + aDest = false; + break; + + default: + case kiapi::board::types::SMM_FROM_DESIGN_RULES: + aDest = std::nullopt; + break; + } + }; + + unpackMask( padstack.front_outer_layers().solder_mask_mode(), + FrontOuterLayers().has_solder_mask ); + + unpackMask( padstack.back_outer_layers().solder_mask_mode(), + BackOuterLayers().has_solder_mask ); + + auto unpackPaste = + []( const SolderPasteMode& aProto, std::optional& aDest ) + { + switch( aProto ) + { + case kiapi::board::types::SPM_PASTE: + aDest = true; + break; + + case kiapi::board::types::SPM_NO_PASTE: + aDest = false; + break; + + default: + case kiapi::board::types::SPM_FROM_DESIGN_RULES: + aDest = std::nullopt; + break; + } + }; + + unpackPaste( padstack.front_outer_layers().solder_paste_mode(), + FrontOuterLayers().has_solder_paste ); + + unpackPaste( padstack.back_outer_layers().solder_paste_mode(), + BackOuterLayers().has_solder_paste ); + + if( padstack.front_outer_layers().has_solder_mask_settings() + && padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() ) + { + FrontOuterLayers().solder_mask_margin = + padstack.front_outer_layers().solder_mask_settings().solder_mask_margin().value_nm(); + } + else + { + FrontOuterLayers().solder_mask_margin = std::nullopt; + } + + if( padstack.back_outer_layers().has_solder_mask_settings() + && padstack.back_outer_layers().solder_mask_settings().has_solder_mask_margin() ) + { + BackOuterLayers().solder_mask_margin = + padstack.back_outer_layers().solder_mask_settings().solder_mask_margin().value_nm(); + } + else + { + BackOuterLayers().solder_mask_margin = std::nullopt; + } + + if( padstack.front_outer_layers().has_solder_paste_settings() + && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin() ) + { + FrontOuterLayers().solder_paste_margin = + padstack.front_outer_layers().solder_paste_settings().solder_paste_margin().value_nm(); + } + else + { + FrontOuterLayers().solder_paste_margin = std::nullopt; + } + + if( padstack.back_outer_layers().has_solder_paste_settings() + && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin() ) + { + BackOuterLayers().solder_paste_margin = + padstack.back_outer_layers().solder_paste_settings().solder_paste_margin().value_nm(); + } + else + { + BackOuterLayers().solder_paste_margin = std::nullopt; + } + + if( padstack.front_outer_layers().has_solder_paste_settings() + && padstack.front_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() ) + { + FrontOuterLayers().solder_paste_margin_ratio = + padstack.front_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value(); + } + else + { + FrontOuterLayers().solder_paste_margin_ratio = std::nullopt; + } + + if( padstack.back_outer_layers().has_solder_paste_settings() + && padstack.back_outer_layers().solder_paste_settings().has_solder_paste_margin_ratio() ) + { + BackOuterLayers().solder_paste_margin_ratio = + padstack.back_outer_layers().solder_paste_settings().solder_paste_margin_ratio().value(); + } + else + { + BackOuterLayers().solder_paste_margin_ratio = std::nullopt; + } + + return true; } @@ -184,9 +325,87 @@ void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const stackLayer->mutable_chamfered_corners()->set_bottom_left( corners & RECT_CHAMFER_BOTTOM_LEFT ); stackLayer->mutable_chamfered_corners()->set_bottom_right( corners & RECT_CHAMFER_BOTTOM_RIGHT ); + ZoneConnectionSettings* zoneSettings = stackLayer->mutable_zone_settings(); + ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes(); + + if( CopperLayerDefaults().zone_connection.has_value() ) + { + zoneSettings->set_zone_connection( ToProtoEnum( + *CopperLayerDefaults().zone_connection ) ); + } + + thermalSettings->set_width( CopperLayerDefaults().thermal_spoke_width.value_or( 0 ) ); + thermalSettings->set_gap( CopperLayerDefaults().thermal_gap.value_or( 0 ) ); + thermalSettings->mutable_angle()->set_value_degrees( ThermalSpokeAngle().AsDegrees() ); + padstack.set_unconnected_layer_removal( ToProtoEnum( m_unconnectedLayerMode ) ); + auto packOptional = + []( const std::optional& aVal, ProtoEnum aTrueVal, + ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum + { + if( aVal.has_value() ) + return *aVal ? aTrueVal : aFalseVal; + + return aNullVal; + }; + + PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers(); + PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers(); + + frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask, + SMM_MASKED, SMM_UNMASKED, + SMM_FROM_DESIGN_RULES ) ); + + backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask, + SMM_MASKED, SMM_UNMASKED, + SMM_FROM_DESIGN_RULES ) ); + + frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste, + SPM_PASTE, SPM_NO_PASTE, + SPM_FROM_DESIGN_RULES ) ); + + backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste, + SPM_PASTE, SPM_NO_PASTE, + SPM_FROM_DESIGN_RULES ) ); + + if( FrontOuterLayers().solder_mask_margin.has_value() ) + { + frontOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm( + *FrontOuterLayers().solder_mask_margin ); + } + + if( BackOuterLayers().solder_mask_margin.has_value() ) + { + backOuter->mutable_solder_mask_settings()->mutable_solder_mask_margin()->set_value_nm( + *BackOuterLayers().solder_mask_margin ); + } + + if( FrontOuterLayers().solder_paste_margin.has_value() ) + { + frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm( + *FrontOuterLayers().solder_paste_margin ); + } + + if( BackOuterLayers().solder_paste_margin.has_value() ) + { + backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin()->set_value_nm( + *BackOuterLayers().solder_paste_margin ); + } + + if( FrontOuterLayers().solder_paste_margin_ratio.has_value() ) + { + frontOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value( + *FrontOuterLayers().solder_paste_margin_ratio ); + } + + if( BackOuterLayers().solder_paste_margin_ratio.has_value() ) + { + backOuter->mutable_solder_paste_settings()->mutable_solder_paste_margin_ratio()->set_value( + *BackOuterLayers().solder_paste_margin_ratio ); + } + aContainer.PackFrom( padstack ); } @@ -477,15 +696,22 @@ const std::optional& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const } +EDA_ANGLE PADSTACK::DefaultThermalSpokeAngleForShape( PCB_LAYER_ID aLayer ) const +{ + const COPPER_LAYER_PROPS& defaults = CopperLayerDefaults(); + + return ( defaults.shape.shape == PAD_SHAPE::CIRCLE + || ( defaults.shape.shape == PAD_SHAPE::CUSTOM + && defaults.shape.anchor_shape == PAD_SHAPE::CIRCLE ) ) + ? ANGLE_45 : ANGLE_90; +} + + EDA_ANGLE PADSTACK::ThermalSpokeAngle( PCB_LAYER_ID aLayer ) const { const COPPER_LAYER_PROPS& defaults = CopperLayerDefaults(); - return defaults.thermal_spoke_angle.value_or( - ( defaults.shape.shape == PAD_SHAPE::CIRCLE - || ( defaults.shape.shape == PAD_SHAPE::CUSTOM - && defaults.shape.anchor_shape == PAD_SHAPE::CIRCLE ) ) - ? ANGLE_45 : ANGLE_90 ); + return defaults.thermal_spoke_angle.value_or( DefaultThermalSpokeAngleForShape( aLayer ) ); } diff --git a/pcbnew/padstack.h b/pcbnew/padstack.h index 9faad36a01..f8e04771f2 100644 --- a/pcbnew/padstack.h +++ b/pcbnew/padstack.h @@ -346,6 +346,7 @@ public: std::optional& ThermalGap( PCB_LAYER_ID aLayer = F_Cu ); const std::optional& ThermalGap( PCB_LAYER_ID aLayer = F_Cu ) const; + EDA_ANGLE DefaultThermalSpokeAngleForShape( PCB_LAYER_ID aLayer = F_Cu ) const; EDA_ANGLE ThermalSpokeAngle( PCB_LAYER_ID aLayer = F_Cu ) const; void SetThermalSpokeAngle( EDA_ANGLE aAngle, PCB_LAYER_ID aLayer = F_Cu );