Move pad and via properties into PADSTACK

This commit is contained in:
Jon Evans 2024-04-08 09:09:20 -04:00
parent c800fb790d
commit e16130a02c
16 changed files with 1076 additions and 435 deletions

View File

@ -103,3 +103,45 @@ ZONE_CONNECTION FromProtoEnum( types::ZoneConnectionStyle aValue )
"Unhandled case in FromProtoEnum<types::ZoneConnectionStyle>" );
}
}
template<>
types::UnconnectedLayerRemoval ToProtoEnum( PADSTACK::UNCONNECTED_LAYER_MODE aValue )
{
switch( aValue )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return types::UnconnectedLayerRemoval::ULR_KEEP;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
return types::UnconnectedLayerRemoval::ULR_REMOVE;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
return types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END;
default:
wxCHECK_MSG( false, types::UnconnectedLayerRemoval::ULR_UNKNOWN,
"Unhandled case in ToProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE>");
}
}
template<>
PADSTACK::UNCONNECTED_LAYER_MODE FromProtoEnum( types::UnconnectedLayerRemoval aValue )
{
switch( aValue )
{
case types::UnconnectedLayerRemoval::ULR_KEEP:
return PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL;
case types::UnconnectedLayerRemoval::ULR_REMOVE:
return PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL;
case types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END:
return PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END;
default:
wxCHECK_MSG( false, PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL,
"Unhandled case in FromProtoEnum<types::UnconnectedLayerRemoval>");
}
}

View File

