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

View File

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

View File

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

View File

@ -40,12 +40,6 @@ class PCB_SHAPE;
class SHAPE; class SHAPE;
class SHAPE_SEGMENT; 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 LINE_READER;
class EDA_3D_CANVAS; class EDA_3D_CANVAS;
class FOOTPRINT; class FOOTPRINT;
@ -183,14 +177,14 @@ public:
*/ */
void SetShape( PAD_SHAPE aShape ) void SetShape( PAD_SHAPE aShape )
{ {
m_padShape = aShape; m_padStack.SetShape( aShape );
SetDirty(); SetDirty();
} }
/** /**
* @return the shape of this pad. * @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 void SetPosition( const VECTOR2I& aPos ) override
{ {
@ -203,14 +197,17 @@ public:
/** /**
* @return the shape of the anchor pad shape, for custom shaped pads. * @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. * @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 * @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 ) 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(); SetDirty();
} }
@ -243,28 +242,37 @@ public:
void SetY( int y ) { m_pos.y = y; SetDirty(); } void SetY( int y ) { m_pos.y = y; SetDirty(); }
void SetX( int x ) { m_pos.x = x; SetDirty(); } void SetX( int x ) { m_pos.x = x; SetDirty(); }
void SetSize( const VECTOR2I& aSize ) { m_size = aSize; SetDirty(); } void SetSize( const VECTOR2I& aSize )
const VECTOR2I& GetSize() const { return m_size; } {
void SetSizeX( const int aX ) { if( aX > 0 ) { m_size.x = aX; SetDirty(); } } m_padStack.Size() = aSize;
int GetSizeX() const { return m_size.x; } SetDirty();
void SetSizeY( const int aY ) { if( aY > 0 ) { m_size.y = aY; SetDirty(); } } }
int GetSizeY() const { return m_size.y; } const VECTOR2I& GetSize() const { return m_padStack.Size(); }
void SetDelta( const VECTOR2I& aSize ) { m_deltaSize = aSize; SetDirty(); } void SetSizeX( const int aX ) { if( aX > 0 ) { m_padStack.Size().x = aX; SetDirty(); } }
const VECTOR2I& GetDelta() const { return m_deltaSize; } 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(); } void SetDelta( const VECTOR2I& aSize ) { m_padStack.TrapezoidDeltaSize() = aSize; SetDirty(); }
const VECTOR2I& GetDrillSize() const { return m_drill; } const VECTOR2I& GetDelta() const { return m_padStack.TrapezoidDeltaSize(); }
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 SetOffset( const VECTOR2I& aOffset ) { m_offset = aOffset; SetDirty(); } void SetDrillSize( const VECTOR2I& aSize ) { m_padStack.Drill().size = aSize; SetDirty(); }
const VECTOR2I& GetOffset() const { return m_offset; } 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(); } 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. * Has meaning only for custom shape pads.
* add a free shape to the shape list. * add a free shape to the shape list.
@ -355,8 +363,12 @@ public:
return m_orient.AsDegrees(); return m_orient.AsDegrees();
} }
void SetDrillShape( PAD_DRILL_SHAPE aShape ) { m_drillShape = aShape; m_shapesDirty = true; } void SetDrillShape( PAD_DRILL_SHAPE aShape )
PAD_DRILL_SHAPE GetDrillShape() const { return m_drillShape; } {
m_padStack.Drill().shape = aShape;
m_shapesDirty = true;
}
PAD_DRILL_SHAPE GetDrillShape() const { return m_padStack.Drill().shape; }
bool IsDirty() const bool IsDirty() const
{ {
@ -370,8 +382,8 @@ public:
m_polyDirty[ERROR_OUTSIDE] = true; m_polyDirty[ERROR_OUTSIDE] = true;
} }
void SetLayerSet( LSET aLayers ) override { m_layerMask = aLayers; } void SetLayerSet( LSET aLayers ) override { m_padStack.SetLayerSet( aLayers ); }
LSET GetLayerSet() const override { return m_layerMask; } LSET GetLayerSet() const override { return m_padStack.LayerSet(); }
void SetAttribute( PAD_ATTRIB aAttribute ); void SetAttribute( PAD_ATTRIB aAttribute );
PAD_ATTRIB GetAttribute() const { return m_attribute; } 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. // format, so for now just infer a copper-less pad to be an APERTURE pad.
bool IsAperturePad() const bool IsAperturePad() const
{ {
return ( m_layerMask & LSET::AllCuMask() ).none(); return ( m_padStack.LayerSet() & LSET::AllCuMask() ).none();
} }
void SetPadToDieLength( int aLength ) { m_lengthPadToDie = aLength; } void SetPadToDieLength( int aLength ) { m_lengthPadToDie = aLength; }
int GetPadToDieLength() const { return m_lengthPadToDie; } int GetPadToDieLength() const { return m_lengthPadToDie; }
std::optional<int> GetLocalClearance() const override { return m_clearance; } std::optional<int> GetLocalClearance() const override { return m_padStack.Clearance(); }
void SetLocalClearance( std::optional<int> aClearance ) { m_clearance = aClearance; } void SetLocalClearance( std::optional<int> aClearance ) { m_padStack.Clearance() = aClearance; }
std::optional<int> GetLocalSolderMaskMargin() const { return m_solderMaskMargin; } std::optional<int> GetLocalSolderMaskMargin() const { return m_padStack.SolderMaskMargin(); }
void SetLocalSolderMaskMargin( std::optional<int> aMargin ) { m_solderMaskMargin = aMargin; } void SetLocalSolderMaskMargin( std::optional<int> aMargin )
{
m_padStack.SolderMaskMargin() = aMargin;
}
std::optional<int> GetLocalSolderPasteMargin() const { return m_solderPasteMargin; } std::optional<int> GetLocalSolderPasteMargin() const { return m_padStack.SolderPasteMargin(); }
void SetLocalSolderPasteMargin( std::optional<int> aMargin ) { m_solderPasteMargin = aMargin; } void SetLocalSolderPasteMargin( std::optional<int> aMargin )
{
m_padStack.SolderPasteMargin() = aMargin;
}
std::optional<double> GetLocalSolderPasteMarginRatio() const { return m_solderPasteMarginRatio; } std::optional<double> GetLocalSolderPasteMarginRatio() const
void SetLocalSolderPasteMarginRatio( std::optional<double> aRatio ) { m_solderPasteMarginRatio = aRatio; } {
return m_padStack.SolderPasteMarginRatio();
}
void SetLocalSolderPasteMarginRatio( std::optional<double> aRatio )
{
m_padStack.SolderPasteMarginRatio() = aRatio;
}
void SetLocalZoneConnection( ZONE_CONNECTION aType ) { m_zoneConnection = aType; } void SetLocalZoneConnection( ZONE_CONNECTION aType ) { m_padStack.ZoneConnection() = aType; }
ZONE_CONNECTION GetLocalZoneConnection() const { return m_zoneConnection; } ZONE_CONNECTION GetLocalZoneConnection() const
{
return m_padStack.ZoneConnection().value_or( ZONE_CONNECTION::INHERITED );
}
/** /**
* Return the pad's "own" clearance in internal units. * 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 * 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. * override similar settings in the parent footprint and zone.
*/ */
void SetThermalSpokeWidth( int aWidth ) { m_thermalSpokeWidth = aWidth; } void SetThermalSpokeWidth( int aWidth ) { m_padStack.ThermalSpokeWidth() = aWidth; }
int GetThermalSpokeWidth() const { return m_thermalSpokeWidth; } int GetThermalSpokeWidth() const { return m_padStack.ThermalSpokeWidth().value_or( 0 ); }
int GetLocalSpokeWidthOverride( wxString* aSource = nullptr ) const; 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 * pads and circular-anchored custom shaped pads), while 90° will produce a + (the default
* for all other shapes). * for all other shapes).
*/ */
void SetThermalSpokeAngle( const EDA_ANGLE& aAngle ) { m_thermalSpokeAngle = aAngle; } void SetThermalSpokeAngle( const EDA_ANGLE& aAngle )
EDA_ANGLE GetThermalSpokeAngle() const { return m_thermalSpokeAngle; } {
m_padStack.SetThermalSpokeAngle( aAngle );
}
EDA_ANGLE GetThermalSpokeAngle() const
{
return m_padStack.ThermalSpokeAngle();
}
// For property system // For property system
void SetThermalSpokeAngleDegrees( double aAngle ) void SetThermalSpokeAngleDegrees( double aAngle )
{ {
m_thermalSpokeAngle = EDA_ANGLE( aAngle, DEGREES_T ); m_padStack.ThermalSpokeAngle() = EDA_ANGLE( aAngle, DEGREES_T );
} }
double GetThermalSpokeAngleDegrees() const double GetThermalSpokeAngleDegrees() const
{ {
return m_thermalSpokeAngle.AsDegrees(); return m_padStack.ThermalSpokeAngle().AsDegrees();
} }
void SetThermalGap( int aGap ) { m_thermalGap = aGap; } void SetThermalGap( int aGap ) { m_padStack.ThermalGap() = aGap; }
int GetThermalGap() const { return m_thermalGap; } int GetThermalGap() const { return m_padStack.ThermalGap().value_or( 0 ); }
int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const; int GetLocalThermalGapOverride( wxString* aSource = nullptr ) const;
@ -563,7 +596,7 @@ public:
* Cannot be > 0.5; the normalized IPC-7351C value is 0.25 * Cannot be > 0.5; the normalized IPC-7351C value is 0.25
*/ */
void SetRoundRectRadiusRatio( double aRadiusScale ); void SetRoundRectRadiusRatio( double aRadiusScale );
double GetRoundRectRadiusRatio() const { return m_roundedCornerScale; } double GetRoundRectRadiusRatio() const { return m_padStack.RoundRectRadiusRatio(); }
/** /**
* Has meaning only for chamfered rectangular pads. * Has meaning only for chamfered rectangular pads.
@ -572,7 +605,7 @@ public:
* Cannot be < 0.5. * Cannot be < 0.5.
*/ */
void SetChamferRectRatio( double aChamferScale ); void SetChamferRectRatio( double aChamferScale );
double GetChamferRectRatio() const { return m_chamferScale; } double GetChamferRectRatio() const { return m_padStack.ChamferRatio(); }
/** /**
* Has meaning only for chamfered rectangular pads. * Has meaning only for chamfered rectangular pads.
@ -581,8 +614,8 @@ public:
* *
* @param aPositions a bit-set of #RECT_CHAMFER_POSITIONS. * @param aPositions a bit-set of #RECT_CHAMFER_POSITIONS.
*/ */
void SetChamferPositions( int aPositions ) { m_chamferPositions = aPositions; } void SetChamferPositions( int aPositions ) { m_padStack.SetChamferPositions( aPositions ); }
int GetChamferPositions() const { return m_chamferPositions; } int GetChamferPositions() const { return m_padStack.ChamferPositions(); }
/** /**
* @return the netcode. * @return the netcode.
@ -591,27 +624,65 @@ public:
void SetSubRatsnest( int aSubRatsnest ) { m_subRatsnest = aSubRatsnest; } void SetSubRatsnest( int aSubRatsnest ) { m_subRatsnest = aSubRatsnest; }
/** /**
* Set the unconnected removal property. * @deprecated - use Padstack().SetUnconnectedLayerMode()
* * Sets the unconnected removal property. If true, the copper is removed on zone fill
* If true, the copper is removed on zone fill or when specifically requested when the pad * or when specifically requested when the via is not connected on a layer.
* is not connected on a layer. This requires that there be a through hole.
*/ */
void SetRemoveUnconnected( bool aSet ) { m_removeUnconnectedLayer = aSet; } void SetRemoveUnconnected( bool aSet )
bool GetRemoveUnconnected() const { return m_removeUnconnectedLayer; } {
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; } void SetKeepTopBottom( bool aSet )
bool GetKeepTopBottom() const { return m_keepTopBottomLayer; } {
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 bool ConditionallyFlashed( PCB_LAYER_ID aLayer ) const
{ {
if( !m_removeUnconnectedLayer ) switch( m_padStack.UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return false; return false;
if( m_keepTopBottomLayer && ( aLayer == F_Cu || aLayer == B_Cu ) ) case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
return false; 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; return true;
} }
@ -620,7 +691,7 @@ public:
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override 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 VECTOR2I m_pos; // Pad Position on board
PAD_SHAPE m_padShape; // Shape: PAD_SHAPE::CIRCLE, PAD_SHAPE::RECTANGLE, PADSTACK m_padStack;
// PAD_SHAPE::OVAL, PAD_SHAPE::TRAPEZOID,
// PAD_SHAPE::ROUNDRECT, PAD_SHAPE::CHAMFERED_RECT,
// PAD_SHAPE::CUSTOM
/* /*
* Editing definitions of primitives for custom pad shapes. In local coordinates relative * Editing definitions of primitives for custom pad shapes. In local coordinates relative
* to m_Pos (NOT shapePos) at orient 0. * to m_Pos (NOT shapePos) at orient 0.
@ -786,36 +854,6 @@ private:
int m_subRatsnest; // Variable used to handle subnet (block) number in int m_subRatsnest; // Variable used to handle subnet (block) number in
// ratsnest computations // 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_ATTRIB m_attribute = PAD_ATTRIB::PTH;
PAD_PROP m_property; // Property in fab files (BGA, FIDUCIAL, TESTPOINT, etc.) 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 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::mutex m_zoneLayerOverridesMutex;
std::array<ZONE_LAYER_OVERRIDE, MAX_CU_LAYERS> m_zoneLayerOverrides; 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/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <convert_basic_shapes_to_polygon.h> // RECT_CHAMFER_POSITIONS
#include "padstack.h" #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 wxString PADSTACK::Name() const
{ {
// TODO
return wxEmptyString; 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 <wx/string.h>
#include <api/serializable.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> #include <zones.h>
class PCB_SHAPE;
/** /**
* The set of pad shapes, used with PAD::{Set,Get}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 class PADSTACK : public SERIALIZABLE
{ {
public: public:
@ -111,35 +130,243 @@ public:
CUSTOM ///< Shapes can be defined on arbitrary layers 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 struct OUTER_LAYER_PROPS
{ {
std::optional<int> solder_mask_margin; std::optional<int> solder_mask_margin;
std::optional<int> solder_paste_margin; std::optional<int> solder_paste_margin;
std::optional<double> solder_paste_margin_ratio; 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: public:
PADSTACK(); PADSTACK();
virtual ~PADSTACK() = default; 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; void Serialize( google::protobuf::Any &aContainer ) const override;
bool Deserialize( const google::protobuf::Any &aContainer ) 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 ///! Returns the name of this padstack in IPC-7351 format
wxString Name() const; 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: private:
MODE m_mode;
///! The board layers that this padstack is active on
LSET m_layerSet;
///! An override for the IPC-7351 padstack name ///! An override for the IPC-7351 padstack name
wxString m_customName; 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 #endif //KICAD_PADSTACK_H

View File

@ -2568,7 +2568,6 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
LSET padLayers; LSET padLayers;
padLayers.set( aLayer ); padLayers.set( aLayer );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetAttribute( PAD_ATTRIB::SMD ); pad->SetAttribute( PAD_ATTRIB::SMD );
pad->SetShape( PAD_SHAPE::CUSTOM ); pad->SetShape( PAD_SHAPE::CUSTOM );
pad->SetThermalSpokeAngle( ANGLE_90 ); 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 ); 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->SetNumber( aElem.name );
pad->SetNetCode( GetNetCode( aElem.net ) ); 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() ); anchorPad = aComponent.ComponentPads.at( compCopper.AssociatedPadIDs.front() );
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint ); 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->SetAttribute( PAD_ATTRIB::SMD );
pad->SetLayerSet( copperLayers ); pad->SetLayerSet( copperLayers );
pad->SetNumber( anchorPad.Identifier.IsEmpty() pad->SetNumber( anchorPad.Identifier.IsEmpty()
@ -1342,8 +1341,6 @@ PAD* CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPad( const COMPONENT_PAD& aCadstarPad,
m_padcodesTested.insert( csPadcode.ID ); m_padcodesTested.insert( csPadcode.ID );
} }
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
return pad.release(); 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 ); PAD* pad = new PAD( aFootprint );
aFootprint->Add( pad ); aFootprint->Add( pad );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetShape( PAD_SHAPE::CIRCLE ); pad->SetShape( PAD_SHAPE::CIRCLE );
pad->SetAttribute( PAD_ATTRIB::NPTH ); pad->SetAttribute( PAD_ATTRIB::NPTH );
@ -2493,8 +2491,6 @@ void PCB_IO_EAGLE::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
aFootprint->Add( pad ); aFootprint->Add( pad );
transferPad( e, pad ); transferPad( e, pad );
pad->SetKeepTopBottom( false ); // TODO: correct? This seems to be KiCad default on import
pad->SetShape( PAD_SHAPE::RECTANGLE ); pad->SetShape( PAD_SHAPE::RECTANGLE );
pad->SetAttribute( PAD_ATTRIB::SMD ); 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( 0, "\n");
m_out->Print( aNestLevel+1, "(options" ); 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)" ); m_out->Print( 0, " (clearance convexhull)" );
#if 1 // Set to 1 to output the default option #if 1 // Set to 1 to output the default option
else 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( layer1 ) ).c_str(),
m_out->Quotew( LSET::Name( layer2 ) ).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", case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
via->GetRemoveUnconnected() ); 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() ) 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 ); std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
// File only contains a token if KeepTopBottom is true
pad->SetKeepTopBottom( false );
NeedSYMBOLorNUMBER(); NeedSYMBOLorNUMBER();
pad->SetNumber( FromUTF8() ); pad->SetNumber( FromUTF8() );
@ -5286,11 +5283,11 @@ bool PCB_IO_KICAD_SEXPR_PARSER::parsePAD_option( PAD* aPad )
switch( token ) switch( token )
{ {
case T_outline: case T_outline:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_OUTLINE ); aPad->SetCustomShapeInZoneOpt( PADSTACK::CUSTOM_SHAPE_ZONE_MODE::OUTLINE );
break; break;
case T_convexhull: case T_convexhull:
aPad->SetCustomShapeInZoneOpt( CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL ); aPad->SetCustomShapeInZoneOpt( PADSTACK::CUSTOM_SHAPE_ZONE_MODE::CONVEXHULL );
break; break;
default: 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 ); std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( m_board );
// File format default is no-token == no-feature. // File format default is no-token == no-feature.
via->SetRemoveUnconnected( false ); via->Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
via->SetKeepStartEnd( false );
for( token = NextTok(); token != T_RIGHT; token = NextTok() ) 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 ) PCB_TRACK( aParent, PCB_VIA_T )
{ {
SetViaType( VIATYPE::THROUGH ); SetViaType( VIATYPE::THROUGH );
m_bottomLayer = B_Cu; Padstack().Drill().start = F_Cu;
Padstack().Drill().end = B_Cu;
SetDrillDefault(); SetDrillDefault();
m_removeUnconnectedLayer = false; Padstack().SetUnconnectedLayerMode( PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL );
m_keepStartEndLayer = true;
m_zoneLayerOverrides.fill( ZLO_NONE ); m_zoneLayerOverrides.fill( ZLO_NONE );
@ -118,11 +118,8 @@ PCB_VIA& PCB_VIA::operator=( const PCB_VIA &aOther )
m_CachedLOD = aOther.m_CachedLOD; m_CachedLOD = aOther.m_CachedLOD;
m_CachedScale = aOther.m_CachedScale; m_CachedScale = aOther.m_CachedScale;
m_bottomLayer = aOther.m_bottomLayer;
m_viaType = aOther.m_viaType; m_viaType = aOther.m_viaType;
m_drill = aOther.m_drill; m_padStack = aOther.m_padStack;
m_removeUnconnectedLayer = aOther.m_removeUnconnectedLayer;
m_keepStartEndLayer = aOther.m_keepStartEndLayer;
m_isFree = aOther.m_isFree; m_isFree = aOther.m_isFree;
return *this; 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 ); 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 && 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_padStack == other.m_padStack && m_Width == other.m_Width &&
m_viaType == other.m_viaType && m_drill == other.m_drill && m_viaType == other.m_viaType &&
m_removeUnconnectedLayer == other.m_removeUnconnectedLayer &&
m_keepStartEndLayer == other.m_keepStartEndLayer &&
m_zoneLayerOverrides == other.m_zoneLayerOverrides; m_zoneLayerOverrides == other.m_zoneLayerOverrides;
} }
@ -278,21 +273,12 @@ double PCB_VIA::Similarity( const BOARD_ITEM& aOther ) const
if( m_End != other.m_End ) if( m_End != other.m_End )
similarity *= 0.9; similarity *= 0.9;
if( m_bottomLayer != other.m_bottomLayer ) if( m_padStack != other.m_padStack )
similarity *= 0.9; similarity *= 0.9;
if( m_viaType != other.m_viaType ) if( m_viaType != other.m_viaType )
similarity *= 0.9; 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 ) if( m_zoneLayerOverrides != other.m_zoneLayerOverrides )
similarity *= 0.9; 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_x_nm( GetPosition().x );
via.mutable_position()->set_y_nm( GetPosition().y ); via.mutable_position()->set_y_nm( GetPosition().y );
const PADSTACK& stack = Padstack();
kiapi::board::types::PadStack* padstack = via.mutable_pad_stack(); kiapi::board::types::PadStack* padstack = via.mutable_pad_stack();
padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED padstack->set_type( GetViaType() == VIATYPE::BLIND_BURIED
? kiapi::board::types::PadStackType::PST_BLIND_BURIED ? kiapi::board::types::PadStackType::PST_BLIND_BURIED
: kiapi::board::types::PadStackType::PST_THROUGH ); : kiapi::board::types::PadStackType::PST_THROUGH );
padstack->set_start_layer( 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( 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(), kiapi::common::PackVector2( *padstack->mutable_drill_diameter(),
{ GetDrillValue(), GetDrillValue() } ); { GetDrillValue(), GetDrillValue() } );
@ -405,23 +393,12 @@ void PCB_VIA::Serialize( google::protobuf::Any &aContainer ) const
kiapi::common::PackVector2( *stackLayer->mutable_size(), kiapi::common::PackVector2( *stackLayer->mutable_size(),
{ GetWidth(), GetWidth() } ); { 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? // 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 via.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
: kiapi::common::types::LockedState::LS_UNLOCKED ); : kiapi::common::types::LockedState::LS_UNLOCKED );
@ -466,36 +443,19 @@ bool PCB_VIA::Deserialize( const google::protobuf::Any &aContainer )
if( GetViaType() != VIATYPE::THROUGH ) 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() ); 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() ); padstack.end_layer() );
} }
else else
{ {
m_layer = F_Cu; Padstack().Drill().start = F_Cu;
m_bottomLayer = B_Cu; Padstack().Drill().end = 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().SetUnconnectedLayerMode( FromProtoEnum<PADSTACK::UNCONNECTED_LAYER_MODE>(
padstack.unconnected_layer_removal() ) );
SetNetCode( via.net().code().value() ); SetNetCode( via.net().code().value() );
SetLocked( via.locked() == kiapi::common::types::LockedState::LS_LOCKED ); 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 int PCB_VIA::GetDrillValue() const
{ {
if( m_drill > 0 ) // Use the specific value. if( m_padStack.Drill().size.x > 0 ) // Use the specific value.
return m_drill; return m_padStack.Drill().size.x;
// Use the default value from the Netclass // Use the default value from the Netclass
NETCLASS* netclass = GetEffectiveNetClass(); 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 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 ); return GetLayerSet().test( aLayer );
#endif #endif
if( aLayer >= m_layer && aLayer <= m_bottomLayer ) if( aLayer >= Padstack().Drill().start && aLayer <= Padstack().Drill().end )
return true; return true;
if( !IsTented() ) 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 PCB_VIA::GetLayerSet() const
{ {
LSET layermask; LSET layermask;
if( m_layer < PCBNEW_LAYER_ID_START ) if( Padstack().Drill().start < PCBNEW_LAYER_ID_START )
return layermask; return layermask;
if( GetViaType() == VIATYPE::THROUGH ) if( GetViaType() == VIATYPE::THROUGH )
layermask = LSET::AllCuMask(); layermask = LSET::AllCuMask();
else 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. // 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 ); layermask.set( id );
if( !IsTented() ) if( !IsTented() )
@ -918,11 +890,11 @@ void PCB_VIA::SetLayerSet( LSET aLayerSet )
if( first ) if( first )
{ {
m_layer = layer; Padstack().Drill().start = layer;
first = false; 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 ) void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
{ {
m_layer = aTopLayer; Padstack().Drill().start = aTopLayer;
m_bottomLayer = aBottomLayer; Padstack().Drill().end = aBottomLayer;
SanitizeLayers(); SanitizeLayers();
} }
void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer ) void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
{ {
m_layer = aLayer; Padstack().Drill().start = aLayer;
} }
void PCB_VIA::SetBottomLayer( PCB_LAYER_ID 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 ) if( GetViaType() != VIATYPE::THROUGH )
{ {
b_layer = m_bottomLayer; b_layer = Padstack().Drill().end;
t_layer = m_layer; t_layer = Padstack().Drill().start;
if( b_layer < t_layer ) if( b_layer < t_layer )
std::swap( 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 PCB_LAYER_ID PCB_VIA::TopLayer() const
{ {
return m_layer; return Padstack().Drill().start;
} }
PCB_LAYER_ID PCB_VIA::BottomLayer() const 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 ) if( GetViaType() == VIATYPE::THROUGH )
{ {
m_layer = F_Cu; Padstack().Drill().start = F_Cu;
m_bottomLayer = B_Cu; Padstack().Drill().end = B_Cu;
} }
if( m_bottomLayer < m_layer ) if( Padstack().Drill().end < Padstack().Drill().start )
std::swap( m_bottomLayer, m_layer ); 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 ) ) ) if( !IsOnLayer( static_cast<PCB_LAYER_ID>( aLayer ) ) )
return false; return false;
if( !m_removeUnconnectedLayer || !IsCopperLayer( aLayer ) ) if( !IsCopperLayer( aLayer ) )
return true; return true;
if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) ) switch( Padstack().UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return true; 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 // 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, static std::initializer_list<KICAD_T> connectedTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T }; PCB_PAD_T };
@ -1736,8 +1724,7 @@ static struct TRACK_VIA_DESC
&PCB_VIA::SetWidth, &PCB_VIA::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) ); &PCB_VIA::SetWidth, &PCB_VIA::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ), propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
&PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia ); &PCB_VIA::SetDrill, &PCB_VIA::GetDrillValue, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Top" ),
new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID, BOARD_ITEM>( _HKI( "Layer Top" ),
&PCB_VIA::SetLayer, &PCB_VIA::GetLayer ), groupVia ); &PCB_VIA::SetLayer, &PCB_VIA::GetLayer ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ), propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Layer Bottom" ),
&PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia ); &PCB_VIA::SetBottomLayer, &PCB_VIA::BottomLayer ), groupVia );

View File

@ -43,6 +43,7 @@
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <core/minoptmax.h> #include <core/minoptmax.h>
#include <core/arraydim.h> #include <core/arraydim.h>
#include <padstack.h>
class PCB_TRACK; class PCB_TRACK;
class PCB_VIA; class PCB_VIA;
@ -410,6 +411,10 @@ public:
VIATYPE GetViaType() const { return m_viaType; } VIATYPE GetViaType() const { return m_viaType; }
void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; } 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 bool HasHole() const override
{ {
return true; return true;
@ -423,6 +428,9 @@ public:
bool IsTented() const override; bool IsTented() const override;
int GetSolderMaskExpansion() const; int GetSolderMaskExpansion() const;
PCB_LAYER_ID GetLayer() const override;
void SetLayer( PCB_LAYER_ID aLayer ) override;
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override; bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
virtual LSET GetLayerSet() const override; virtual LSET GetLayerSet() const override;
@ -494,25 +502,55 @@ public:
int GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const; 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 * 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. * or when specifically requested when the via is not connected on a layer.
*/ */
void SetRemoveUnconnected( bool aSet ) { m_removeUnconnectedLayer = aSet; } void SetRemoveUnconnected( bool aSet )
bool GetRemoveUnconnected() const { return m_removeUnconnectedLayer; } {
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 * Sets whether we keep the start and end annular rings even if they are not connected
*/ */
void SetKeepStartEnd( bool aSet ) { m_keepStartEndLayer = aSet; } void SetKeepStartEnd( bool aSet )
bool GetKeepStartEnd() const { return m_keepStartEndLayer; } {
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 bool ConditionallyFlashed( PCB_LAYER_ID aLayer ) const
{ {
if( !m_removeUnconnectedLayer ) switch( m_padStack.UnconnectedLayerMode() )
{
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL:
return false; return false;
if( m_keepStartEndLayer && ( aLayer == m_layer || aLayer == m_bottomLayer ) ) case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL:
return false; 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; return true;
} }
@ -547,14 +585,17 @@ public:
* *
* @param aDrill is the new drill diameter * @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. * Return the local drill setting for this PCB_VIA.
* *
* @note Use GetDrillValue() to get the calculated value. * @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). * 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. * 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). * 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; wxString layerMaskDescribe() const override;
private: 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 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 bool m_isFree; ///< "Free" vias don't get their nets auto-updated
std::mutex m_zoneLayerOverridesMutex; 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 ); aPad->TransformShapeToPolygon( poly, aLayer, aGap, m_maxError, ERROR_OUTSIDE );
// the pad shape in zone can be its convex hull or the shape itself // 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; std::vector<VECTOR2I> convex_hull;
BuildConvexHull( convex_hull, poly ); BuildConvexHull( convex_hull, poly );

View File

@ -160,4 +160,9 @@ BOOST_AUTO_TEST_CASE( ZoneConnectionStyle )
testEnums<ZONE_CONNECTION, kiapi::board::types::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() BOOST_AUTO_TEST_SUITE_END()