From 91db6e8aab9a66bfbbe0a4f10ad610e3c15d184e Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Wed, 6 Nov 2024 23:42:02 -0500 Subject: [PATCH] Align API for padstacks with the actual implementation --- api/proto/board/board_types.proto | 46 +++++--- pcbnew/padstack.cpp | 186 +++++++++++++++++------------- pcbnew/padstack.h | 10 ++ pcbnew/pcb_track.cpp | 3 - 4 files changed, 149 insertions(+), 96 deletions(-) diff --git a/api/proto/board/board_types.proto b/api/proto/board/board_types.proto index 5d8f7d4c66..b325117a9f 100644 --- a/api/proto/board/board_types.proto +++ b/api/proto/board/board_types.proto @@ -194,6 +194,7 @@ enum ZoneConnectionStyle ZCS_PTH_THERMAL = 5; // Thermal reliefs for plated through holes, solid for SMD pads } +// Controls for how copper zones connect to a pad message ZoneConnectionSettings { ZoneConnectionStyle zone_connection = 1; @@ -219,8 +220,8 @@ message SolderPasteOverrides // The defintion of a padstack on a single layer message PadStackLayer { - // The board layers of this padstack entry - repeated BoardLayer layers = 1; + // The board layer of this padstack entry. For Front/Inner/Back padstacks, In1_Cu is used to represent inner layers. + BoardLayer layer = 1; // The shape of the pad on this layer PadStackShape shape = 2; @@ -243,6 +244,7 @@ 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; + // Reserved for future use -- at the moment, zone connection settings are not per-layer ZoneConnectionSettings zone_settings = 9; } @@ -267,39 +269,55 @@ message PadStackOuterLayer { SolderMaskMode solder_mask_mode = 1; SolderPasteMode solder_paste_mode = 2; + + // NOTE: At present, KiCad does not support different solder mask expansion settings for the top and bottom layers SolderMaskOverrides solder_mask_settings = 3; + + // NOTE: At present, KiCad does not support different solder paste expansion settings for the top and bottom layers SolderPasteOverrides solder_paste_settings = 4; } +message DrillProperties +{ + // Lowest (closest to F_Cu) layer this drill exists on. + BoardLayer start_layer = 1; + + // Highest (closest to B_Cu) layer this drill exists on. + BoardLayer end_layer = 2; + + // The diameter, in x and y, of the pad's drilled hole, if this pad has a hole. + // x and y will be the same value if the hole is round. + kiapi.common.types.Vector2 diameter = 3; +} + // A pad stack definition for a multilayer pad or via. message PadStack { // What type of pad stack this represents. PadStackType type = 1; - // Lowest (closest to F_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. - BoardLayer start_layer = 2; + // The set of board layers this padstack has a definition for + repeated BoardLayer layers = 2; - // Highest (closest to B_Cu) layer this stack exists on. Ignored if type == PST_THROUGH. - BoardLayer end_layer = 3; + // Properties of the drilled hole in this padstack, if it has one + DrillProperties drill = 3; // How to treat pad shapes on unconnected layers. UnconnectedLayerRemoval unconnected_layer_removal = 4; - // The diameter, in x and y, of the pad's drilled hole, if this pad has a hole. - // x and y will be the same value if the hole is round. - kiapi.common.types.Vector2 drill_diameter = 5; - - repeated PadStackLayer layers = 6; + repeated PadStackLayer copper_layers = 5; // The overall rotation of this padstack (affects all layers) - kiapi.common.types.Angle angle = 7; + kiapi.common.types.Angle angle = 6; // Solder mask and paste settings for the front - PadStackOuterLayer front_outer_layers = 8; + PadStackOuterLayer front_outer_layers = 7; // Solder mask and paste settings for the back - PadStackOuterLayer back_outer_layers = 9; + PadStackOuterLayer back_outer_layers = 8; + + // Controls for how copper zones connect to the padstack + ZoneConnectionSettings zone_settings = 9; } enum ViaType diff --git a/pcbnew/padstack.cpp b/pcbnew/padstack.cpp index 3ce7694782..8fde25e098 100644 --- a/pcbnew/padstack.cpp +++ b/pcbnew/padstack.cpp @@ -146,6 +146,53 @@ bool PADSTACK::operator==( const PADSTACK& aOther ) const } +bool PADSTACK::unpackCopperLayer( const kiapi::board::types::PadStackLayer& aProto ) +{ + using namespace kiapi::board::types; + PCB_LAYER_ID layer = FromProtoEnum( aProto.layer() ); + + if( m_mode == MODE::NORMAL && layer != ALL_LAYERS ) + return false; + + if( m_mode == MODE::FRONT_INNER_BACK && layer != F_Cu && layer != INNER_LAYERS && layer != B_Cu ) + return false; + + SetSize( kiapi::common::UnpackVector2( aProto.size() ), layer ); + SetShape( FromProtoEnum( aProto.shape() ), layer ); + SetAnchorShape( FromProtoEnum( aProto.custom_anchor_shape() ), layer ); + + SHAPE_PROPS& props = CopperLayer( layer ).shape; + props.chamfered_rect_ratio = aProto.chamfer_ratio(); + props.round_rect_radius_ratio = aProto.corner_rounding_ratio(); + + if( aProto.chamfered_corners().top_left() ) + props.chamfered_rect_positions |= RECT_CHAMFER_TOP_LEFT; + + if( aProto.chamfered_corners().top_right() ) + props.chamfered_rect_positions |= RECT_CHAMFER_TOP_RIGHT; + + if( aProto.chamfered_corners().bottom_left() ) + props.chamfered_rect_positions |= RECT_CHAMFER_BOTTOM_LEFT; + + if( aProto.chamfered_corners().bottom_right() ) + props.chamfered_rect_positions |= RECT_CHAMFER_BOTTOM_RIGHT; + + ClearPrimitives( layer ); + google::protobuf::Any a; + + for( const GraphicShape& shapeProto : aProto.custom_shapes() ) + { + a.PackFrom( shapeProto ); + std::unique_ptr shape = std::make_unique( m_parent ); + + if( shape->Deserialize( a ) ) + AddPrimitive( shape.release(), layer ); + } + + return true; +} + + bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer ) { using namespace kiapi::board::types; @@ -155,76 +202,40 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer ) return false; m_mode = FromProtoEnum( padstack.type() ); - - // TODO - m_layerSet.reset(); - + SetLayerSet( kiapi::board::UnpackLayerSet( padstack.layers() ) ); m_orientation = EDA_ANGLE( padstack.angle().value_degrees(), DEGREES_T ); - Drill().size = kiapi::common::UnpackVector2( padstack.drill_diameter() ); - Drill().start = FromProtoEnum( padstack.start_layer() ); - Drill().end = FromProtoEnum( padstack.end_layer() ); + Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() ); + Drill().start = FromProtoEnum( padstack.drill().start_layer() ); + Drill().end = FromProtoEnum( padstack.drill().end_layer() ); - // We don't yet support complex padstacks - // TODO(JE) Rework for full padstacks - if( padstack.layers_size() == 1 ) + for( const PadStackLayer& layer : padstack.copper_layers() ) { - const PadStackLayer& layer = padstack.layers( 0 ); - SetSize( kiapi::common::UnpackVector2( layer.size() ), ALL_LAYERS ); - SetLayerSet( kiapi::board::UnpackLayerSet( layer.layers() ) ); - SetShape( FromProtoEnum( layer.shape() ), F_Cu ); - SetAnchorShape( FromProtoEnum( layer.custom_anchor_shape() ), F_Cu ); + if( !unpackCopperLayer( layer ) ) + return false; + } - SHAPE_PROPS& props = CopperLayer( ALL_LAYERS ).shape; - props.chamfered_rect_ratio = layer.chamfer_ratio(); - props.round_rect_radius_ratio = layer.corner_rounding_ratio(); + if( padstack.has_zone_settings() ) + { + CopperLayer( ALL_LAYERS ).zone_connection = + FromProtoEnum( padstack.zone_settings().zone_connection() ); - if( layer.chamfered_corners().top_left() ) - props.chamfered_rect_positions |= RECT_CHAMFER_TOP_LEFT; - - if( layer.chamfered_corners().top_right() ) - props.chamfered_rect_positions |= RECT_CHAMFER_TOP_RIGHT; - - if( layer.chamfered_corners().bottom_left() ) - props.chamfered_rect_positions |= RECT_CHAMFER_BOTTOM_LEFT; - - if( layer.chamfered_corners().bottom_right() ) - props.chamfered_rect_positions |= RECT_CHAMFER_BOTTOM_RIGHT; - - ClearPrimitives( F_Cu ); - google::protobuf::Any a; - - for( const GraphicShape& shapeProto : layer.custom_shapes() ) + if( padstack.zone_settings().has_thermal_spokes() ) { - a.PackFrom( shapeProto ); - std::unique_ptr shape = std::make_unique( m_parent ); + const ThermalSpokeSettings& thermals = padstack.zone_settings().thermal_spokes(); - if( shape->Deserialize( a ) ) - AddPrimitive( shape.release(), F_Cu ); - } - - if( layer.has_zone_settings() ) - { - CopperLayer( ALL_LAYERS ).zone_connection = - FromProtoEnum( layer.zone_settings().zone_connection() ); - - if( layer.zone_settings().has_thermal_spokes() ) - { - const ThermalSpokeSettings& thermals = layer.zone_settings().thermal_spokes(); - - CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap(); - CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width(); - SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu ); - } - } - else - { - CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED; - CopperLayer( ALL_LAYERS ).thermal_gap = 0; - CopperLayer( ALL_LAYERS ).thermal_spoke_width = 0; - CopperLayer( ALL_LAYERS ).thermal_spoke_angle = DefaultThermalSpokeAngleForShape( F_Cu ); + CopperLayer( ALL_LAYERS ).thermal_gap = thermals.gap(); + CopperLayer( ALL_LAYERS ).thermal_spoke_width = thermals.width(); + SetThermalSpokeAngle( thermals.angle().value_degrees(), F_Cu ); } } + else + { + CopperLayer( ALL_LAYERS ).zone_connection = ZONE_CONNECTION::INHERITED; + CopperLayer( ALL_LAYERS ).thermal_gap = 0; + CopperLayer( ALL_LAYERS ).thermal_spoke_width = 0; + CopperLayer( ALL_LAYERS ).thermal_spoke_angle = DefaultThermalSpokeAngleForShape( F_Cu ); + } SetUnconnectedLayerMode( FromProtoEnum( padstack.unconnected_layer_removal() ) ); @@ -352,43 +363,60 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer ) } -void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const +void PADSTACK::packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const { using namespace kiapi::board::types; - PadStack padstack; - padstack.set_type( ToProtoEnum( m_mode ) ); - padstack.set_start_layer( ToProtoEnum( StartLayer() ) ); - padstack.set_end_layer( ToProtoEnum( EndLayer() ) ); - kiapi::common::PackVector2( *padstack.mutable_drill_diameter(), Drill().size ); - padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() ); + PadStackLayer* stackLayer = aProto.add_copper_layers(); - // TODO(JE) Rework for full padstacks - PadStackLayer* stackLayer = padstack.add_layers(); - kiapi::board::PackLayerSet( *stackLayer->mutable_layers(), LayerSet() ); - kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( PADSTACK::ALL_LAYERS ) ); - stackLayer->set_shape( ToProtoEnum( Shape( ALL_LAYERS ) ) ); - stackLayer->set_custom_anchor_shape( ToProtoEnum( AnchorShape( ALL_LAYERS ) ) ); - stackLayer->set_chamfer_ratio( CopperLayer( ALL_LAYERS ).shape.chamfered_rect_ratio ); - stackLayer->set_corner_rounding_ratio( CopperLayer( ALL_LAYERS ).shape.round_rect_radius_ratio ); + stackLayer->set_layer( ToProtoEnum( aLayer ) ); + kiapi::common::PackVector2( *stackLayer->mutable_size(), Size( aLayer ) ); + + stackLayer->set_shape( ToProtoEnum( Shape( aLayer ) ) ); + + stackLayer->set_custom_anchor_shape( + ToProtoEnum( AnchorShape( aLayer ) ) ); + + stackLayer->set_chamfer_ratio( CopperLayer( aLayer ).shape.chamfered_rect_ratio ); + stackLayer->set_corner_rounding_ratio( CopperLayer( aLayer ).shape.round_rect_radius_ratio ); google::protobuf::Any a; - // TODO(JE) Rework for full padstacks - for( const std::shared_ptr& shape : Primitives( ALL_LAYERS ) ) + for( const std::shared_ptr& shape : Primitives( aLayer ) ) { shape->Serialize( a ); GraphicShape* s = stackLayer->add_custom_shapes(); a.UnpackTo( s ); } - const int& corners = CopperLayer( ALL_LAYERS ).shape.chamfered_rect_positions; + const int& corners = CopperLayer( aLayer ).shape.chamfered_rect_positions; stackLayer->mutable_chamfered_corners()->set_top_left( corners & RECT_CHAMFER_TOP_LEFT ); stackLayer->mutable_chamfered_corners()->set_top_right( corners & RECT_CHAMFER_TOP_RIGHT ); 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(); + +void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const +{ + using namespace kiapi::board::types; + PadStack padstack; + + padstack.set_type( ToProtoEnum( m_mode ) ); + kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet ); + padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() ); + + DrillProperties* drill = padstack.mutable_drill(); + drill->set_start_layer( ToProtoEnum( StartLayer() ) ); + drill->set_end_layer( ToProtoEnum( EndLayer() ) ); + kiapi::common::PackVector2( *drill->mutable_diameter(), Drill().size ); + + ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer ) + { + packCopperLayer( aLayer, padstack ); + } ); + + ZoneConnectionSettings* zoneSettings = padstack.mutable_zone_settings(); ThermalSpokeSettings* thermalSettings = zoneSettings->mutable_thermal_spokes(); if( CopperLayer( ALL_LAYERS ).zone_connection.has_value() ) diff --git a/pcbnew/padstack.h b/pcbnew/padstack.h index f12334d68c..b9359a7a68 100644 --- a/pcbnew/padstack.h +++ b/pcbnew/padstack.h @@ -36,6 +36,12 @@ class BOARD_ITEM; class PCB_SHAPE; +namespace kiapi::board::types +{ + class PadStack; + class PadStackLayer; +} + /** * The set of pad shapes, used with PAD::{Set,Get}Shape() @@ -428,6 +434,10 @@ public: void ClearPrimitives( PCB_LAYER_ID aLayer ); private: + void packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const; + + bool unpackCopperLayer( const kiapi::board::types::PadStackLayer& aProto ); + ///! The BOARD_ITEM this PADSTACK belongs to; will be used as the parent for owned shapes BOARD_ITEM* m_parent; diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp index 21629db51b..5ae65cd29e 100644 --- a/pcbnew/pcb_track.cpp +++ b/pcbnew/pcb_track.cpp @@ -475,8 +475,6 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const padstack.Serialize( padStackWrapper ); padStackWrapper.UnpackTo( via.mutable_pad_stack() ); - - via.set_type( ToProtoEnum( GetViaType() ) ); via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED : kiapi::common::types::LockedState::LS_UNLOCKED ); @@ -497,7 +495,6 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer ) const_cast( m_Uuid ) = KIID( via.id().value() ); SetStart( VECTOR2I( via.position().x_nm(), via.position().y_nm() ) ); SetEnd( GetStart() ); - SetDrill( via.pad_stack().drill_diameter().x_nm() ); google::protobuf::Any padStackWrapper; padStackWrapper.PackFrom( via.pad_stack() );