@ -676,7 +676,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
case ZONE_CONNECTION::NONE: m_ZoneConnectionChoice->SetSelection( 3 ); break;
}
if( m_previewPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
if( m_previewPad->GetCustomShapeInZoneOpt() == PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL )
m_ZoneCustomPadShape->SetSelection( 1 );
else
m_ZoneCustomPadShape->SetSelection( 0 );
@ -1620,9 +1620,8 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
m_currentPad->SetAnchorPadShape( m_masterPad->GetAnchorPadShape() );
m_currentPad->ReplacePrimitives( m_masterPad->GetPrimitives() );
m_currentPad->SetPadstack( m_masterPad->Padstack() );
m_currentPad->SetLayerSet( m_masterPad->GetLayerSet() );
m_currentPad->SetRemoveUnconnected( m_masterPad->GetRemoveUnconnected() );
m_currentPad->SetKeepTopBottom( m_masterPad->GetKeepTopBottom() );
m_currentPad->SetNumber( m_masterPad->GetNumber() );
@ -1947,8 +1946,8 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
// shapes are convex to begin with, this really only makes any difference for custom
// pad shapes.
aPad->SetCustomShapeInZoneOpt( m_ZoneCustomPadShape->GetSelection() == 0 ?
CUST_PAD_SHAPE_IN_ZONE_OUTLINE :
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
PADSTACK::CUSTOM_SHAPE_ZONE_MODE::OUTLINE :
PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL );
switch( aPad->GetAttribute() )
{
@ -2001,8 +2000,7 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
LSET padLayerMask = LSET();
int copperLayersChoice = m_rbCopperLayersSel->GetSelection();
aPad->SetRemoveUnconnected( false );
aPad->SetKeepTopBottom( false );
aPad->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
switch( m_padType->GetSelection() )
{
@ -2017,14 +2015,14 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
case 1:
// Front, back and connected
padLayerMask |= LSET::AllCuMask();
aPad->SetRemoveUnconnected( true );
aPad->SetKeepTopBottom( true );
aPad->Padstack().SetUnconnectedLayerMode(
PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END );
break;
case 2:
// Connected only
padLayerMask |= LSET::AllCuMask();
aPad->SetRemoveUnconnected( true );
aPad->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL );
break;
case 3:

View File

@ -115,12 +115,13 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_FRAME* aParen
auto getAnnularRingSelection =
[]( const PCB_VIA* via ) -> int
{
if( !via->GetRemoveUnconnected() )
return 0;
else if( via->GetKeepStartEnd() )
return 1;
else
return 2;
switch( via->Padstack().UnconnectedLayerMode() )
{
default:
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL: return 0;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END: return 1;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL: return 2;
}
};
// Look for values that are common for every item that is selected
@ -649,15 +650,16 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
switch( m_annularRingsCtrl->GetSelection() )
{
case 0:
v->SetRemoveUnconnected( false );
v->Padstack().SetUnconnectedLayerMode(
PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
break;
case 1:
v->SetRemoveUnconnected( true );
v->SetKeepStartEnd( true );
v->Padstack().SetUnconnectedLayerMode(
PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END );
break;
case 2:
v->SetRemoveUnconnected( true );
v->SetKeepStartEnd( false );
v->Padstack().SetUnconnectedLayerMode(
PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL );
break;
default:
break;

View File

@ -68,8 +68,10 @@ using KIGFX::PCB_RENDER_SETTINGS;
PAD::PAD( FOOTPRINT* parent ) :
BOARD_CONNECTED_ITEM( parent, PCB_PAD_T )
{
m_size.x = m_size.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 60 ); // Default pad size 60 mils.
m_drill.x = m_drill.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 30 ); // Default drill size 30 mils.
VECTOR2I& drill = m_padStack.Drill().size;
VECTOR2I& size = m_padStack.Size();
size.x = size.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 60 ); // Default pad size 60 mils.
drill.x = drill.y = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 30 ); // Default drill size 30 mils.
m_orient = ANGLE_0;
m_lengthPadToDie = 0;
@ -84,28 +86,19 @@ PAD::PAD( FOOTPRINT* parent ) :
SetProperty( PAD_PROP::NONE ); // no special fabrication property
// Parameters for round rect only:
m_roundedCornerScale = 0.25; // from IPC-7351C standard
m_padStack.SetRoundRectRadiusRatio( 0.25 ); // from IPC-7351C standard
// Parameters for chamfered rect only:
m_chamferScale = 0.2; // Size of chamfer: ratio of smallest of X,Y size
m_chamferPositions = RECT_NO_CHAMFER; // No chamfered corner
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use parent setting by default
m_thermalSpokeWidth = 0; // Use parent setting by default
m_thermalSpokeAngle = ANGLE_45; // Default for circular pads
m_thermalGap = 0; // Use parent setting by default
m_customShapeClearanceArea = CUST_PAD_SHAPE_IN_ZONE_OUTLINE;
m_padStack.SetChamferRatio( 0.2 );
m_padStack.SetChamferPositions( RECT_NO_CHAMFER );
// Set layers mask to default for a standard thru hole pad.
m_layerMask = PTHMask();
m_padStack.SetLayerSet( PTHMask() );
SetSubRatsnest( 0 ); // used in ratsnest calculations
SetDirty();
m_effectiveBoundingRadius = 0;
m_removeUnconnectedLayer = false;
m_keepTopBottomLayer = true;
m_zoneLayerOverrides.fill( ZLO_NONE );
}
@ -132,8 +125,6 @@ PAD& PAD::operator=( const PAD &aOther )
SetPinFunction( aOther.GetPinFunction() );
SetSubRatsnest( aOther.GetSubRatsnest() );
m_effectiveBoundingRadius = aOther.m_effectiveBoundingRadius;
m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
m_keepTopBottomLayer = aOther.m_keepTopBottomLayer;
return *this;
}
@ -167,21 +158,9 @@ void PAD::Serialize( google::protobuf::Any &aContainer ) const
stackLayer->set_shape(
ToProtoEnum<PAD_SHAPE, kiapi::board::types::PadStackShape>( GetShape() ) );
kiapi::board::types::UnconnectedLayerRemoval ulr;
if( m_removeUnconnectedLayer )
{
if( m_keepTopBottomLayer )
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END;
else
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE;
}
else
{
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP;
}
padstack->set_unconnected_layer_removal( ulr );
padstack->set_unconnected_layer_removal(
ToProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE,
kiapi::board::types::UnconnectedLayerRemoval>( GetUnconnectedLayerMode() ) );
kiapi::board::types::DesignRuleOverrides* overrides = pad.mutable_overrides();
@ -240,24 +219,8 @@ bool PAD::Deserialize( const google::protobuf::Any &aContainer )
SetShape( FromProtoEnum<PAD_SHAPE>( layer.shape() ) );
}
switch( padstack.unconnected_layer_removal() )
{
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE:
m_removeUnconnectedLayer = true;
m_keepTopBottomLayer = false;
break;
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END:
m_removeUnconnectedLayer = true;
m_keepTopBottomLayer = true;
break;
default:
case kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP:
m_removeUnconnectedLayer = false;
m_keepTopBottomLayer = false;
break;
}
SetUnconnectedLayerMode(
FromProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
const kiapi::board::types::DesignRuleOverrides& overrides = pad.overrides();
@ -453,13 +416,18 @@ bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
if( GetProperty() == PAD_PROP::HEATSINK )
return true;
if( !m_removeUnconnectedLayer )
PADSTACK::UNCONNECTED_LAYER_MODE mode = m_padStack.UnconnectedLayerMode();
if( mode == PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL )
return true;
// Plated through hole pads need copper on the top/bottom layers for proper soldering
// Unless the user has removed them in the pad dialog
if( m_keepTopBottomLayer && ( aLayer == F_Cu || aLayer == B_Cu ) )
if( mode == PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END
&& ( aLayer == F_Cu || aLayer == B_Cu ) )
{
return true;
}
if( const BOARD* board = GetBoard() )
{
@ -482,22 +450,19 @@ bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
int PAD::GetRoundRectCornerRadius() const
{
return KiROUND( std::min( m_size.x, m_size.y ) * m_roundedCornerScale );
return m_padStack.RoundRectRadius();
}
void PAD::SetRoundRectCornerRadius( double aRadius )
{
int min_r = std::min( m_size.x, m_size.y );
if( min_r > 0 )
SetRoundRectRadiusRatio( aRadius / min_r );
m_padStack.SetRoundRectRadius( aRadius );
}
void PAD::SetRoundRectRadiusRatio( double aRadiusScale )
{
m_roundedCornerScale = alg::clamp( 0.0, aRadiusScale, 0.5 );
m_padStack.SetRoundRectRadiusRatio( alg::clamp( 0.0, aRadiusScale, 0.5 ) );
SetDirty();
}
@ -505,7 +470,7 @@ void PAD::SetRoundRectRadiusRatio( double aRadiusScale )
void PAD::SetChamferRectRatio( double aChamferScale )
{
m_chamferScale = alg::clamp( 0.0, aChamferScale, 0.5 );
m_padStack.SetChamferRatio( alg::clamp( 0.0, aChamferScale, 0.5 ) );
SetDirty();
}
@ -597,6 +562,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
VECTOR2I shapePos = ShapePos(); // Fetch only once; rotation involves trig
PAD_SHAPE effectiveShape = GetShape();
const VECTOR2I& size = m_padStack.Size( aLayer );
if( GetShape() == PAD_SHAPE::CUSTOM )
effectiveShape = GetAnchorPadShape();
@ -604,17 +570,17 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
switch( effectiveShape )
{
case PAD_SHAPE::CIRCLE:
add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
break;
case PAD_SHAPE::OVAL:
if( m_size.x == m_size.y ) // the oval pad is in fact a circle
if( size.x == size.y ) // the oval pad is in fact a circle
{
add( new SHAPE_CIRCLE( shapePos, m_size.x / 2 ) );
add( new SHAPE_CIRCLE( shapePos, size.x / 2 ) );
}
else
{
VECTOR2I half_size = m_size / 2;
VECTOR2I half_size = size / 2;
int half_width = std::min( half_size.x, half_size.y );
VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
RotatePoint( half_len, m_orient );
@ -628,7 +594,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
case PAD_SHAPE::ROUNDRECT:
{
int r = ( effectiveShape == PAD_SHAPE::ROUNDRECT ) ? GetRoundRectCornerRadius() : 0;
VECTOR2I half_size( m_size.x / 2, m_size.y / 2 );
VECTOR2I half_size( size.x / 2, size.y / 2 );
VECTOR2I trap_delta( 0, 0 );
if( r )
@ -647,7 +613,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
}
else if( effectiveShape == PAD_SHAPE::TRAPEZOID )
{
trap_delta = m_deltaSize / 2;
trap_delta = m_padStack.TrapezoidDeltaSize( aLayer ) / 2;
}
SHAPE_LINE_CHAIN corners;
@ -735,7 +701,7 @@ void PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
m_effectiveBoundingBox = m_effectiveShape->BBox();
// Hole shape
VECTOR2I half_size = m_drill / 2;
VECTOR2I half_size = m_padStack.Drill().size / 2;
int half_width = std::min( half_size.x, half_size.y );
VECTOR2I half_len( half_size.x - half_width, half_size.y - half_width );
@ -808,31 +774,33 @@ void PAD::SetAttribute( PAD_ATTRIB aAttribute )
{
m_attribute = aAttribute;
LSET& layerMask = m_padStack.LayerSet();
switch( aAttribute )
{
case PAD_ATTRIB::PTH:
// Plump up to all copper layers
m_layerMask |= LSET::AllCuMask();
layerMask |= LSET::AllCuMask();
break;
case PAD_ATTRIB::SMD:
case PAD_ATTRIB::CONN:
{
// Trim down to no more than one copper layer
LSET copperLayers = m_layerMask & LSET::AllCuMask();
LSET copperLayers = layerMask & LSET::AllCuMask();
if( copperLayers.count() > 1 )
{
m_layerMask &= ~LSET::AllCuMask();
layerMask &= ~LSET::AllCuMask();
if( copperLayers.test( B_Cu ) )
m_layerMask.set( B_Cu );
layerMask.set( B_Cu );
else
m_layerMask.set( copperLayers.Seq().front() );
layerMask.set( copperLayers.Seq().front() );
}
// No hole
m_drill = VECTOR2I( 0, 0 );
m_padStack.Drill().size = VECTOR2I( 0, 0 );
break;
}
@ -888,14 +856,14 @@ void PAD::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
if( aFlipLeftRight )
{
MIRROR( m_pos.x, aCentre.x );
MIRROR( m_offset.x, 0 );
MIRROR( m_deltaSize.x, 0 );
MIRROR( m_padStack.Offset().x, 0 );
MIRROR( m_padStack.TrapezoidDeltaSize().x, 0 );
}
else
{
MIRROR( m_pos.y, aCentre.y );
MIRROR( m_offset.y, 0 );
MIRROR( m_deltaSize.y, 0 );
MIRROR( m_padStack.Offset().y, 0 );
MIRROR( m_padStack.TrapezoidDeltaSize().y, 0 );
}
SetFPRelativeOrientation( -GetFPRelativeOrientation() );
@ -917,20 +885,24 @@ void PAD::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
if( aFlipLeftRight )
{
mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT );
mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT );
mirrorBitFlags( m_padStack.ChamferPositions(), RECT_CHAMFER_TOP_LEFT,
RECT_CHAMFER_TOP_RIGHT );
mirrorBitFlags( m_padStack.ChamferPositions(), RECT_CHAMFER_BOTTOM_LEFT,
RECT_CHAMFER_BOTTOM_RIGHT );
}
else
{
mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_BOTTOM_LEFT );
mirrorBitFlags( m_chamferPositions, RECT_CHAMFER_TOP_RIGHT, RECT_CHAMFER_BOTTOM_RIGHT );
mirrorBitFlags( m_padStack.ChamferPositions(), RECT_CHAMFER_TOP_LEFT,
RECT_CHAMFER_BOTTOM_LEFT );
mirrorBitFlags( m_padStack.ChamferPositions(), RECT_CHAMFER_TOP_RIGHT,
RECT_CHAMFER_BOTTOM_RIGHT );
}
// flip pads layers
// PADS items are currently on all copper layers, or
// currently, only on Front or Back layers.
// So the copper layers count is not taken in account
SetLayerSet( FlipLayerMask( m_layerMask ) );
SetLayerSet( FlipLayerMask( m_padStack.LayerSet() ) );
// Flip the basic shapes, in custom pads
FlipPrimitives( aFlipLeftRight );
@ -950,10 +922,10 @@ void PAD::FlipPrimitives( bool aFlipLeftRight )
VECTOR2I PAD::ShapePos() const
{
if( m_offset.x == 0 && m_offset.y == 0 )
if( m_padStack.Offset().x == 0 && m_padStack.Offset().y == 0 )
return m_pos;
VECTOR2I loc_offset = m_offset;
VECTOR2I loc_offset = m_padStack.Offset();
RotatePoint( loc_offset, m_orient );
@ -973,14 +945,21 @@ bool PAD::IsOnCopperLayer() const
switch( GetShape() )
{
case PAD_SHAPE::CIRCLE:
if( m_offset == VECTOR2I( 0, 0 ) && m_size.x <= m_drill.x )
if( m_padStack.Offset() == VECTOR2I( 0, 0 )
&& m_padStack.Size().x <= m_padStack.Drill().size.x )
{
return false;
}
break;
case PAD_SHAPE::OVAL:
if( m_offset == VECTOR2I( 0, 0 ) && m_size.x <= m_drill.x && m_size.y <= m_drill.y )
if( m_padStack.Offset() == VECTOR2I( 0, 0 )
&& m_padStack.Size().x <= m_padStack.Drill().size.x
&& m_padStack.Size().y <= m_padStack.Drill().size.y )
{
return false;
}
break;
@ -997,16 +976,16 @@ bool PAD::IsOnCopperLayer() const
std::optional<int> PAD::GetLocalClearance( wxString* aSource ) const
{
if( m_clearance.has_value() && aSource )
if( m_padStack.Clearance().has_value() && aSource )
*aSource = _( "pad" );
return m_clearance;
return m_padStack.Clearance();
}
std::optional<int> PAD::GetClearanceOverrides( wxString* aSource ) const
{
if( m_clearance.has_value() )
if( m_padStack.Clearance().has_value() )
return GetLocalClearance( aSource );
if( FOOTPRINT* parentFootprint = GetParentFootprint() )
@ -1047,10 +1026,10 @@ int PAD::GetSolderMaskExpansion() const
// Pads defined only on mask layers (and perhaps on other tech layers) use the shape
// defined by the pad settings only. ALL other pads, even those that don't actually have
// any copper (such as NPTH pads with holes the same size as the pad) get mask expansion.
if( ( m_layerMask & LSET::AllCuMask() ).none() )
if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
return 0;
std::optional<int> margin = m_solderMaskMargin;
std::optional<int> margin = m_padStack.SolderMaskMargin();
if( !margin.has_value() )
{
@ -1069,7 +1048,7 @@ int PAD::GetSolderMaskExpansion() const
// ensure mask have a size always >= 0
if( marginValue < 0 )
{
int minsize = -std::min( m_size.x, m_size.y ) / 2;
int minsize = -std::min( m_padStack.Size().x, m_padStack.Size().y ) / 2;
if( marginValue < minsize )
marginValue = minsize;
@ -1084,11 +1063,11 @@ VECTOR2I PAD::GetSolderPasteMargin() const
// Pads defined only on mask layers (and perhaps on other tech layers) use the shape
// defined by the pad settings only. ALL other pads, even those that don't actually have
// any copper (such as NPTH pads with holes the same size as the pad) get paste expansion.
if( ( m_layerMask & LSET::AllCuMask() ).none() )
if( ( m_padStack.LayerSet() & LSET::AllCuMask() ).none() )
return VECTOR2I( 0, 0 );
std::optional<int> margin = m_solderPasteMargin;
std::optional<double> mratio = m_solderPasteMarginRatio;
std::optional<int> margin = m_padStack.SolderPasteMargin();
std::optional<double> mratio = m_padStack.SolderPasteMarginRatio();
if( !margin.has_value() )
{
@ -1115,17 +1094,17 @@ VECTOR2I PAD::GetSolderPasteMargin() const
}
VECTOR2I pad_margin;
pad_margin.x = margin.value_or( 0 ) + KiROUND( m_size.x * mratio.value_or( 0 ) );
pad_margin.y = margin.value_or( 0 ) + KiROUND( m_size.y * mratio.value_or( 0 ) );
pad_margin.x = margin.value_or( 0 ) + KiROUND( m_padStack.Size().x * mratio.value_or( 0 ) );
pad_margin.y = margin.value_or( 0 ) + KiROUND( m_padStack.Size().y * mratio.value_or( 0 ) );
// ensure mask have a size always >= 0
if( m_padShape != PAD_SHAPE::CUSTOM )
if( m_padStack.Shape() != PAD_SHAPE::CUSTOM )
{
if( pad_margin.x < -m_size.x / 2 )
pad_margin.x = -m_size.x / 2;
if( pad_margin.x < -m_padStack.Size().x / 2 )
pad_margin.x = -m_padStack.Size().x / 2;
if( pad_margin.y < -m_size.y / 2 )
pad_margin.y = -m_size.y / 2;
if( pad_margin.y < -m_padStack.Size().y / 2 )
pad_margin.y = -m_padStack.Size().y / 2;
}
return pad_margin;
@ -1134,7 +1113,7 @@ VECTOR2I PAD::GetSolderPasteMargin() const
ZONE_CONNECTION PAD::GetZoneConnectionOverrides( wxString* aSource ) const
{
ZONE_CONNECTION connection = m_zoneConnection;
ZONE_CONNECTION connection = m_padStack.ZoneConnection().value_or( ZONE_CONNECTION::INHERITED );
if( connection != ZONE_CONNECTION::INHERITED )
{
@ -1154,19 +1133,19 @@ ZONE_CONNECTION PAD::GetZoneConnectionOverrides( wxString* aSource ) const
int PAD::GetLocalSpokeWidthOverride( wxString* aSource ) const
{
if( m_thermalSpokeWidth > 0 && aSource )
if( m_padStack.ThermalSpokeWidth().has_value() && aSource )
*aSource = _( "pad" );
return m_thermalSpokeWidth;
return m_padStack.ThermalSpokeWidth().value_or( 0 );
}
int PAD::GetLocalThermalGapOverride( wxString* aSource ) const
{
if( m_thermalGap > 0 && aSource )
if( m_padStack.ThermalGap().has_value() && aSource )
*aSource = _( "pad" );
return m_thermalGap;
return m_padStack.ThermalGap().value_or( 0 );
}
@ -1235,14 +1214,14 @@ void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
aList.emplace_back( ShowPadShape(), props );
if( ( GetShape() == PAD_SHAPE::CIRCLE || GetShape() == PAD_SHAPE::OVAL )
&& m_size.x == m_size.y )
&& m_padStack.Size().x == m_padStack.Size().y )
{
aList.emplace_back( _( "Diameter" ), aFrame->MessageTextFromValue( m_size.x ) );
aList.emplace_back( _( "Diameter" ), aFrame->MessageTextFromValue( m_padStack.Size().x ) );
}
else
{
aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( m_size.x ) );
aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( m_size.y ) );
aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( m_padStack.Size().x ) );
aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( m_padStack.Size().y ) );
}
EDA_ANGLE fp_orient = parentFootprint ? parentFootprint->GetOrientation() : ANGLE_0;
@ -1262,20 +1241,22 @@ void PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>&
aFrame->MessageTextFromValue( GetPadToDieLength() ) );
}
if( m_drill.x > 0 || m_drill.y > 0 )
const VECTOR2I& drill = m_padStack.Drill().size;
if( drill.x > 0 || drill.y > 0 )
{
if( GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
{
aList.emplace_back( _( "Hole" ),
wxString::Format( wxT( "%s" ),
aFrame->MessageTextFromValue( m_drill.x ) ) );
aFrame->MessageTextFromValue( drill.x ) ) );
}
else
{
aList.emplace_back( _( "Hole X / Y" ),
wxString::Format( wxT( "%s / %s" ),
aFrame->MessageTextFromValue( m_drill.x ),
aFrame->MessageTextFromValue( m_drill.y ) ) );
aFrame->MessageTextFromValue( drill.x ),
aFrame->MessageTextFromValue( drill.y ) ) );
}
}
@ -1354,6 +1335,8 @@ int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
{
int diff;
// TODO(JE) move padstack comparision into PADSTACK
if( ( diff = static_cast<int>( aPadRef->GetShape() ) -
static_cast<int>( aPadCmp->GetShape() ) ) != 0 )
return diff;
@ -1362,41 +1345,53 @@ int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
static_cast<int>( aPadCmp->m_attribute ) ) != 0 )
return diff;
if( ( diff = static_cast<int>( aPadRef->m_drillShape ) -
static_cast<int>( aPadCmp->m_drillShape ) ) != 0 )
if( ( diff = static_cast<int>( aPadRef->GetDrillShape() ) -
static_cast<int>( aPadCmp->GetDrillShape() ) ) != 0 )
return diff;
if( ( diff = aPadRef->m_drill.x - aPadCmp->m_drill.x ) != 0 )
if( ( diff = aPadRef->Padstack().Drill().size.x - aPadCmp->Padstack().Drill().size.x ) != 0 )
return diff;
if( ( diff = aPadRef->m_drill.y - aPadCmp->m_drill.y ) != 0 )
if( ( diff = aPadRef->Padstack().Drill().size.y - aPadCmp->Padstack().Drill().size.y ) != 0 )
return diff;
if( ( diff = aPadRef->m_size.x - aPadCmp->m_size.x ) != 0 )
if( ( diff = aPadRef->m_padStack.Size().x - aPadCmp->m_padStack.Size().x ) != 0 )
return diff;
if( ( diff = aPadRef->m_size.y - aPadCmp->m_size.y ) != 0 )
if( ( diff = aPadRef->m_padStack.Size().y - aPadCmp->m_padStack.Size().y ) != 0 )
return diff;
if( ( diff = aPadRef->m_offset.x - aPadCmp->m_offset.x ) != 0 )
if( ( diff = aPadRef->m_padStack.Offset().x - aPadCmp->m_padStack.Offset().x ) != 0 )
return diff;
if( ( diff = aPadRef->m_offset.y - aPadCmp->m_offset.y ) != 0 )
if( ( diff = aPadRef->m_padStack.Offset().y - aPadCmp->m_padStack.Offset().y ) != 0 )
return diff;
if( ( diff = aPadRef->m_deltaSize.x - aPadCmp->m_deltaSize.x ) != 0 )
if( ( diff = aPadRef->m_padStack.TrapezoidDeltaSize().x
- aPadCmp->m_padStack.TrapezoidDeltaSize().x )
!= 0 )
{
return diff;
}
if( ( diff = aPadRef->m_padStack.TrapezoidDeltaSize().y
- aPadCmp->m_padStack.TrapezoidDeltaSize().y )
!= 0 )
{
return diff;
}
if( ( diff = aPadRef->m_padStack.RoundRectRadiusRatio()
- aPadCmp->m_padStack.RoundRectRadiusRatio() )
!= 0 )
{
return diff;
}
if( ( diff = aPadRef->m_padStack.ChamferPositions() - aPadCmp->m_padStack.ChamferPositions() ) != 0 )
return diff;
if( ( diff = aPadRef->m_deltaSize.y - aPadCmp->m_deltaSize.y ) != 0 )
return diff;
if( ( diff = aPadRef->m_roundedCornerScale - aPadCmp->m_roundedCornerScale ) != 0 )
return diff;
if( ( diff = aPadRef->m_chamferPositions - aPadCmp->m_chamferPositions ) != 0 )
return diff;
if( ( diff = aPadRef->m_chamferScale - aPadCmp->m_chamferScale ) != 0 )
if( ( diff = aPadRef->m_padStack.ChamferRatio() - aPadCmp->m_padStack.ChamferRatio() ) != 0 )
return diff;
if( ( diff = static_cast<int>( aPadRef->m_editPrimitives.size() ) -
@ -1410,7 +1405,7 @@ int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
// Lorenzo: gencad also needs it to implement padstacks!
#if __cplusplus >= 201103L
long long d = aPadRef->m_layerMask.to_ullong() - aPadCmp->m_layerMask.to_ullong();
long long d = aPadRef->GetLayerSet().to_ullong() - aPadCmp->GetLayerSet().to_ullong();
if( d < 0 )
return -1;
@ -1420,8 +1415,8 @@ int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
return 0;
#else
// these strings are not typically constructed, since we don't get here often.
std::string s1 = aPadRef->m_layerMask.to_string();
std::string s2 = aPadCmp->m_layerMask.to_string();
std::string s1 = aPadRef->GetLayerSet().to_string();
std::string s2 = aPadCmp->GetLayerSet().to_string();
return s1.compare( s2 );
#endif
}
@ -1687,6 +1682,7 @@ const BOX2I PAD::ViewBBox() const
void PAD::ImportSettingsFrom( const PAD& aMasterPad )
{
SetPadstack( aMasterPad.Padstack() );
SetShape( aMasterPad.GetShape() );
// Layer Set should be updated before calling SetAttribute()
SetLayerSet( aMasterPad.GetLayerSet() );
@ -1717,9 +1713,6 @@ void PAD::ImportSettingsFrom( const PAD& aMasterPad )
SetOrientation( pad_rot );
SetRemoveUnconnected( aMasterPad.GetRemoveUnconnected() );
SetKeepTopBottom( aMasterPad.GetKeepTopBottom() );
SetSize( aMasterPad.GetSize() );
SetDelta( VECTOR2I( 0, 0 ) );
SetOffset( aMasterPad.GetOffset() );
@ -1813,8 +1806,8 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
// This minimal value is mainly for very small pads, like SM0402.
// Most of time pads are using the segment count given by aError value.
const int pad_min_seg_per_circle_count = 16;
int dx = m_size.x / 2;
int dy = m_size.y / 2;
int dx = m_padStack.Size().x / 2;
int dy = m_padStack.Size().y / 2;
VECTOR2I padShapePos = ShapePos(); // Note: for pad having a shape offset, the pad
// position is NOT the shape position
@ -1846,11 +1839,11 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
case PAD_SHAPE::TRAPEZOID:
case PAD_SHAPE::RECTANGLE:
{
int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.x / 2 : 0;
int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_deltaSize.y / 2 : 0;
int ddx = GetShape() == PAD_SHAPE::TRAPEZOID ? m_padStack.TrapezoidDeltaSize().x / 2 : 0;
int ddy = GetShape() == PAD_SHAPE::TRAPEZOID ? m_padStack.TrapezoidDeltaSize().y / 2 : 0;
SHAPE_POLY_SET outline;
TransformTrapezoidToPolygon( outline, padShapePos, m_size, m_orient, ddx, ddy, aClearance,
TransformTrapezoidToPolygon( outline, padShapePos, m_padStack.Size(), m_orient, ddx, ddy, aClearance,
aMaxError, aErrorLoc );
aBuffer.Append( outline );
break;
@ -1862,7 +1855,7 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
bool doChamfer = GetShape() == PAD_SHAPE::CHAMFERED_RECT;
SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, padShapePos, m_size, m_orient,
TransformRoundChamferedRectToPolygon( outline, padShapePos, m_padStack.Size(), m_orient,
GetRoundRectCornerRadius(),
doChamfer ? GetChamferRectRatio() : 0,
doChamfer ? GetChamferPositions() : 0,
@ -2129,6 +2122,12 @@ static struct PAD_DESC
.Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
}
ENUM_MAP<PADSTACK::UNCONNECTED_LAYER_MODE>::Instance()
.Map( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL, _HKI( "All copper layers" ) )
.Map( PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL, _HKI( "Connected layers only" ) )
.Map( PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END,
_HKI( "Front, back and connected layers" ) );
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PAD );
propMgr.InheritsAfter( TYPE_HASH( PAD ), TYPE_HASH( BOARD_CONNECTED_ITEM ) );
@ -2230,6 +2229,11 @@ static struct PAD_DESC
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP>( _HKI( "Fabrication Property" ),
&PAD::SetProperty, &PAD::GetProperty ), groupPad );
auto layerMode = new PROPERTY_ENUM<PAD, PADSTACK::UNCONNECTED_LAYER_MODE>(
_HKI( "Copper Layers" ),
&PAD::SetUnconnectedLayerMode, &PAD::GetUnconnectedLayerMode );
propMgr.AddProperty( layerMode, groupPad );
auto padToDie = new PROPERTY<PAD, int>( _HKI( "Pad To Die Length" ),
&PAD::SetPadToDieLength, &PAD::GetPadToDieLength,
PROPERTY_DISPLAY::PT_SIZE );

View File

@ -40,12 +40,6 @@ class PCB_SHAPE;
class SHAPE;
class SHAPE_SEGMENT;
enum CUST_PAD_SHAPE_IN_ZONE
{
CUST_PAD_SHAPE_IN_ZONE_OUTLINE,
CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL
};
class LINE_READER;
class EDA_3D_CANVAS;
class FOOTPRINT;
@ -183,14 +177,14 @@ public:
*/
void SetShape( PAD_SHAPE aShape )
{
m_padShape = aShape;
m_padStack.SetShape( aShape );
SetDirty();
}
/**
* @return the shape of this pad.
*/
PAD_SHAPE GetShape() const { return m_padShape; }
PAD_SHAPE GetShape() const { return m_padStack.Shape(); }
void SetPosition( const VECTOR2I& aPos ) override
{
@ -203,14 +197,17 @@ public:
/**
* @return the shape of the anchor pad shape, for custom shaped pads.
*/
PAD_SHAPE GetAnchorPadShape() const { return m_anchorPadShape; }
PAD_SHAPE GetAnchorPadShape() const
{
return m_padStack.AnchorShape();
}
/**
* @return the option for the custom pad shape to use as clearance area in copper zones.
*/
CUST_PAD_SHAPE_IN_ZONE GetCustomShapeInZoneOpt() const
PADSTACK::CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
{
return m_customShapeClearanceArea;
return m_padStack.CustomShapeInZoneMode();
}
/**
@ -218,9 +215,9 @@ public:
*
* @param aOption is the clearance area shape CUST_PAD_SHAPE_IN_ZONE option
*/
void SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE aOption )
void SetCustomShapeInZoneOpt( PADSTACK::CUSTOM_SHAPE_ZONE_MODE aOption )
{
m_customShapeClearanceArea = aOption;
m_padStack.SetCustomShapeInZoneMode( aOption );
}
/**
@ -231,7 +228,9 @@ public:
*/
void SetAnchorPadShape( PAD_SHAPE aShape )
{
m_anchorPadShape = ( aShape == PAD_SHAPE::RECTANGLE ) ? PAD_SHAPE::RECTANGLE : PAD_SHAPE::CIRCLE;
m_padStack.SetAnchorShape( aShape == PAD_SHAPE::RECTANGLE
? PAD_SHAPE::RECTANGLE
: PAD_SHAPE::CIRCLE );
SetDirty();
}
@ -243,28 +242,37 @@ public:
void SetY( int y ) { m_pos.y = y; SetDirty(); }
void SetX( int x ) { m_pos.x = x; SetDirty(); }
void SetSize( const VECTOR2I& aSize ) { m_size = aSize; SetDirty(); }
const VECTOR2I& GetSize() const { return m_size; }
void SetSizeX( const int aX ) { if( aX > 0 ) { m_size.x = aX; SetDirty(); } }
int GetSizeX() const { return m_size.x; }
void SetSizeY( const int aY ) { if( aY > 0 ) { m_size.y = aY; SetDirty(); } }
int GetSizeY() const { return m_size.y; }
void SetSize( const VECTOR2I& aSize )
{
m_padStack.Size() = aSize;
SetDirty();
}
const VECTOR2I& GetSize() const { return m_padStack.Size(); }
void SetDelta( const VECTOR2I& aSize ) { m_deltaSize = aSize; SetDirty(); }
const VECTOR2I& GetDelta() const { return m_deltaSize; }
void SetSizeX( const int aX ) { if( aX > 0 ) { m_padStack.Size().x = aX; SetDirty(); } }
int GetSizeX() const { return m_padStack.Size().x; }
void SetSizeY( const int aY ) { if( aY > 0 ) { m_padStack.Size().y = aY; SetDirty(); } }
int GetSizeY() const { return m_padStack.Size().y; }
void SetDrillSize( const VECTOR2I& aSize ) { m_drill = aSize; SetDirty(); }
const VECTOR2I& GetDrillSize() const { return m_drill; }
void SetDrillSizeX( const int aX ) { m_drill.x = aX; SetDirty(); }
int GetDrillSizeX() const { return m_drill.x; }
void SetDrillSizeY( const int aY ) { m_drill.y = aY; SetDirty(); }
int GetDrillSizeY() const { return m_drill.y; }
void SetDelta( const VECTOR2I& aSize ) { m_padStack.TrapezoidDeltaSize() = aSize; SetDirty(); }
const VECTOR2I& GetDelta() const { return m_padStack.TrapezoidDeltaSize(); }
void SetOffset( const VECTOR2I& aOffset ) { m_offset = aOffset; SetDirty(); }
const VECTOR2I& GetOffset() const { return m_offset; }
void SetDrillSize( const VECTOR2I& aSize ) { m_padStack.Drill().size = aSize; SetDirty(); }
const VECTOR2I& GetDrillSize() const { return m_padStack.Drill().size; }
void SetDrillSizeX( const int aX ) { m_padStack.Drill().size.x = aX; SetDirty(); }
int GetDrillSizeX() const { return m_padStack.Drill().size.x; }
void SetDrillSizeY( const int aY ) { m_padStack.Drill().size.y = aY; SetDirty(); }
int GetDrillSizeY() const { return m_padStack.Drill().size.y; }
void SetOffset( const VECTOR2I& aOffset ) { m_padStack.Offset() = aOffset; SetDirty(); }
const VECTOR2I& GetOffset() const { return m_padStack.Offset(); }
VECTOR2I GetCenter() const override { return GetPosition(); }
const PADSTACK& Padstack() const { return m_padStack; }
PADSTACK& Padstack() { return m_padStack; }
void SetPadstack( const PADSTACK& aPadstack ) { m_padStack = aPadstack; }
/**
* Has meaning only for custom shape pads.
* add a free shape to the shape list.
@ -355,8 +363,12 @@ public:
return m_orient.AsDegrees();
}
void SetDrillShape( PAD_DRILL_SHAPE aShape ) { m_drillShape = aShape; m_shapesDirty = true; }
PAD_DRILL_SHAPE GetDrillShape() const { return m_drillShape; }
void SetDrillShape( PAD_DRILL_SHAPE aShape )
{
m_padStack.Drill().shape = aShape;
m_shapesDirty = true;
}
PAD_DRILL_SHAPE GetDrillShape() const { return m_padStack.Drill().shape; }
bool IsDirty() const
{
@ -370,8 +382,8 @@ public:
m_polyDirty[ERROR_OUTSIDE] = true;
}
void SetLayerSet( LSET aLayers ) override { m_layerMask = aLayers; }
LSET GetLayerSet() const override { return m_layerMask; }
void SetLayerSet( LSET aLayers ) override { m_padStack.SetLayerSet( aLayers ); }
LSET GetLayerSet() const override { return m_padStack.LayerSet(); }
void SetAttribute( PAD_ATTRIB aAttribute );
PAD_ATTRIB GetAttribute() const { return m_attribute; }
@ -383,26 +395,41 @@ public:
// format, so for now just infer a copper-less pad to be an APERTURE pad.
bool IsAperturePad() const
{
return ( m_layerMask & LSET::AllCuMask() ).none();
return ( m_padStack.LayerSet() & LSET::AllCuMask() ).none();
}
void SetPadToDieLength( int aLength ) { m_lengthPadToDie = aLength; }
int GetPadToDieLength() const { return m_lengthPadToDie; }
std::optional<int> GetLocalClearance() const override { return m_clearance; }
void SetLocalClearance( std::optional<int> aClearance ) { m_clearance = aClearance; }
std::optional<int> GetLocalClearance() const override { return m_padStack.Clearance(); }
void SetLocalClearance( std::optional<int> aClearance ) { m_padStack.Clearance() = aClearance; }
std::optional<int> GetLocalSolderMaskMargin() const { return m_solderMaskMargin; }
void SetLocalSolderMaskMargin( std::optional<int> aMargin ) { m_solderMaskMargin = aMargin; }
std::optional<int> GetLocalSolderMaskMargin() const { return m_padStack.SolderMaskMargin(); }
void SetLocalSolderMaskMargin( std::optional<int> aMargin )
{
m_padStack.SolderMaskMargin() = aMargin;
}
std::optional<int> GetLocalSolderPasteMargin() const { return m_solderPasteMargin; }
void SetLocalSolderPasteMargin( std::optional<int> aMargin ) { m_solderPasteMargin = aMargin; }
std::optional<int> GetLocalSolderPasteMargin() const { return m_padStack.SolderPasteMargin(); }
void SetLocalSolderPasteMargin( std::optional<int> aMargin )
{
m_padStack.SolderPasteMargin() = aMargin;
}
std::optional<double> GetLocalSolderPasteMarginRatio() const { return m_solderPasteMarginRatio; }
void SetLocalSolderPasteMarginRatio( std::optional<double> aRatio ) { m_solderPasteMarginRatio = aRatio; }
std::optional<double> GetLocalSolderPasteMarginRatio() const
{
return m_padStack.SolderPasteMarginRatio();
}
void SetLocalSolderPasteMarginRatio( std::optional<double> aRatio )
{
m_padStack.SolderPasteMarginRatio() = aRatio;
}
void SetLocalZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; }
ZONE_CONNECTION GetLocalZoneConnection() const { return m_zoneConnection; }
void SetLocalZoneConnection( ZONE_CONNECTION aType ) { m_padStack.ZoneConnection() = aType; }
ZONE_CONNECTION GetLocalZoneConnection() const
{
return m_padStack.ZoneConnection().value_or( ZONE_CONNECTION::INHERITED );
}
/**
* Return the pad's "own" clearance in internal units.
@ -518,8 +545,8 @@ public:
* Set the width of the thermal spokes connecting the pad to a zone. If != 0 this will
* override similar settings in the parent footprint and zone.
*/
void SetThermalSpokeWidth( int aWidth ) { m_thermalSpokeWidth = aWidth; }
int GetThermalSpokeWidth() const { return m_thermalSpokeWidth; }
void SetThermalSpokeWidth( int aWidth ) { m_padStack.ThermalSpokeWidth() = aWidth; }
int GetThermalSpokeWidth() const { return m_padStack.ThermalSpokeWidth().value_or( 0 ); }
int GetLocalSpokeWidthOverride( wxString* aSource = nullptr ) const;
@ -528,21 +555,27 @@ public:
* pads and circular-anchored custom shaped pads), while 90° will produce a + (the default
* for all other shapes).
*/
void SetThermalSpokeAngle( const EDA_ANGLE& aAngle ) { m_thermalSpokeAngle = aAngle; }
EDA_ANGLE GetThermalSpokeAngle() const { return m_thermalSpokeAngle; }
void SetThermalSpokeAngle( const EDA_ANGLE& aAngle )
{
m_padStack.SetThermalSpokeAngle( aAngle );
}
EDA_ANGLE GetThermalSpokeAngle() const
{
return m_padStack.ThermalSpokeAngle();
}
// For property system
void SetThermalSpokeAngleDegrees( double aAngle )
{
m_thermalSpokeAngle = EDA_ANGLE( aAngle, DEGREES_T );
m_padStack.ThermalSpokeAngle() = EDA_ANGLE( aAngle, DEGREES_T );
}
double GetThermalSpokeAngleDegrees() const
{
return m_thermalSpokeAngle.AsDegrees();
return m_padStack.ThermalSpokeAngle().AsDegrees();
}
void SetThermalGap( int aGap ) { m_thermalGap = aGap; }
int GetThermalGap() const { return m_thermalGap; }
void SetThermalGap( int aGap ) { m_padStack.ThermalGap() = aGap; }
int GetThermalGap() const { return m_padStack.ThermalGap().value_or( 0 ); }
int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const;
@ -563,7 +596,7 @@ public:
* Cannot be > 0.5; the normalized IPC-7351C value is 0.25
*/
void SetRoundRectRadiusRatio( double aRadiusScale );
double GetRoundRectRadiusRatio() const { return m_roundedCornerScale; }
double GetRoundRectRadiusRatio() const { return m_padStack.RoundRectRadiusRatio(); }
/**
* Has meaning only for chamfered rectangular pads.
@ -572,7 +605,7 @@ public:
* Cannot be < 0.5.
*/
void SetChamferRectRatio( double aChamferScale );
double GetChamferRectRatio() const { return m_chamferScale; }
double GetChamferRectRatio() const { return m_padStack.ChamferRatio(); }
/**
* Has meaning only for chamfered rectangular pads.
@ -581,8 +614,8 @@ public:
*
* @param aPositions a bit-set of #RECT_CHAMFER_POSITIONS.
*/
void SetChamferPositions( int aPositions ) { m_chamferPositions = aPositions; }
int GetChamferPositions() const { return m_chamferPositions; }
void SetChamferPositions( int aPositions ) { m_padStack.SetChamferPositions( aPositions ); }
int GetChamferPositions() const { return m_padStack.ChamferPositions(); }
/**
* @return the netcode.
@ -591,27 +624,65 @@ public:
void SetSubRatsnest( int aSubRatsnest ) { m_subRatsnest = aSubRatsnest; }
/**
* Set the unconnected removal property.
*
* If true, the copper is removed on zone fill or when specifically requested when the pad
* is not connected on a layer. This requires that there be a through hole.
* @deprecated - use Padstack().SetUnconnectedLayerMode()
* Sets the unconnected removal property. If true, the copper is removed on zone fill
* or when specifically requested when the via is not connected on a layer.
*/
void SetRemoveUnconnected( bool aSet ) { m_removeUnconnectedLayer = aSet; }
bool GetRemoveUnconnected() const { return m_removeUnconnectedLayer; }
void SetRemoveUnconnected( bool aSet )
{
m_padStack.SetUnconnectedLayerMode( aSet
? PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL
: PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
}
bool GetRemoveUnconnected() const
{
return m_padStack.UnconnectedLayerMode() != PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL;
}
/**
* Set whether we keep the top and bottom connections even if they are not connected.
* @deprecated - use Padstack().SetUnconnectedLayerMode()
* Sets whether we keep the start and end annular rings even if they are not connected
*/
void SetKeepTopBottom( bool aSet ) { m_keepTopBottomLayer = aSet; }
bool GetKeepTopBottom() const { return m_keepTopBottomLayer; }
void SetKeepTopBottom( bool aSet )
{
m_padStack.SetUnconnectedLayerMode( aSet
? PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END
: PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL );
}
bool GetKeepTopBottom() const
{
return m_padStack.UnconnectedLayerMode()
== PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END;
}
void SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE aMode )
{
m_padStack.SetUnconnectedLayerMode( aMode );
}
PADSTACK::UNCONNECTED_LAYER_MODE GetUnconnectedLayerMode() const
{
return m_padStack.UnconnectedLayerMode();
}
bool ConditionallyFlashed( PCB_LAYER_ID aLayer ) const
{
if( !m_removeUnconnectedLayer )
switch( m_padStack.UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return false;
if( m_keepTopBottomLayer && ( aLayer == F_Cu || aLayer == B_Cu ) )
return false;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
return true;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
{
if( aLayer == m_padStack.Drill().start || aLayer == m_padStack.Drill().end )
return false;
}
}
return true;
}
@ -620,7 +691,7 @@ public:
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override
{
return m_layerMask[aLayer];
return m_padStack.LayerSet().test( aLayer );
}
/**
@ -761,10 +832,7 @@ private:
VECTOR2I m_pos; // Pad Position on board
PAD_SHAPE m_padShape; // Shape: PAD_SHAPE::CIRCLE, PAD_SHAPE::RECTANGLE,
// PAD_SHAPE::OVAL, PAD_SHAPE::TRAPEZOID,
// PAD_SHAPE::ROUNDRECT, PAD_SHAPE::CHAMFERED_RECT,
// PAD_SHAPE::CUSTOM
PADSTACK m_padStack;
/*
* Editing definitions of primitives for custom pad shapes. In local coordinates relative
* to m_Pos (NOT shapePos) at orient 0.
@ -786,36 +854,6 @@ private:
int m_subRatsnest; // Variable used to handle subnet (block) number in
// ratsnest computations
VECTOR2I m_drill; // Drill diameter (x == y) or slot dimensions (x != y)
VECTOR2I m_size; // X and Y size (relative to orient 0)
PAD_DRILL_SHAPE m_drillShape; // PAD_DRILL_SHAPE::CIRCLE, PAD_DRILL_SHAPE::OBLONG
double m_roundedCornerScale; // Scaling factor of min(width, height) to corner
// radius, default 0.25
double m_chamferScale; // Scaling factor of min(width, height) to chamfer
// size, default 0.25
int m_chamferPositions; // The positions of the chamfers (at orient 0)
PAD_SHAPE m_anchorPadShape; // For custom shaped pads: shape of pad anchor,
// PAD_SHAPE::RECTANGLE, PAD_SHAPE::CIRCLE
/*
* Most of the time the hole is the center of the shape (m_Offset = 0). But some designers
* use oblong/rect pads with a hole moved to one of the oblong/rect pad shape ends.
* In all cases the hole is at the pad position. This offset is from the hole to the center
* of the pad shape (ie: the copper area around the hole).
* ShapePos() returns the board shape position according to the offset and the pad rotation.
*/
VECTOR2I m_offset;
LSET m_layerMask; // Bitwise layer: 1 = copper layer, 15 = cmp,
// 2..14 = internal layers, 16..31 = technical layers
VECTOR2I m_deltaSize; // Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes
// one end and half expands the other. It is only valid
// to have a single axis be non-0.
PAD_ATTRIB m_attribute = PAD_ATTRIB::PTH;
PAD_PROP m_property; // Property in fab files (BGA, FIDUCIAL, TESTPOINT, etc.)
@ -824,41 +862,6 @@ private:
int m_lengthPadToDie; // Length net from pad to die, inside the package
///< If true, the pad copper is removed for layers that are not connected.
bool m_removeUnconnectedLayer;
///< When removing unconnected pads, keep the top and bottom pads.
bool m_keepTopBottomLayer;
/*
* Pad clearances, margins, etc. exist in a hierarchy. If a given level is specified then
* the remaining levels are NOT consulted.
*
* LEVEL 1: (highest priority) local overrides (pad, footprint, etc.)
* LEVEL 2: Rules
* LEVEL 3: Accumulated local settings, netclass settings, & board design settings
*
* These are the LEVEL 1 settings (overrides) for a pad.
*/
std::optional<int> m_clearance;
std::optional<int> m_solderMaskMargin; // Solder mask margin
std::optional<int> m_solderPasteMargin; // Solder paste margin absolute value
std::optional<double> m_solderPasteMarginRatio; // Solder mask margin ratio of pad size
// The final margin is the sum of these 2 values
/*
* How to build the custom shape in zone, to create the clearance area:
* CUST_PAD_SHAPE_IN_ZONE_OUTLINE = use pad shape
* CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL = use the convex hull of the pad shape
*/
CUST_PAD_SHAPE_IN_ZONE m_customShapeClearanceArea;
ZONE_CONNECTION m_zoneConnection; // No connection, thermal relief, etc.
int m_thermalSpokeWidth; // Thermal spoke width.
EDA_ANGLE m_thermalSpokeAngle; // Rotation of the spokes. 45° will produce an X,
// while 90° will produce a +.
int m_thermalGap;
std::mutex m_zoneLayerOverridesMutex;
std::array<ZONE_LAYER_OVERRIDE, MAX_CU_LAYERS> m_zoneLayerOverrides;
};

View File

@ -18,11 +18,66 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <convert_basic_shapes_to_polygon.h> // RECT_CHAMFER_POSITIONS
#include "padstack.h"
PADSTACK::PADSTACK()
PADSTACK::PADSTACK() :
m_mode( MODE::NORMAL ),
m_unconnectedLayerMode( UNCONNECTED_LAYER_MODE::KEEP_ALL ),
m_customShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE::OUTLINE )
{
m_defaultCopperProps.shape = SHAPE_PROPS();
m_defaultCopperProps.zone_connection = ZONE_CONNECTION::INHERITED;
m_defaultCopperProps.thermal_spoke_width = std::nullopt;
m_defaultCopperProps.thermal_spoke_angle = ANGLE_45;
m_defaultCopperProps.thermal_gap = std::nullopt;
m_drill.shape = PAD_DRILL_SHAPE::CIRCLE;
m_drill.start = F_Cu;
m_drill.end = B_Cu;
m_secondaryDrill.start = UNDEFINED_LAYER;
m_secondaryDrill.end = UNDEFINED_LAYER;
}
PADSTACK::PADSTACK( const PADSTACK& aOther )
{
*this = aOther;
}
PADSTACK& PADSTACK::operator=( const PADSTACK &aOther )
{
m_mode = aOther.m_mode;
m_layerSet = aOther.m_layerSet;
m_customName = aOther.m_customName;
m_defaultCopperProps = aOther.m_defaultCopperProps;
m_defaultOuterProps = aOther.m_defaultOuterProps;
m_unconnectedLayerMode = aOther.m_unconnectedLayerMode;
m_copperOverrides = aOther.m_copperOverrides;
m_topOverrides = aOther.m_topOverrides;
m_bottomOverrides = aOther.m_bottomOverrides;
m_drill = aOther.m_drill;
m_secondaryDrill = aOther.m_secondaryDrill;
return *this;
}
bool PADSTACK::operator==( const PADSTACK& aOther ) const
{
return m_mode == aOther.m_mode
&& m_layerSet == aOther.m_layerSet
&& m_customName == aOther.m_customName
&& m_defaultCopperProps == aOther.m_defaultCopperProps
&& m_defaultOuterProps == aOther.m_defaultOuterProps
&& m_unconnectedLayerMode == aOther.m_unconnectedLayerMode
&& m_copperOverrides == aOther.m_copperOverrides
&& m_topOverrides == aOther.m_topOverrides
&& m_bottomOverrides == aOther.m_bottomOverrides
&& m_drill == aOther.m_drill
&& m_secondaryDrill == aOther.m_secondaryDrill;
}
@ -39,5 +94,290 @@ void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
wxString PADSTACK::Name() const
{
// TODO
return wxEmptyString;
}
PADSTACK::SHAPE_PROPS::SHAPE_PROPS() :
shape( PAD_SHAPE::CIRCLE ),
anchor_shape( PAD_SHAPE::CIRCLE ),
round_rect_corner_radius( 0 ),
round_rect_radius_ratio( 0.25 ),
chamfered_rect_ratio( 0.2 ),
chamfered_rect_positions( RECT_NO_CHAMFER )
{
}
bool PADSTACK::SHAPE_PROPS::operator==( const SHAPE_PROPS& aOther ) const
{
return shape == aOther.shape && offset == aOther.offset
&& round_rect_corner_radius == aOther.round_rect_corner_radius
&& round_rect_radius_ratio == aOther.round_rect_radius_ratio
&& chamfered_rect_ratio == aOther.chamfered_rect_ratio
&& chamfered_rect_positions == aOther.chamfered_rect_positions;
}
bool PADSTACK::COPPER_LAYER_PROPS::operator==( const COPPER_LAYER_PROPS& aOther ) const
{
return shape == aOther.shape && zone_connection == aOther.zone_connection
&& thermal_spoke_width == aOther.thermal_spoke_width
&& thermal_spoke_angle == aOther.thermal_spoke_angle
&& thermal_gap == aOther.thermal_gap
&& custom_shapes == aOther.custom_shapes;
}
bool PADSTACK::OUTER_LAYER_PROPS::operator==( const OUTER_LAYER_PROPS& aOther ) const
{
return solder_mask_margin == aOther.solder_mask_margin
&& solder_paste_margin == aOther.solder_paste_margin
&& solder_paste_margin_ratio == aOther.solder_paste_margin_ratio
&& has_solder_mask == aOther.has_solder_mask
&& has_solder_paste == aOther.has_solder_paste;
}
bool PADSTACK::DRILL_PROPS::operator==( const DRILL_PROPS& aOther ) const
{
return size == aOther.size && shape == aOther.shape
&& start == aOther.start && end == aOther.end;
}
PAD_SHAPE PADSTACK::Shape( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.shape;
}
void PADSTACK::SetShape( PAD_SHAPE aShape, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().shape.shape = aShape;
}
VECTOR2I& PADSTACK::Size( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().shape.size;
}
const VECTOR2I& PADSTACK::Size( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.size;
}
PAD_DRILL_SHAPE PADSTACK::DrillShape( PCB_LAYER_ID aLayer ) const
{
return m_drill.shape;
}
void PADSTACK::SetDrillShape( PAD_DRILL_SHAPE aShape, PCB_LAYER_ID aLayer )
{
m_drill.shape = aShape;
}
VECTOR2I& PADSTACK::Offset( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().shape.offset;
}
const VECTOR2I& PADSTACK::Offset( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.offset;
}
PAD_SHAPE PADSTACK::AnchorShape( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.anchor_shape;
}
void PADSTACK::SetAnchorShape( PAD_SHAPE aShape, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().shape.anchor_shape = aShape;
}
VECTOR2I& PADSTACK::TrapezoidDeltaSize( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().shape.trapezoid_delta_size;
}
const VECTOR2I& PADSTACK::TrapezoidDeltaSize( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.trapezoid_delta_size;
}
double PADSTACK::RoundRectRadiusRatio( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.round_rect_radius_ratio;
}
void PADSTACK::SetRoundRectRadiusRatio( double aRatio, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().shape.round_rect_radius_ratio = aRatio;
}
int PADSTACK::RoundRectRadius( PCB_LAYER_ID aLayer ) const
{
const VECTOR2I& size = Size( aLayer );
return KiROUND( std::min( size.x, size.y ) * RoundRectRadiusRatio( aLayer ) );
}
void PADSTACK::SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer )
{
const VECTOR2I& size = Size( aLayer );
int min_r = std::min( size.x, size.y );
if( min_r > 0 )
SetRoundRectRadiusRatio( aRadius / min_r, aLayer );
}
double PADSTACK::ChamferRatio( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.chamfered_rect_ratio;
}
void PADSTACK::SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().shape.chamfered_rect_ratio = aRatio;
}
int& PADSTACK::ChamferPositions( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().shape.chamfered_rect_positions;
}
const int& PADSTACK::ChamferPositions( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().shape.chamfered_rect_positions;
}
void PADSTACK::SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().shape.chamfered_rect_positions = aPositions;
}
std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().clearance;
}
const std::optional<int>& PADSTACK::Clearance( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().clearance;
}
std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer )
{
return OuterLayerDefaults().solder_mask_margin;
}
const std::optional<int>& PADSTACK::SolderMaskMargin( PCB_LAYER_ID aLayer ) const
{
return OuterLayerDefaults().solder_mask_margin;
}
std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer )
{
return OuterLayerDefaults().solder_paste_margin;
}
const std::optional<int>& PADSTACK::SolderPasteMargin( PCB_LAYER_ID aLayer ) const
{
return OuterLayerDefaults().solder_paste_margin;
}
std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer )
{
return OuterLayerDefaults().solder_paste_margin_ratio;
}
const std::optional<double>& PADSTACK::SolderPasteMarginRatio( PCB_LAYER_ID aLayer ) const
{
return OuterLayerDefaults().solder_paste_margin_ratio;
}
std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().zone_connection;
}
const std::optional<ZONE_CONNECTION>& PADSTACK::ZoneConnection( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().zone_connection;
}
std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().thermal_spoke_width;
}
const std::optional<int>& PADSTACK::ThermalSpokeWidth( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().thermal_spoke_width;
}
std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer )
{
return CopperLayerDefaults().thermal_gap;
}
const std::optional<int>& PADSTACK::ThermalGap( PCB_LAYER_ID aLayer ) const
{
return CopperLayerDefaults().thermal_gap;
}
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 );
}
void PADSTACK::SetThermalSpokeAngle( EDA_ANGLE aAngle, PCB_LAYER_ID aLayer )
{
CopperLayerDefaults().thermal_spoke_angle = aAngle;
}
IMPLEMENT_ENUM_TO_WXANY( PADSTACK::UNCONNECTED_LAYER_MODE )

View File

@ -26,8 +26,14 @@
#include <wx/string.h>
#include <api/serializable.h>
#include <geometry/eda_angle.h>
#include <layer_ids.h>
#include <math/vector2d.h>
#include <properties/property.h>
#include <zones.h>
class PCB_SHAPE;
/**
* The set of pad shapes, used with PAD::{Set,Get}Shape()
@ -92,6 +98,19 @@ enum class PAD_PROP
};
/**
* A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of
* the word. This means that a PCB_PAD has a padstack, but also a PCB_VIA. The padstack for
* a pad defines its geometry on copper, soldermask, and paste layers, as well as any drilling
* or milling associated with the pad (round or slot hole, back-drilling, etc). Padstacks also
* define thermal relief settings for all copper layers, clearance overrides for all copper
* layers, and potentially other properties in the future. In other words, the padstack defines
* most of the geometric features of a pad on all layers. It does not define electrical properties
* or other metadata.
*
* For padstacks that do not vary between layers, F_Cu is used as the copper layer to store all
* padstack properties.
*/
class PADSTACK : public SERIALIZABLE
{
public:
@ -111,35 +130,243 @@ public:
CUSTOM ///< Shapes can be defined on arbitrary layers
};
///! Whether or not to remove the copper shape for unconnected layers
enum class UNCONNECTED_LAYER_MODE
{
KEEP_ALL,
REMOVE_ALL,
REMOVE_EXCEPT_START_AND_END
};
enum class CUSTOM_SHAPE_ZONE_MODE
{
OUTLINE,
CONVEXHULL
};
///! The set of properties that define a pad's shape on a given layer
struct SHAPE_PROPS
{
PAD_SHAPE shape; ///< Shape of the pad
PAD_SHAPE anchor_shape; ///< Shape of the anchor when shape == PAD_SHAPE::CUSTOM
VECTOR2I size; ///< Size of the shape, or of the anchor pad for custom shape pads
/*
* Most of the time the hole is the center of the shape (m_Offset = 0). But some designers
* use oblong/rect pads with a hole moved to one of the oblong/rect pad shape ends.
* In all cases the hole is at the pad position. This offset is from the hole to the center
* of the pad shape (ie: the copper area around the hole).
* ShapePos() returns the board shape position according to the offset and the pad rotation.
*/
VECTOR2I offset; ///< Offset of the shape center from the pad center
double round_rect_corner_radius;
double round_rect_radius_ratio;
double chamfered_rect_ratio; ///< Size of chamfer: ratio of smallest of X,Y size
int chamfered_rect_positions; ///< @see RECT_CHAMFER_POSITIONS
/**
* Delta for PAD_SHAPE::TRAPEZOID; half the delta squeezes one end and half expands the
* other. It is only valid to have a single axis be non-zero.
*/
VECTOR2I trapezoid_delta_size;
SHAPE_PROPS();
bool operator==( const SHAPE_PROPS& aOther ) const;
};
/**
* The features of a padstack that can vary between copper layers
* All parameters are optional; leaving them un-set means "use parent/rule defaults"
* Pad clearances, margins, etc. exist in a hierarchy. If a given level is specified then
* the remaining levels are NOT consulted.
*
* LEVEL 1: (highest priority) local overrides (pad, footprint, etc.)
* LEVEL 2: Rules
* LEVEL 3: Accumulated local settings, netclass settings, & board design settings
*
* These are the LEVEL 1 settings (overrides) for a pad.
*/
struct COPPER_LAYER_PROPS
{
SHAPE_PROPS shape;
std::optional<ZONE_CONNECTION> zone_connection;
std::optional<int> thermal_spoke_width;
std::optional<EDA_ANGLE> thermal_spoke_angle;
std::optional<int> thermal_gap;
std::optional<int> clearance;
std::vector<std::shared_ptr<PCB_SHAPE>> custom_shapes;
bool operator==( const COPPER_LAYER_PROPS& aOther ) const;
};
///! The features of a padstack that can vary on outer layers.
///! All parameters are optional; leaving them un-set means "use parent/rule defaults"
struct OUTER_LAYER_PROPS
{
std::optional<int> solder_mask_margin;
std::optional<int> solder_paste_margin;
std::optional<double> solder_paste_margin_ratio;
ZONE_CONNECTION zone_connection;
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
bool operator==( const OUTER_LAYER_PROPS& aOther ) const;
};
struct INNER_LAYER_PROPS
///! The properties of a padstack drill. Drill position is always the pad position (origin).
struct DRILL_PROPS
{
ZONE_CONNECTION zone_connection;
VECTOR2I size; ///< Drill diameter (x == y) or slot dimensions (x != y)
PAD_DRILL_SHAPE shape;
PCB_LAYER_ID start;
PCB_LAYER_ID end;
bool operator==( const DRILL_PROPS& aOther ) const;
};
public:
PADSTACK();
virtual ~PADSTACK() = default;
PADSTACK( const PADSTACK& aOther );
PADSTACK& operator=( const PADSTACK &aOther );
bool operator==( const PADSTACK& aOther ) const;
bool operator!=( const PADSTACK& aOther ) const { return !operator==( aOther ); }
void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) override;
const LSET& LayerSet() const { return m_layerSet; }
LSET& LayerSet() { return m_layerSet; }
void SetLayerSet( const LSET& aSet ) { m_layerSet = aSet; }
///! Returns the name of this padstack in IPC-7351 format
wxString Name() const;
DRILL_PROPS& Drill() { return m_drill; }
const DRILL_PROPS& Drill() const { return m_drill; }
DRILL_PROPS& SecondaryDrill() { return m_secondaryDrill; }
const DRILL_PROPS& SecondaryDrill() const { return m_secondaryDrill; }
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const { return m_unconnectedLayerMode; }
void SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE aMode ) { m_unconnectedLayerMode = aMode; }
COPPER_LAYER_PROPS& CopperLayerDefaults() { return m_defaultCopperProps; }
const COPPER_LAYER_PROPS& CopperLayerDefaults() const { return m_defaultCopperProps; }
OUTER_LAYER_PROPS& OuterLayerDefaults() { return m_defaultOuterProps; }
const OUTER_LAYER_PROPS& OuterLayerDefaults() const { return m_defaultOuterProps; }
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const { return m_customShapeInZoneMode; }
void SetCustomShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE aM ) { m_customShapeInZoneMode = aM; }
// The following section has convenience getters for the padstack properties on a given layer.
PAD_SHAPE Shape( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetShape( PAD_SHAPE aShape, PCB_LAYER_ID aLayer = F_Cu );
VECTOR2I& Size( PCB_LAYER_ID aLayer = F_Cu );
const VECTOR2I& Size( PCB_LAYER_ID aLayer = F_Cu ) const;
PAD_DRILL_SHAPE DrillShape( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetDrillShape( PAD_DRILL_SHAPE aShape, PCB_LAYER_ID aLayer = F_Cu );
VECTOR2I& Offset( PCB_LAYER_ID aLayer = F_Cu );
const VECTOR2I& Offset( PCB_LAYER_ID aLayer = F_Cu ) const;
PAD_SHAPE AnchorShape( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetAnchorShape( PAD_SHAPE aShape, PCB_LAYER_ID aLayer = F_Cu );
VECTOR2I& TrapezoidDeltaSize( PCB_LAYER_ID aLayer = F_Cu );
const VECTOR2I& TrapezoidDeltaSize( PCB_LAYER_ID aLayer = F_Cu ) const;
double RoundRectRadiusRatio( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetRoundRectRadiusRatio( double aRatio, PCB_LAYER_ID aLayer = F_Cu );
int RoundRectRadius( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetRoundRectRadius( double aRadius, PCB_LAYER_ID aLayer = F_Cu );
double ChamferRatio( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetChamferRatio( double aRatio, PCB_LAYER_ID aLayer = F_Cu );
int& ChamferPositions( PCB_LAYER_ID aLayer = F_Cu );
const int& ChamferPositions( PCB_LAYER_ID aLayer = F_Cu ) const;
void SetChamferPositions( int aPositions, PCB_LAYER_ID aLayer = F_Cu );
std::optional<int>& Clearance( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<int>& Clearance( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<int>& SolderMaskMargin( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<int>& SolderMaskMargin( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<int>& SolderPasteMargin( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<int>& SolderPasteMargin( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<double>& SolderPasteMarginRatio( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<double>& SolderPasteMarginRatio( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<ZONE_CONNECTION>& ZoneConnection( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<ZONE_CONNECTION>& ZoneConnection( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<int>& ThermalSpokeWidth( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<int>& ThermalSpokeWidth( PCB_LAYER_ID aLayer = F_Cu ) const;
std::optional<int>& ThermalGap( PCB_LAYER_ID aLayer = F_Cu );
const std::optional<int>& ThermalGap( 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 );
private:
MODE m_mode;
///! The board layers that this padstack is active on
LSET m_layerSet;
///! An override for the IPC-7351 padstack name
wxString m_customName;
///! The properties applied to copper layers if they aren't overridden
COPPER_LAYER_PROPS m_defaultCopperProps;
///! The properties applied to outer technical layers if they aren't overridden
OUTER_LAYER_PROPS m_defaultOuterProps;
UNCONNECTED_LAYER_MODE m_unconnectedLayerMode;
/**
* How to build the custom shape in zone, to create the clearance area:
* CUSTOM_SHAPE_ZONE_MODE::OUTLINE = use pad shape
* CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL = use the convex hull of the pad shape
*/
CUSTOM_SHAPE_ZONE_MODE m_customShapeInZoneMode;
///! Any entries here override the copper layer settings on the given copper layer.
///! If m_mode == MODE::TOP_INNER_BOTTOM, the inner layer setting is always In1_Cu and the only
///! keys in this map that are used are F_Cu, In1_Cu, and B_Cu.
///! If m_mode == MODE::NORMAL, this map is ignored.
std::unordered_map<PCB_LAYER_ID, COPPER_LAYER_PROPS> m_copperOverrides;
///! Any non-null optional values here override the mask/paste settings for the top layers
OUTER_LAYER_PROPS m_topOverrides;
///! Any non-null optional values here override the mask/paste settings for bottom layers
OUTER_LAYER_PROPS m_bottomOverrides;
///! The primary drill parameters, which also define the start and end layers for through-hole
///! vias and pads (F_Cu to B_Cu for normal holes; a subset of layers for blind/buried vias)
DRILL_PROPS m_drill;
///! Secondary drill, used to define back-drilling
DRILL_PROPS m_secondaryDrill;
};
#ifndef SWIG
DECLARE_ENUM_TO_WXANY( PADSTACK::UNCONNECTED_LAYER_MODE );
#endif
#endif //KICAD_PADSTACK_H

View File

@ -2568,7 +2568,6 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
LSET padLayers;
padLayers.set( aLayer );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetAttribute( PAD_ATTRIB::SMD );
pad->SetShape( PAD_SHAPE::CUSTOM );
pad->SetThermalSpokeAngle( ANGLE_90 );
@ -3016,8 +3015,6 @@ void ALTIUM_PCB::ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, con
{
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetNumber( aElem.name );
pad->SetNetCode( GetNetCode( aElem.net ) );

View File

@ -898,7 +898,6 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadLibraryCoppers( const SYMDEF_PCB& aComponen
anchorPad = aComponent.ComponentPads.at( compCopper.AssociatedPadIDs.front() );
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetAttribute( PAD_ATTRIB::SMD );
pad->SetLayerSet( copperLayers );
pad->SetNumber( anchorPad.Identifier.IsEmpty()
@ -1342,8 +1341,6 @@ PAD* CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPad( const COMPONENT_PAD& aCadstarPad,
m_padcodesTested.insert( csPadcode.ID );
}
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
return pad.release();
}

View File

@ -2450,8 +2450,6 @@ void PCB_IO_EAGLE::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aC
PAD* pad = new PAD( aFootprint );
aFootprint->Add( pad );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetShape( PAD_SHAPE::CIRCLE );
pad->SetAttribute( PAD_ATTRIB::NPTH );
@ -2493,8 +2491,6 @@ void PCB_IO_EAGLE::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
aFootprint->Add( pad );
transferPad( e, pad );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetShape( PAD_SHAPE::RECTANGLE );
pad->SetAttribute( PAD_ATTRIB::SMD );

View File

@ -1697,7 +1697,7 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad, int aNestLevel ) const
m_out->Print( 0, "\n");
m_out->Print( aNestLevel+1, "(options" );
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
if( aPad->GetCustomShapeInZoneOpt() == PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL )
m_out->Print( 0, " (clearance convexhull)" );
#if 1 // Set to 1 to output the default option
else
@ -2199,12 +2199,20 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack, int aNestLevel ) const
m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
if( via->GetRemoveUnconnected() )
switch( via->Padstack().UnconnectedLayerMode() )
{
KICAD_FORMAT::FormatBool( m_out, 0, "remove_unused_layers",
via->GetRemoveUnconnected() );
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
m_out->Print( 0, "(remove_unused_layers yes)" );
m_out->Print( 0, "(keep_end_layers no)" );
break;
KICAD_FORMAT::FormatBool( m_out, 0, "keep_end_layers", via->GetKeepStartEnd() );
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
m_out->Print( 0, "(remove_unused_layers yes)" );
m_out->Print( 0, "(keep_end_layers yes)" );
break;
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
break;
}
if( via->IsLocked() )

View File

@ -4699,9 +4699,6 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
// File only contains a token if KeepTopBottom is true
pad->SetKeepTopBottom( false );
NeedSYMBOLorNUMBER();
pad->SetNumber( FromUTF8() );
@ -5286,11 +5283,11 @@ bool PCB_IO_KICAD_SEXPR_PARSER::parsePAD_option( PAD* aPad )
switch( token )
{
case T_outline:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_OUTLINE );
aPad->SetCustomShapeInZoneOpt( PADSTACK::CUSTOM_SHAPE_ZONE_MODE::OUTLINE );
break;
case T_convexhull:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
aPad->SetCustomShapeInZoneOpt( PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL );
break;
default:
@ -5715,8 +5712,7 @@ PCB_VIA* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_VIA()
std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
// File format default is no-token == no-feature.
via->SetRemoveUnconnected( false );
via->SetKeepStartEnd( false );
via->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{

View File

@ -86,11 +86,11 @@ PCB_VIA::PCB_VIA( BOARD_ITEM* aParent ) :
PCB_TRACK( aParent, PCB_VIA_T )
{
SetViaType( VIATYPE::THROUGH );
m_bottomLayer = B_Cu;
Padstack().Drill().start = F_Cu;
Padstack().Drill().end = B_Cu;
SetDrillDefault();
m_removeUnconnectedLayer = false;
m_keepStartEndLayer = true;
Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
m_zoneLayerOverrides.fill( ZLO_NONE );
@ -118,11 +118,8 @@ PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
m_CachedLOD = aOther.m_CachedLOD;
m_CachedScale = aOther.m_CachedScale;
m_bottomLayer = aOther.m_bottomLayer;
m_viaType = aOther.m_viaType;
m_drill = aOther.m_drill;
m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
m_keepStartEndLayer = aOther.m_keepStartEndLayer;
m_padStack = aOther.m_padStack;
m_isFree = aOther.m_isFree;
return *this;
@ -249,10 +246,8 @@ bool PCB_VIA::operator==( const BOARD_ITEM& aOther ) const
const PCB_VIA& other = static_cast<const PCB_VIA&>( aOther );
return m_Start == other.m_Start && m_End == other.m_End && m_layer == other.m_layer &&
m_bottomLayer == other.m_bottomLayer && m_Width == other.m_Width &&
m_viaType == other.m_viaType && m_drill == other.m_drill &&
m_removeUnconnectedLayer == other.m_removeUnconnectedLayer &&
m_keepStartEndLayer == other.m_keepStartEndLayer &&
m_padStack == other.m_padStack && m_Width == other.m_Width &&
m_viaType == other.m_viaType &&
m_zoneLayerOverrides == other.m_zoneLayerOverrides;
}
@ -278,21 +273,12 @@ double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
if( m_End != other.m_End )
similarity *= 0.9;
if( m_bottomLayer != other.m_bottomLayer )
if( m_padStack != other.m_padStack )
similarity *= 0.9;
if( m_viaType != other.m_viaType )
similarity *= 0.9;
if( m_drill != other.m_drill )
similarity *= 0.9;
if( m_removeUnconnectedLayer != other.m_removeUnconnectedLayer )
similarity *= 0.9;
if( m_keepStartEndLayer != other.m_keepStartEndLayer )
similarity *= 0.9;
if( m_zoneLayerOverrides != other.m_zoneLayerOverrides )
similarity *= 0.9;
@ -389,14 +375,16 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
via.mutable_position()->set_x_nm( GetPosition().x );
via.mutable_position()->set_y_nm( GetPosition().y );
const PADSTACK& stack = Padstack();
kiapi::board::types::PadStack* padstack = via.mutable_pad_stack();
padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED
? kiapi::board::types::PadStackType::PST_BLIND_BURIED
: kiapi::board::types::PadStackType::PST_THROUGH );
padstack->set_start_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( m_layer ) );
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( stack.Drill().start ) );
padstack->set_end_layer(
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( m_bottomLayer ) );
ToProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( stack.Drill().end ) );
kiapi::common::PackVector2( *padstack->mutable_drill_diameter(),
{ GetDrillValue(), GetDrillValue() } );
@ -405,23 +393,12 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
kiapi::common::PackVector2( *stackLayer->mutable_size(),
{ GetWidth(), GetWidth() } );
kiapi::board::types::UnconnectedLayerRemoval ulr;
if( m_removeUnconnectedLayer )
{
if( m_keepStartEndLayer )
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END;
else
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE;
}
else
{
ulr = kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP;
}
// TODO: Microvia status is ignored here. Do we still need it?
padstack->set_unconnected_layer_removal( ulr );
padstack->set_unconnected_layer_removal(
ToProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE,
kiapi::board::types::UnconnectedLayerRemoval>(
Padstack().UnconnectedLayerMode() ) );
via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED );
@ -466,36 +443,19 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
if( GetViaType() != VIATYPE::THROUGH )
{
m_layer = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
Padstack().Drill().start = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
padstack.start_layer() );
m_bottomLayer = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
Padstack().Drill().end = FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>(
padstack.end_layer() );
}
else
{
m_layer = F_Cu;
m_bottomLayer = B_Cu;
}
switch( padstack.unconnected_layer_removal() )
{
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE:
m_removeUnconnectedLayer = true;
m_keepStartEndLayer = false;
break;
case kiapi::board::types::UnconnectedLayerRemoval::ULR_REMOVE_EXCEPT_START_AND_END:
m_removeUnconnectedLayer = true;
m_keepStartEndLayer = true;
break;
default:
case kiapi::board::types::UnconnectedLayerRemoval::ULR_KEEP:
m_removeUnconnectedLayer = false;
m_keepStartEndLayer = false;
break;
Padstack().Drill().start = F_Cu;
Padstack().Drill().end = B_Cu;
}
Padstack().SetUnconnectedLayerMode( FromProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE>(
padstack.unconnected_layer_removal() ) );
SetNetCode( via.net().code().value() );
SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED );
@ -599,8 +559,8 @@ int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
int PCB_VIA::GetDrillValue() const
{
if( m_drill > 0 ) // Use the specific value.
return m_drill;
if( m_padStack.Drill().size.x > 0 ) // Use the specific value.
return m_padStack.Drill().size.x;
// Use the default value from the Netclass
NETCLASS* netclass = GetEffectiveNetClass();
@ -833,7 +793,7 @@ INSPECT_RESULT PCB_TRACK::Visit( INSPECTOR inspector, void* testData,
std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
{
return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), m_drill );
return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), Padstack().Drill().size.x );
}
@ -862,7 +822,7 @@ bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const
return GetLayerSet().test( aLayer );
#endif
if( aLayer >= m_layer && aLayer <= m_bottomLayer )
if( aLayer >= Padstack().Drill().start && aLayer <= Padstack().Drill().end )
return true;
if( !IsTented() )
@ -877,20 +837,32 @@ bool PCB_VIA::IsOnLayer( PCB_LAYER_ID aLayer ) const
}
PCB_LAYER_ID PCB_VIA::GetLayer() const
{
return Padstack().Drill().start;
}
void PCB_VIA::SetLayer( PCB_LAYER_ID aLayer )
{
Padstack().Drill().start = aLayer;
}
LSET PCB_VIA::GetLayerSet() const
{
LSET layermask;
if( m_layer < PCBNEW_LAYER_ID_START )
if( Padstack().Drill().start < PCBNEW_LAYER_ID_START )
return layermask;
if( GetViaType() == VIATYPE::THROUGH )
layermask = LSET::AllCuMask();
else
wxASSERT( m_layer <= m_bottomLayer );
wxASSERT( Padstack().Drill().start <= Padstack().Drill().end );
// PCB_LAYER_IDs are numbered from front to back, this is top to bottom.
for( int id = m_layer; id <= m_bottomLayer; ++id )
for( int id = Padstack().Drill().start; id <= Padstack().Drill().end; ++id )
layermask.set( id );
if( !IsTented() )
@ -918,11 +890,11 @@ void PCB_VIA::SetLayerSet( LSET aLayerSet )
if( first )
{
m_layer = layer;
Padstack().Drill().start = layer;
first = false;
}
m_bottomLayer = layer;
Padstack().Drill().end = layer;
}
}
@ -930,21 +902,21 @@ void PCB_VIA::SetLayerSet( LSET aLayerSet )
void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
{
m_layer = aTopLayer;
m_bottomLayer = aBottomLayer;
Padstack().Drill().start = aTopLayer;
Padstack().Drill().end = aBottomLayer;
SanitizeLayers();
}
void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
{
m_layer = aLayer;
Padstack().Drill().start = aLayer;
}
void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
{
m_bottomLayer = aLayer;
Padstack().Drill().end = aLayer;
}
@ -955,8 +927,8 @@ void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) c
if( GetViaType() != VIATYPE::THROUGH )
{
b_layer = m_bottomLayer;
t_layer = m_layer;
b_layer = Padstack().Drill().end;
t_layer = Padstack().Drill().start;
if( b_layer < t_layer )
std::swap( b_layer, t_layer );
@ -972,13 +944,13 @@ void PCB_VIA::LayerPair( PCB_LAYER_ID* top_layer, PCB_LAYER_ID* bottom_layer ) c
PCB_LAYER_ID PCB_VIA::TopLayer() const
{
return m_layer;
return Padstack().Drill().start;
}
PCB_LAYER_ID PCB_VIA::BottomLayer() const
{
return m_bottomLayer;
return Padstack().Drill().end;
}
@ -986,12 +958,12 @@ void PCB_VIA::SanitizeLayers()
{
if( GetViaType() == VIATYPE::THROUGH )
{
m_layer = F_Cu;
m_bottomLayer = B_Cu;
Padstack().Drill().start = F_Cu;
Padstack().Drill().end = B_Cu;
}
if( m_bottomLayer < m_layer )
std::swap( m_bottomLayer, m_layer );
if( Padstack().Drill().end < Padstack().Drill().start )
std::swap( Padstack().Drill().end, Padstack().Drill().start );
}
@ -1021,12 +993,28 @@ bool PCB_VIA::FlashLayer( int aLayer ) const
if( !IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) ) )
return false;
if( !m_removeUnconnectedLayer || !IsCopperLayer( aLayer ) )
if( !IsCopperLayer( aLayer ) )
return true;
if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
switch( Padstack().UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return true;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
{
if( aLayer == Padstack().Drill().start || aLayer == Padstack().Drill().end )
return true;
// Check for removal below
break;
}
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
// Check for removal below
break;
}
// Must be static to keep from raising its ugly head in performance profiles
static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T };
@ -1736,8 +1724,7 @@ static struct TRACK_VIA_DESC
&PCB_VIA::SetWidth, &PCB_VIA::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
&PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ),
new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID, BOARD_ITEM>( _HKI( "Layer Top" ),
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Top" ),
&PCB_VIA::SetLayer, &PCB_VIA::GetLayer ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
&PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia );

View File

@ -43,6 +43,7 @@
#include <geometry/shape_segment.h>
#include <core/minoptmax.h>
#include <core/arraydim.h>
#include <padstack.h>
class PCB_TRACK;
class PCB_VIA;
@ -410,6 +411,10 @@ public:
VIATYPE GetViaType() const { return m_viaType; }
void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; }
const PADSTACK& Padstack() const { return m_padStack; }
PADSTACK& Padstack() { return m_padStack; }
void SetPadstack( const PADSTACK& aPadstack ) { m_padStack = aPadstack; }
bool HasHole() const override
{
return true;
@ -423,6 +428,9 @@ public:
bool IsTented() const override;
int GetSolderMaskExpansion() const;
PCB_LAYER_ID GetLayer() const override;
void SetLayer( PCB_LAYER_ID aLayer ) override;
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
virtual LSET GetLayerSet() const override;
@ -494,25 +502,55 @@ public:
int GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const;
/**
* @deprecated - use Padstack().SetUnconnectedLayerMode()
* Sets the unconnected removal property. If true, the copper is removed on zone fill
* or when specifically requested when the via is not connected on a layer.
*/
void SetRemoveUnconnected( bool aSet ) { m_removeUnconnectedLayer = aSet; }
bool GetRemoveUnconnected() const { return m_removeUnconnectedLayer; }
void SetRemoveUnconnected( bool aSet )
{
m_padStack.SetUnconnectedLayerMode( aSet
? PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL
: PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
}
bool GetRemoveUnconnected() const
{
return m_padStack.UnconnectedLayerMode() != PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL;
}
/**
* @deprecated - use Padstack().SetUnconnectedLayerMode()
* Sets whether we keep the start and end annular rings even if they are not connected
*/
void SetKeepStartEnd( bool aSet ) { m_keepStartEndLayer = aSet; }
bool GetKeepStartEnd() const { return m_keepStartEndLayer; }
void SetKeepStartEnd( bool aSet )
{
m_padStack.SetUnconnectedLayerMode( aSet
? PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END
: PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL );
}
bool GetKeepStartEnd() const
{
return m_padStack.UnconnectedLayerMode()
== PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END;
}
bool ConditionallyFlashed( PCB_LAYER_ID aLayer ) const
{
if( !m_removeUnconnectedLayer )
switch( m_padStack.UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return false;
if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) )
return false;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
return true;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END:
{
if( aLayer == m_padStack.Drill().start || aLayer == m_padStack.Drill().end )
return false;
}
}
return true;
}
@ -547,14 +585,17 @@ public:
*
* @param aDrill is the new drill diameter
*/
void SetDrill( int aDrill ) { m_drill = aDrill; }
void SetDrill( int aDrill )
{
m_padStack.Drill().size = { aDrill, aDrill };
}
/**
* Return the local drill setting for this PCB_VIA.
*
* @note Use GetDrillValue() to get the calculated value.
*/
int GetDrill() const { return m_drill; }
int GetDrill() const { return m_padStack.Drill().size.x; }
/**
* Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
@ -566,7 +607,10 @@ public:
/**
* Set the drill value for vias to the default value #UNDEFINED_DRILL_DIAMETER.
*/
void SetDrillDefault() { m_drill = UNDEFINED_DRILL_DIAMETER; }
void SetDrillDefault()
{
m_padStack.Drill().size = { UNDEFINED_DRILL_DIAMETER, UNDEFINED_DRILL_DIAMETER };
}
/**
* Check if the via is a free via (as opposed to one created on a track by the router).
@ -611,15 +655,10 @@ protected:
wxString layerMaskDescribe() const override;
private:
/// The bottom layer of the via (the top layer is in m_layer)
PCB_LAYER_ID m_bottomLayer;
VIATYPE m_viaType; ///< through, blind/buried or micro
int m_drill; ///< for vias: via drill (- 1 for default value)
PADSTACK m_padStack;
bool m_removeUnconnectedLayer; ///< Remove annular rings on unconnected layers
bool m_keepStartEndLayer; ///< Keep the start and end annular rings
bool m_isFree; ///< "Free" vias don't get their nets auto-updated
std::mutex m_zoneLayerOverridesMutex;

View File

@ -773,7 +773,7 @@ void ZONE_FILLER::addKnockout( PAD* aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_P
aPad->TransformShapeToPolygon( poly, aLayer, aGap, m_maxError, ERROR_OUTSIDE );
// the pad shape in zone can be its convex hull or the shape itself
if( aPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
if( aPad->GetCustomShapeInZoneOpt() == PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL )
{
std::vector<VECTOR2I> convex_hull;
BuildConvexHull( convex_hull, poly );

View File

@ -160,4 +160,9 @@ BOOST_AUTO_TEST_CASE( ZoneConnectionStyle )
testEnums<ZONE_CONNECTION, kiapi::board::types::ZoneConnectionStyle>();
}
BOOST_AUTO_TEST_CASE( UnconnectedLayerRemoval )
{
testEnums<PADSTACK::UNCONNECTED_LAYER_MODE, kiapi::board::types::UnconnectedLayerRemoval>();
}
BOOST_AUTO_TEST_SUITE_END()