ADDED: Support for IPC-4761 Via protection features

Fixes https://gitlab.com/kicad/code/kicad/-/work_items/18837
This commit is contained in:
Daniel Treffenstädt 2025-03-01 18:02:54 +00:00 committed by Jon Evans
parent 5bdeb40d66
commit 3768221d9c
50 changed files with 2223 additions and 791 deletions

View File

@ -294,15 +294,31 @@ message PadStackLayer
enum SolderMaskMode
{
SMM_UNKOWN = 0;
SMM_UNKNOWN = 0;
SMM_MASKED = 1;
SMM_UNMASKED = 2;
SMM_FROM_DESIGN_RULES = 3;
}
enum ViaCoveringMode
{
VCM_UNKNOWN = 0;
VCM_COVERED = 1;
VCM_UNCOVERED = 2;
VCM_FROM_DESIGN_RULES = 3;
}
enum ViaPluggingMode
{
VPM_UNKNOWN = 0;
VPM_PLUGGED = 1;
VPM_UNPLUGGED = 2;
VPM_FROM_DESIGN_RULES = 3;
}
enum SolderPasteMode
{
SPM_UNKOWN = 0;
SPM_UNKNOWN = 0;
SPM_PASTE = 1;
SPM_NO_PASTE = 2;
SPM_FROM_DESIGN_RULES = 3;
@ -319,6 +335,9 @@ message PadStackOuterLayer
// NOTE: At present, KiCad does not support different solder paste expansion settings for the top and bottom layers
SolderPasteOverrides solder_paste_settings = 4;
ViaPluggingMode plugging_mode = 5;
ViaCoveringMode covering_mode = 6;
}
enum DrillShape
@ -329,6 +348,22 @@ enum DrillShape
DS_UNDEFINED = 3;
}
enum ViaDrillCappingMode
{
VDCM_UNKNOWN = 0;
VDCM_CAPPED = 1;
VDCM_UNCAPPED = 2;
VDCM_FROM_DESIGN_RULES = 3;
}
enum ViaDrillFillingMode
{
VDFM_UNKNOWN = 0;
VDFM_FILLED = 1;
VDFM_UNFILLED = 2;
VDFM_FROM_DESIGN_RULES = 3;
}
message DrillProperties
{
// Lowest (closest to F_Cu) layer this drill exists on.
@ -342,6 +377,10 @@ message DrillProperties
kiapi.common.types.Vector2 diameter = 3;
DrillShape shape = 4;
ViaDrillCappingMode capped = 5;
ViaDrillFillingMode filled = 6;
}
// A pad stack definition for a multilayer pad or via.

View File

@ -157,7 +157,8 @@ wxString GbrMakeProjectGUIDfromString( const wxString& aText )
std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribute,
bool aUseX1StructuredComment )
bool aUseX1StructuredComment,
const std::string& aCustomAttribute )
{
std::string attribute_string; // the specific aperture attribute (TA.xxx)
std::string comment_string; // a optional G04 comment line to write before the TA. line
@ -321,7 +322,9 @@ std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribu
attribute_string = "TA.AperFunction,ComponentOutline,Courtyard";
break;
break;
case GBR_APERTURE_ATTRIB_OTHER:
attribute_string = "TA.AperFunction,Other," + aCustomAttribute;
break;
}
std::string full_attribute_string;

View File

@ -37,6 +37,14 @@ void FormatBool( OUTPUTFORMATTER* aOut, const wxString& aKey, bool aValue )
aOut->Print( "(%ls %s)", aKey.wc_str(), aValue ? "yes" : "no" );
}
void FormatOptBool( OUTPUTFORMATTER* aOut, const wxString& aKey, std::optional<bool> aValue )
{
if( aValue.has_value() )
FormatBool( aOut, aKey, aValue.value() );
else
aOut->Print( "(%ls none)", aKey.wc_str() );
}
void FormatUuid( OUTPUTFORMATTER* aOut, const KIID& aUuid )
{

View File

@ -70,7 +70,8 @@ JOB_EXPORT_PCB_DRILL::JOB_EXPORT_PCB_DRILL() :
m_zeroFormat( ZEROS_FORMAT::DECIMAL ),
m_mapFormat( MAP_FORMAT::PDF ),
m_gerberPrecision( 5 ),
m_generateMap( false )
m_generateMap( false ),
m_generateTenting( false )
{
m_params.emplace_back( new JOB_PARAM<bool>( "excellon.mirror_y",
&m_excellonMirrorY,
@ -108,6 +109,10 @@ JOB_EXPORT_PCB_DRILL::JOB_EXPORT_PCB_DRILL() :
&m_generateMap,
m_generateMap ) );
m_params.emplace_back( new JOB_PARAM<bool>( "generate_tenting",
&m_generateTenting,
m_generateTenting ) );
m_params.emplace_back( new JOB_PARAM<MAP_FORMAT>( "map_format",
&m_mapFormat,
m_mapFormat ) );

View File

@ -89,6 +89,8 @@ public:
int m_gerberPrecision;
bool m_generateMap;
bool m_generateTenting;
};
#endif

View File

@ -333,6 +333,10 @@ teardrop
teardrops
tedit
tenting
covering
plugging
filling
capping
text_frame
text_position_mode
thermal_width

View File

@ -405,17 +405,24 @@ void GERBER_PLOTTER::SetCurrentLineWidth( int aWidth, void* aData )
wxASSERT_MSG( aWidth >= 0, "Plotter called to set negative pen width" );
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
int aperture_attribute = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
int aperture_attribute = 0;
std::string custom_attribute = "";
if( gbr_metadata )
{
aperture_attribute = gbr_metadata->GetApertureAttrib();
custom_attribute = gbr_metadata->GetCustomAttribute();
}
selectAperture( VECTOR2I( aWidth, aWidth ), 0, ANGLE_0, APERTURE::AT_PLOTTING,
aperture_attribute );
aperture_attribute, custom_attribute );
m_currentPenWidth = aWidth;
}
int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
int aApertureAttribute )
int aApertureAttribute,
const std::string& aCustomAttribute )
{
int last_D_code = 9;
@ -425,10 +432,13 @@ int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
APERTURE* tool = &m_apertures[idx];
last_D_code = tool->m_DCode;
if( (tool->m_Type == aType) && (tool->m_Size == aSize) &&
(tool->m_Radius == aRadius) && (tool->m_Rotation == aRotation) &&
(tool->m_ApertureAttribute == aApertureAttribute) )
if( ( tool->m_Type == aType ) && ( tool->m_Size == aSize ) && ( tool->m_Radius == aRadius )
&& ( tool->m_Rotation == aRotation )
&& ( tool->m_ApertureAttribute == aApertureAttribute )
&& ( tool->m_CustomAttribute == aCustomAttribute ) )
{
return idx;
}
}
// Allocate a new aperture
@ -439,6 +449,7 @@ int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
new_tool.m_Rotation = aRotation;
new_tool.m_DCode = last_D_code + 1;
new_tool.m_ApertureAttribute = aApertureAttribute;
new_tool.m_CustomAttribute = aCustomAttribute;
m_apertures.push_back( new_tool );
@ -448,7 +459,8 @@ int GERBER_PLOTTER::GetOrCreateAperture( const VECTOR2I& aSize, int aRadius,
int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
int aApertureAttribute )
int aApertureAttribute,
const std::string& aCustomAttribute )
{
int last_D_code = 9;
@ -468,12 +480,12 @@ int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
{
APERTURE* tool = &m_apertures[idx];
last_D_code = tool->m_DCode;
last_D_code = tool->m_DCode;
if( (tool->m_Type == aType) &&
(tool->m_Corners.size() == aCorners.size() ) &&
(tool->m_Rotation == aRotation) &&
(tool->m_ApertureAttribute == aApertureAttribute) )
if( ( tool->m_Type == aType ) && ( tool->m_Corners.size() == aCorners.size() )
&& ( tool->m_Rotation == aRotation )
&& ( tool->m_ApertureAttribute == aApertureAttribute )
&& ( tool->m_CustomAttribute == aCustomAttribute ) )
{
// A candidate is found. the corner lists must be similar
bool is_same = polyCompare( tool->m_Corners, aCorners );
@ -493,6 +505,7 @@ int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
new_tool.m_Rotation = aRotation;
new_tool.m_DCode = last_D_code + 1;
new_tool.m_ApertureAttribute = aApertureAttribute;
new_tool.m_CustomAttribute = aCustomAttribute;
m_apertures.push_back( new_tool );
@ -501,7 +514,8 @@ int GERBER_PLOTTER::GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners,
void GERBER_PLOTTER::selectAperture( const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
APERTURE::APERTURE_TYPE aType, int aApertureAttribute,
const std::string& aCustomAttribute )
{
bool change = ( m_currentApertureIdx < 0 ) ||
( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
@ -510,13 +524,16 @@ void GERBER_PLOTTER::selectAperture( const VECTOR2I& aSize, int aRadius, const E
( m_apertures[m_currentApertureIdx].m_Rotation != aRotation );
if( !change )
change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
{
change = ( m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute )
|| ( m_apertures[m_currentApertureIdx].m_CustomAttribute != aCustomAttribute );
}
if( change )
{
// Pick an existing aperture or create a new one
m_currentApertureIdx = GetOrCreateAperture( aSize, aRadius, aRotation, aType,
aApertureAttribute );
aApertureAttribute, aCustomAttribute );
fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
}
}
@ -524,7 +541,7 @@ void GERBER_PLOTTER::selectAperture( const VECTOR2I& aSize, int aRadius, const E
void GERBER_PLOTTER::selectAperture( const std::vector<VECTOR2I>& aCorners,
const EDA_ANGLE& aRotation, APERTURE::APERTURE_TYPE aType,
int aApertureAttribute )
int aApertureAttribute, const std::string& aCustomAttribute )
{
bool change = ( m_currentApertureIdx < 0 ) ||
( m_apertures[m_currentApertureIdx].m_Type != aType ) ||
@ -544,30 +561,43 @@ void GERBER_PLOTTER::selectAperture( const std::vector<VECTOR2I>& aCorners,
}
if( !change )
change = m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute;
{
change = ( m_apertures[m_currentApertureIdx].m_ApertureAttribute != aApertureAttribute )
|| ( m_apertures[m_currentApertureIdx].m_CustomAttribute != aCustomAttribute );
}
if( change )
{
// Pick an existing aperture or create a new one
m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotation, aType,
aApertureAttribute );
m_currentApertureIdx = GetOrCreateAperture( aCorners, aRotation, aType, aApertureAttribute,
aCustomAttribute );
fprintf( m_outputFile, "D%d*\n", m_apertures[m_currentApertureIdx].m_DCode );
}
}
void GERBER_PLOTTER::selectAperture( int aDiameter, const EDA_ANGLE& aPolygonRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute )
void GERBER_PLOTTER::selectApertureWithAttributes( const VECTOR2I& aPos, GBR_METADATA* aGbrMetadata,
VECTOR2I aSize, int aRadius,
const EDA_ANGLE& aAngle,
APERTURE::APERTURE_TYPE aType )
{
// Pick an existing aperture or create a new one, matching the
// aDiameter, aPolygonRotation, type and attributes for type =
// AT_REGULAR_POLY3 to AT_REGULAR_POLY12
VECTOR2D pos_dev = userToDeviceCoordinates( aPos );
wxASSERT( aType>= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3 &&
aType <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
int aperture_attribute = 0;
std::string custom_attribute = "";
VECTOR2I size( aDiameter, (int) ( aPolygonRotation.AsDegrees() * 1000.0 ) );
selectAperture( VECTOR2I( 0, 0 ), aDiameter / 2, aPolygonRotation, aType, aApertureAttribute );
if( aGbrMetadata )
{
aperture_attribute = aGbrMetadata->GetApertureAttrib();
custom_attribute = aGbrMetadata->GetCustomAttribute();
}
selectAperture( aSize, aRadius, aAngle, aType, aperture_attribute, custom_attribute );
if( aGbrMetadata )
formatNetAttribute( &aGbrMetadata->m_NetlistMetadata );
emitDcode( pos_dev, 3 );
}
@ -595,8 +625,10 @@ void GERBER_PLOTTER::writeApertureList()
if( attribute != m_apertureAttribute )
{
fputs( GBR_APERTURE_METADATA::FormatAttribute(
(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute,
useX1StructuredComment ).c_str(), m_outputFile );
(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB) attribute,
useX1StructuredComment, tool.m_CustomAttribute )
.c_str(),
m_outputFile );
}
fprintf( m_outputFile, "%%ADD%d", tool.m_DCode );
@ -1291,15 +1323,7 @@ void GERBER_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, OUTLINE_
}
else
{
VECTOR2D pos_dev = userToDeviceCoordinates( pos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( size, 0, ANGLE_0, APERTURE::AT_CIRCLE, aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_dev, 3 );
selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_CIRCLE );
}
}
@ -1320,14 +1344,7 @@ void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
if( orient.IsCardinal90() )
std::swap( size.x, size.y );
VECTOR2D pos_device = userToDeviceCoordinates( aPos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( size, 0, ANGLE_0, APERTURE::AT_OVAL, aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_device, 3 );
selectApertureWithAttributes( aPos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_OVAL );
}
else // Plot pad as region.
// Only regions and flashed items accept a object attribute TO.P for the pin name
@ -1351,14 +1368,8 @@ void GERBER_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
orient -= ANGLE_180;
}
VECTOR2D pos_device = userToDeviceCoordinates( aPos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( size, 0, orient, APERTURE::AM_ROTATED_OVAL, aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_device, 3 );
selectApertureWithAttributes( aPos, gbr_metadata, size, 0, orient,
APERTURE::AM_ROTATED_OVAL );
return;
}
@ -1415,14 +1426,7 @@ void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
}
else
{
VECTOR2D pos_device = userToDeviceCoordinates( pos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( size, 0, ANGLE_0, APERTURE::AT_RECT, aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_device, 3 );
selectApertureWithAttributes( pos, gbr_metadata, size, 0, ANGLE_0, APERTURE::AT_RECT );
}
}
else
@ -1432,14 +1436,8 @@ void GERBER_PLOTTER::FlashPadRect( const VECTOR2I& pos, const VECTOR2I& aSize,
{
m_hasApertureRotRect = true;
VECTOR2D pos_device = userToDeviceCoordinates( pos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( size, 0, aOrient, APERTURE::AM_ROT_RECT, aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_device, 3 );
selectApertureWithAttributes( pos, gbr_metadata, size, 0, aOrient,
APERTURE::AM_ROT_RECT );
}
else
#endif
@ -1504,15 +1502,8 @@ void GERBER_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I&
{
m_hasApertureRoundRect = true;
VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( aSize, aCornerRadius, aOrient, APERTURE::AM_ROUND_RECT,
aperture_attrib );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
emitDcode( pos_dev, 3 );
selectApertureWithAttributes( aPadPos, gbr_metadata, aSize, aCornerRadius, aOrient,
APERTURE::AM_ROUND_RECT );
return;
}
@ -1732,7 +1723,8 @@ void GERBER_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aS
VECTOR2D pos_dev = userToDeviceCoordinates( aPadPos );
selectAperture( cornerList, aOrient, APERTURE::AM_FREE_POLYGON,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(),
gbr_metadata.GetCustomAttribute() );
formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
emitDcode( pos_dev, 3 );
@ -1802,7 +1794,8 @@ void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const
}
selectAperture( cornerList, aPadOrient, APERTURE::AM_FREE_POLYGON,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(),
gbr_metadata.GetCustomAttribute() );
formatNetAttribute( &gbr_metadata.m_NetlistMetadata );
emitDcode( pos_device, 3 );
@ -1832,31 +1825,31 @@ void GERBER_PLOTTER::FlashPadChamferRoundRect( const VECTOR2I& aShapePos, const
case 4:
m_hasApertureOutline4P = true;
selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
break;
case 5:
m_hasApertureChamferedRect = true;
selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE5P,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
break;
case 6:
m_hasApertureChamferedRect = true;
selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE6P,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
break;
case 7:
m_hasApertureChamferedRect = true;
selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE7P,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
break;
case 8:
m_hasApertureChamferedRect = true;
selectAperture( cornerList, aPadOrient, APERTURE::APER_MACRO_OUTLINE8P,
gbr_metadata.GetApertureAttrib() );
gbr_metadata.GetApertureAttrib(), gbr_metadata.GetCustomAttribute() );
break;
default:
@ -1911,8 +1904,15 @@ void GERBER_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aC
// polygon corners list
std::vector<VECTOR2I> corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] };
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attrib );
int aperture_attribute = 0;
std::string custom_attribute = "";
if( gbr_metadata )
{
aperture_attribute = gbr_metadata->GetApertureAttrib();
custom_attribute = gbr_metadata->GetCustomAttribute();
}
selectAperture( corners, aPadOrient, APERTURE::APER_MACRO_OUTLINE4P, aperture_attribute,
custom_attribute );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
@ -1959,17 +1959,14 @@ void GERBER_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aDiamet
}
else
{
VECTOR2D pos_dev = userToDeviceCoordinates( aShapePos );
int aperture_attrib = gbr_metadata ? gbr_metadata->GetApertureAttrib() : 0;
APERTURE::APERTURE_TYPE apert_type =
(APERTURE::APERTURE_TYPE)(APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3);
selectAperture( aDiameter, aOrient, apert_type, aperture_attrib );
( APERTURE::APERTURE_TYPE )( APERTURE::AT_REGULAR_POLY3 + aCornerCount - 3 );
if( gbr_metadata )
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
wxASSERT( apert_type >= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY3
&& apert_type <= APERTURE::APERTURE_TYPE::AT_REGULAR_POLY12 );
emitDcode( pos_dev, 3 );
selectApertureWithAttributes( aShapePos, gbr_metadata, VECTOR2I( 0, 0 ), aDiameter / 2,
aOrient, apert_type );
}
}

View File

@ -745,9 +745,22 @@ public:
int m_SolderPasteMargin; // Solder paste margin absolute value
double m_SolderPasteMarginRatio; // Solder mask margin ratio value of pad size
// The final margin is the sum of these 2 values
bool m_AllowSoldermaskBridgesInFPs;
bool m_TentViasFront; // The default tenting option if not overridden on an
bool m_TentViasBack; // individual via
bool m_AllowSoldermaskBridgesInFPs;
bool m_TentViasFront; // The default tenting option if not overridden on an
bool m_TentViasBack; // individual via
bool m_CoverViasFront; // The default covering option if not overridden on an
bool m_CoverViasBack; // individual via
bool m_PlugViasFront; // The default plugging option if not overridden on an
bool m_PlugViasBack; // individual via
bool m_CapVias; // The default capping option if not overridden on an
// individual via
bool m_FillVias; // The default filling option if not overridden on ana
// individual via
std::shared_ptr<NET_SETTINGS> m_NetSettings;

View File

@ -91,16 +91,16 @@ class GBR_APERTURE_METADATA
public:
enum GBR_APERTURE_ATTRIB
{
GBR_APERTURE_ATTRIB_NONE, ///< uninitialized attribute.
GBR_APERTURE_ATTRIB_ETCHEDCMP, ///< Aperture used for etched components.
GBR_APERTURE_ATTRIB_NONE, ///< uninitialized attribute.
GBR_APERTURE_ATTRIB_ETCHEDCMP, ///< Aperture used for etched components.
/// Aperture used for connected items like tracks (not vias).
GBR_APERTURE_ATTRIB_CONDUCTOR,
GBR_APERTURE_ATTRIB_EDGECUT, ///< Aperture used for board cutout,
GBR_APERTURE_ATTRIB_EDGECUT, ///< Aperture used for board cutout,
/// Aperture used for not connected items (texts, outlines on copper).
GBR_APERTURE_ATTRIB_NONCONDUCTOR,
GBR_APERTURE_ATTRIB_VIAPAD, ///< Aperture used for vias.
GBR_APERTURE_ATTRIB_VIAPAD, ///< Aperture used for vias.
/// Aperture used for through hole component on outer layer.
GBR_APERTURE_ATTRIB_COMPONENTPAD,
@ -119,8 +119,8 @@ public:
/// Aperture used for edge connector pad (outer layers).
GBR_APERTURE_ATTRIB_CONNECTORPAD,
GBR_APERTURE_ATTRIB_WASHERPAD, ///< Aperture used for mechanical pads (NPTH).
GBR_APERTURE_ATTRIB_TESTPOINT, ///< Aperture used for test point pad (outer layers).
GBR_APERTURE_ATTRIB_WASHERPAD, ///< Aperture used for mechanical pads (NPTH).
GBR_APERTURE_ATTRIB_TESTPOINT, ///< Aperture used for test point pad (outer layers).
/// Aperture used for fiducial pad (outer layers), at board level.
GBR_APERTURE_ATTRIB_FIDUCIAL_GLBL,
@ -137,8 +137,8 @@ public:
/// Aperture used for castellated pads in drill files.
GBR_APERTURE_ATTRIB_CASTELLATEDDRILL,
GBR_APERTURE_ATTRIB_VIADRILL, ///< Aperture used for via holes in drill files.
GBR_APERTURE_ATTRIB_CMP_DRILL, ///< Aperture used for pad holes in drill files.
GBR_APERTURE_ATTRIB_VIADRILL, ///< Aperture used for via holes in drill files.
GBR_APERTURE_ATTRIB_CMP_DRILL, ///< Aperture used for pad holes in drill files.
/// Aperture used for pads oblong holes in drill files.
GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL,
@ -163,11 +163,13 @@ public:
/// Aperture used to draw component outline courtyard in placement files.
GBR_APERTURE_ATTRIB_CMP_COURTYARD,
GBR_APERTURE_ATTRIB_END ///< sentinel: max value
///< aperture used for other purposes. Requires a text description of this feature.
GBR_APERTURE_ATTRIB_OTHER,
GBR_APERTURE_ATTRIB_END ///< sentinel: max value
};
GBR_APERTURE_METADATA()
:m_ApertAttribute( GBR_APERTURE_ATTRIB_NONE )
GBR_APERTURE_METADATA() : m_ApertAttribute( GBR_APERTURE_ATTRIB_NONE ), m_CustomAttribute( "" )
{}
/**
@ -187,15 +189,18 @@ public:
* like "%TA.AperFunction,<function>*%"
*/
static std::string FormatAttribute( GBR_APERTURE_ATTRIB aAttribute,
bool aUseX1StructuredComment );
bool aUseX1StructuredComment,
const std::string& aCustomAttribute );
std::string FormatAttribute( bool aUseX1StructuredComment )
{
return FormatAttribute( m_ApertAttribute, aUseX1StructuredComment );
return FormatAttribute( m_ApertAttribute, aUseX1StructuredComment, m_CustomAttribute );
}
// The id of the aperture attribute
GBR_APERTURE_ATTRIB m_ApertAttribute;
std::string m_CustomAttribute;
};
@ -212,11 +217,19 @@ public:
m_ApertureMetadata.m_ApertAttribute = aApertAttribute;
}
void SetApertureAttrib( std::string aCustomAttribute )
{
m_ApertureMetadata.m_ApertAttribute = GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_OTHER;
m_ApertureMetadata.m_CustomAttribute = aCustomAttribute;
}
GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB GetApertureAttrib()
{
return m_ApertureMetadata.m_ApertAttribute;
}
std::string GetCustomAttribute() { return m_ApertureMetadata.m_CustomAttribute; }
void SetNetAttribType( int aNetAttribType )
{
m_NetlistMetadata.m_NetAttribType = aNetAttribType;

View File

@ -19,6 +19,8 @@
#pragma once
#include <optional>
#include <wx/stream.h>
#include <wx/string.h>
@ -38,6 +40,18 @@ namespace KICAD_FORMAT {
*/
KICOMMON_API void FormatBool( OUTPUTFORMATTER* aOut, const wxString& aKey, bool aValue );
/**
* Writes an optional boolean to the formatter.
* If a value is present, calls FormatBool.
* If no value is present, Writes (aKey none).
*
* @param aOut is the output formatter to write to
* @param aKey is the name of the boolean flag
* @param aValue is the value to write
*/
KICOMMON_API void FormatOptBool( OUTPUTFORMATTER* aOut, const wxString& aKey,
std::optional<bool> aValue );
KICOMMON_API void FormatUuid( OUTPUTFORMATTER* aOut, const KIID& aUuid );
/**

View File

@ -139,6 +139,8 @@ public:
// Only one attribute is allowed by aperture
// 0 = no specific aperture attribute
int m_ApertureAttribute;
std::string m_CustomAttribute;
};

View File

@ -245,11 +245,13 @@ public:
* @param aType is the type ( shape ) of tool.
* @param aApertureAttribute is an aperture attribute of the tool (a tool can have only one
* attribute) 0 = no specific attribute.
* @param aCustomAttribute a String describing custom tools
* @return an index to the aperture in aperture list which meets the size and type of tool
* if the aperture does not exist, it is created and entered in aperture list.
*/
int GetOrCreateAperture( const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
APERTURE::APERTURE_TYPE aType, int aApertureAttribute,
const std::string& aCustomAttribute );
/**
* @param aCorners is the corner list.
@ -257,11 +259,13 @@ public:
* @param aType is the type ( shape ) of tool that can manage a list of corners (polygon).
* @param aApertureAttribute is an aperture attribute of the tool (a tool can have only one
* attribute) 0 = no specific attribute.
* @param aCustomAttribute a String describing custom tools
* @return an index to the aperture in aperture list which meets the data and type of tool
* if the aperture does not exist, it is created and entered in aperture list.
*/
int GetOrCreateAperture( const std::vector<VECTOR2I>& aCorners, const EDA_ANGLE& aRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
APERTURE::APERTURE_TYPE aType, int aApertureAttribute,
const std::string& aCustomAttribute );
protected:
virtual void ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle,
@ -301,7 +305,8 @@ protected:
* Write the DCode selection on gerber file.
*/
void selectAperture( const VECTOR2I& aSize, int aRadius, const EDA_ANGLE& aRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
APERTURE::APERTURE_TYPE aType, int aApertureAttribute,
const std::string& aCustomAttribute );
/**
* Pick an existing aperture or create a new one, matching the aDiameter, aPolygonRotation,
* type and attributes.
@ -310,18 +315,17 @@ protected:
* write the DCode selection on gerber file
*/
void selectAperture( const std::vector<VECTOR2I>& aCorners, const EDA_ANGLE& aPolygonRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
APERTURE::APERTURE_TYPE aType, int aApertureAttribute,
const std::string& aCustomAttribute );
/**
* Pick an existing aperture or create a new one, matching the corner list, aRotDegree,
* type and attributes.
*
* It only applies to apertures managing a polygon that differs from AT_REGULAR_POLY3
* to AT_REGULAR_POLY12 (for instance APER_MACRO_TRAPEZOID ) write the DCode selection
* on gerber file.
* Pick an aperture or create a new one and emits the DCode.
*
*/
void selectAperture( int aDiameter, const EDA_ANGLE& aRotation,
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
void selectApertureWithAttributes( const VECTOR2I& aPos, GBR_METADATA* aGbrMetadata,
VECTOR2I aSize, int aRadius, const EDA_ANGLE& aAngle,
APERTURE::APERTURE_TYPE aType );
/**
* Emit a D-Code record, using proper conversions to format a leading zero omitted gerber

View File

@ -38,6 +38,7 @@
#define ARG_GERBER_PRECISION "--gerber-precision"
#define ARG_EXCELLON_UNITS "--excellon-units"
#define ARG_GENERATE_MAP "--generate-map"
#define ARG_GENERATE_TENTING "--generate-tenting"
#define ARG_MAP_FORMAT "--map-format"
#define ARG_DRILL_ORIGIN "--drill-origin"
@ -89,6 +90,10 @@ CLI::PCB_EXPORT_DRILL_COMMAND::PCB_EXPORT_DRILL_COMMAND() : PCB_EXPORT_BASE_COMM
.help( UTF8STDSTR( _( "Generate map / summary of drill hits" ) ) )
.flag();
m_argParser.add_argument( ARG_GENERATE_TENTING )
.help( UTF8STDSTR( _( "Generate a file specifically for tenting" ) ) )
.flag();
m_argParser.add_argument( ARG_MAP_FORMAT )
.default_value( std::string( "pdf" ) )
.help( UTF8STDSTR( _( "Valid options: pdf,gerberx2,ps,dxf,svg" ) ) )
@ -240,6 +245,7 @@ int CLI::PCB_EXPORT_DRILL_COMMAND::doPerform( KIWAY& aKiway )
drillJob->m_excellonMinimalHeader = m_argParser.get<bool>( ARG_EXCELLON_MINIMALHEAD );
drillJob->m_excellonCombinePTHNPTH = !m_argParser.get<bool>( ARG_EXCELLON_SEPARATE_TH );
drillJob->m_generateMap = m_argParser.get<bool>( ARG_GENERATE_MAP );
drillJob->m_generateTenting = m_argParser.get<bool>( ARG_GENERATE_TENTING );
drillJob->m_gerberPrecision = m_argParser.get<int>( ARG_GERBER_PRECISION );
if( drillJob->m_gerberPrecision != 5 && drillJob->m_gerberPrecision != 6 )

View File

@ -222,6 +222,16 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
m_TentViasFront = true;
m_TentViasBack = true;
m_CoverViasFront = false;
m_CoverViasBack = false;
m_PlugViasFront = false;
m_PlugViasBack = false;
m_CapVias = false;
m_FillVias = false;
// Layer thickness for 3D viewer
m_boardThickness = pcbIUScale.mmToIU( DEFAULT_BOARD_THICKNESS_MM );

View File

@ -59,6 +59,7 @@ bool DIALOG_GENDRILL::g_minimalHeader = false; // Only for Excellon format
bool DIALOG_GENDRILL::g_mirror = false; // Only for Excellon format
bool DIALOG_GENDRILL::g_merge_PTH_NPTH = false; // Only for Excellon format
bool DIALOG_GENDRILL::g_generateMap = false;
bool DIALOG_GENDRILL::g_generateTenting = false;
int DIALOG_GENDRILL::g_mapFileType = 4; // The last choice in m_Choice_Drill_Map
int DIALOG_GENDRILL::g_drillFileType = 0;
@ -129,7 +130,8 @@ bool DIALOG_GENDRILL::TransferDataFromWindow()
{
if( !m_job )
{
genDrillAndMapFiles( true, m_cbGenerateMap->GetValue() );
genDrillAndMapFiles( true, m_cbGenerateMap->GetValue(),
m_generateTentingLayers->GetValue() );
}
else
{
@ -147,6 +149,7 @@ bool DIALOG_GENDRILL::TransferDataFromWindow()
m_job->m_mapFormat = static_cast<JOB_EXPORT_PCB_DRILL::MAP_FORMAT>( m_choiceDrillMap->GetSelection() );
m_job->m_zeroFormat = static_cast<JOB_EXPORT_PCB_DRILL::ZEROS_FORMAT>( m_zeros->GetSelection() );
m_job->m_generateMap = m_cbGenerateMap->IsChecked();
m_job->m_generateTenting = m_generateTentingLayers->IsChecked();
}
return true;
@ -171,6 +174,7 @@ bool DIALOG_GENDRILL::TransferDataToWindow()
m_choiceDrillMap->SetSelection( g_mapFileType );
m_altDrillMode->SetValue( !g_useRouteModeForOvalHoles );
m_cbGenerateMap->SetValue( g_generateMap );
m_generateTentingLayers->SetValue( g_generateTenting );
// Output directory
m_outputDirectoryName->SetValue( m_plotOpts.GetOutputDirectory() );
@ -194,6 +198,7 @@ bool DIALOG_GENDRILL::TransferDataToWindow()
m_choiceDrillMap->SetSelection( static_cast<int>( m_job->m_mapFormat ) );
m_altDrillMode->SetValue( !m_job->m_excellonOvalDrillRoute );
m_cbGenerateMap->SetValue( m_job->m_generateMap );
m_generateTentingLayers->SetValue( m_job->m_generateTenting );
}
wxCommandEvent dummy;
@ -221,6 +226,7 @@ void DIALOG_GENDRILL::initDialog()
g_mapFileType = cfg->m_GenDrill.map_file_type;
g_zerosFormat = cfg->m_GenDrill.zeros_format;
g_generateMap = cfg->m_GenDrill.generate_map;
g_generateTenting = cfg->m_GenDrill.generate_tenting;
// Ensure validity of g_mapFileType
if( g_mapFileType < 0 || g_mapFileType >= (int) m_choiceDrillMap->GetCount() )
@ -249,6 +255,7 @@ void DIALOG_GENDRILL::onFileFormatSelection( wxCommandEvent& event )
m_Check_Minimal->Enable( enbl_Excellon );
m_Check_Merge_PTH_NPTH->Enable( enbl_Excellon );
m_altDrillMode->Enable( enbl_Excellon );
m_generateTentingLayers->Enable( !enbl_Excellon );
if( enbl_Excellon )
{
@ -279,6 +286,7 @@ void DIALOG_GENDRILL::updateConfig()
cfg->m_GenDrill.map_file_type = g_mapFileType;
cfg->m_GenDrill.zeros_format = g_zerosFormat;
cfg->m_GenDrill.generate_map = g_generateMap;
cfg->m_GenDrill.generate_tenting = g_generateTenting;
}
@ -373,6 +381,7 @@ void DIALOG_GENDRILL::UpdateDrillParams()
g_zerosFormat = m_zeros->GetSelection();
g_useRouteModeForOvalHoles = !m_altDrillMode->GetValue();
g_generateMap = m_cbGenerateMap->IsChecked();
g_generateTenting = m_generateTentingLayers->IsChecked();
if( m_origin->GetSelection() == 0 )
g_drillFileOffset = VECTOR2I( 0, 0 );
@ -392,7 +401,7 @@ void DIALOG_GENDRILL::UpdateDrillParams()
}
void DIALOG_GENDRILL::genDrillAndMapFiles( bool aGenDrill, bool aGenMap )
void DIALOG_GENDRILL::genDrillAndMapFiles( bool aGenDrill, bool aGenMap, bool aGenTenting )
{
updateConfig(); // set params and Save drill options
@ -462,7 +471,7 @@ void DIALOG_GENDRILL::genDrillAndMapFiles( bool aGenDrill, bool aGenMap )
gerberWriter.SetMapFileFormat( filefmt[choice] );
gerberWriter.CreateDrillandMapFilesSet( outputDir.GetFullPath(), aGenDrill, aGenMap,
&reporter );
aGenTenting, &reporter );
}
}

View File

@ -84,7 +84,7 @@ private:
* plated through holes, and one file per layer pair, which have one or more holes, excluding
* through holes, already in the first file. One file for all Not Plated through holes.
*/
void genDrillAndMapFiles( bool aGenDrill, bool aGenMap );
void genDrillAndMapFiles( bool aGenDrill, bool aGenMap, bool aGenTenting );
void updatePrecisionOptions();
void updateConfig();
@ -96,6 +96,7 @@ private:
static bool g_mirror;
static bool g_merge_PTH_NPTH;
static bool g_generateMap;
static bool g_generateTenting;
static DRILL_PRECISION g_precision; // Precision for drill files in non-decimal
// format
static VECTOR2I g_drillFileOffset; // Drill offset: 0,0 for absolute

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -84,6 +84,19 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
m_rbGerberX2 = new wxRadioButton( this, wxID_ANY, _("Gerber X2"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerMargins->Add( m_rbGerberX2, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
wxFlexGridSizer* fgSizerGerberX2Options;
fgSizerGerberX2Options = new wxFlexGridSizer( 4, 1, 3, 0 );
fgSizerGerberX2Options->SetFlexibleDirection( wxBOTH );
fgSizerGerberX2Options->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_generateTentingLayers = new wxCheckBox( this, wxID_ANY, _("Generate tenting layers"), wxDefaultPosition, wxDefaultSize, 0 );
m_generateTentingLayers->Enable( false );
fgSizerGerberX2Options->Add( m_generateTentingLayers, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
bSizerMargins->Add( fgSizerGerberX2Options, 1, wxEXPAND|wxLEFT, 20 );
bSizerMargins->Add( 0, 6, 0, wxEXPAND, 5 );
@ -172,6 +185,9 @@ DIALOG_GENDRILL_BASE::DIALOG_GENDRILL_BASE( wxWindow* parent, wxWindowID id, con
fgSizer1->Add( m_staticTextPrecision, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 );
bRightCol->Add( fgSizer1, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 );

View File

@ -846,6 +846,89 @@
<event name="OnRadioButton">onFileFormatSelection</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">20</property>
<property name="flag">wxEXPAND|wxLEFT</property>
<property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="true">
<property name="cols">1</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols"></property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizerGerberX2Options</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">4</property>
<property name="vgap">3</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">0</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Generate tenting layers</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_generateTentingLayers</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
@ -1684,6 +1767,16 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object>
</object>
</object>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -55,6 +55,7 @@ class DIALOG_GENDRILL_BASE : public DIALOG_SHIM
wxCheckBox* m_Check_Merge_PTH_NPTH;
wxCheckBox* m_altDrillMode;
wxRadioButton* m_rbGerberX2;
wxCheckBox* m_generateTentingLayers;
wxCheckBox* m_cbGenerateMap;
wxChoice* m_choiceDrillMap;
wxStaticText* m_optionsLabel;

View File

@ -35,6 +35,15 @@
#include <kidialog.h>
#include <connectivity/connectivity_data.h>
#include <board_commit.h>
#include <magic_enum.hpp>
bool DIALOG_TRACK_VIA_PROPERTIES::IPC4761_CONFIGURATION::operator==(
const IPC4761_CONFIGURATION& aOther ) const
{
return ( tent == aOther.tent ) && ( plug == aOther.plug ) && ( cover == aOther.cover )
&& ( cap == aOther.cap ) && ( fill == aOther.fill );
}
DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent,
@ -101,11 +110,6 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
m_ViaEndLayer->SetBoardFrame( aParent );
m_ViaEndLayer->Resync();
m_btnLinkTenting->SetBitmap( KiBitmapBundle( BITMAPS::edit_cmp_symb_links ) );
m_btnLinkTenting->SetValue( true );
m_tentingBackCtrl->Disable();
m_tentingBackLabel->Disable();
wxFont infoFont = KIUI::GetInfoFont( this );
m_techLayersLabel->SetFont( infoFont );
@ -123,33 +127,101 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
// The selection layer for tracks
int track_selection_layer = -1;
auto getAnnularRingSelection =
[]( const PCB_VIA* via ) -> int
{
switch( via->Padstack().UnconnectedLayerMode() )
{
default:
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL: return 0;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END: return 1;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL: return 2;
}
};
auto getAnnularRingSelection = []( const PCB_VIA* via ) -> int
{
switch( via->Padstack().UnconnectedLayerMode() )
{
default:
case PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL: return 0;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END: return 1;
case PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_ALL: return 2;
}
};
auto getTentingSelection =
[]( const PCB_VIA* via, PCB_LAYER_ID aLayer ) -> int
{
std::optional<bool> tentingOverride = via->Padstack().IsTented( aLayer );
for( auto& preset : magic_enum::enum_values<IPC4761_PRESET>() )
{
if( preset >= IPC4761_PRESET::CUSTOM )
continue;
if( tentingOverride.has_value() )
{
if( *tentingOverride )
return 1; // Tented
const auto& name_it = m_IPC4761Names.find( preset );
return 2; // Not tented
}
wxString name = _( "Unknown choice" );
return 0; // From design rules
};
if( name_it != m_IPC4761Names.end() )
name = name_it->second;
m_protectionFeatures->AppendString( name );
}
auto getProtectionSurface = []( const std::optional<bool>& front,
const std::optional<bool>& back ) -> IPC4761_SURFACE
{
IPC4761_SURFACE value = IPC4761_SURFACE::CUSTOM;
if( !front.has_value() )
value = IPC4761_SURFACE::FROM_RULES;
else if( front.value() )
value = IPC4761_SURFACE::FRONT;
else
value = IPC4761_SURFACE::NONE;
if( !back.has_value() )
{
if( value == IPC4761_SURFACE::FROM_RULES )
return IPC4761_SURFACE::FROM_RULES;
}
else if( back.value() )
{
if( value == IPC4761_SURFACE::FRONT )
return IPC4761_SURFACE::BOTH;
else if( value == IPC4761_SURFACE::NONE )
return IPC4761_SURFACE::BACK;
}
else
{
if( value == IPC4761_SURFACE::FRONT )
return IPC4761_SURFACE::FRONT;
else if( value == IPC4761_SURFACE::NONE )
return IPC4761_SURFACE::NONE;
}
return IPC4761_SURFACE::CUSTOM;
};
auto getProtectionDrill = []( const std::optional<bool>& drill ) -> IPC4761_DRILL
{
if( !drill.has_value() )
return IPC4761_DRILL::FROM_RULES;
if( drill.value() )
return IPC4761_DRILL::SET;
return IPC4761_DRILL::NOT_SET;
};
auto getViaConfiguration = [&]( const PCB_VIA* via ) -> IPC4761_PRESET
{
IPC4761_CONFIGURATION config;
config.tent = getProtectionSurface( via->Padstack().FrontOuterLayers().has_solder_mask,
via->Padstack().BackOuterLayers().has_solder_mask );
config.cover = getProtectionSurface( via->Padstack().FrontOuterLayers().has_covering,
via->Padstack().BackOuterLayers().has_covering );
config.plug = getProtectionSurface( via->Padstack().FrontOuterLayers().has_plugging,
via->Padstack().BackOuterLayers().has_plugging );
config.cap = getProtectionDrill( via->Padstack().Drill().is_capped );
config.fill = getProtectionDrill( via->Padstack().Drill().is_filled );
for( const auto& [preset, configuration] : m_IPC4761Presets )
{
if( configuration == config )
return preset;
}
return IPC4761_PRESET::CUSTOM;
};
// Look for values that are common for every item that is selected
for( EDA_ITEM* item : m_items )
@ -239,16 +311,6 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
m_viaNotFree->SetValue( !v->GetIsFree() );
m_annularRingsCtrl->SetSelection( getAnnularRingSelection( v ) );
m_tentingFrontCtrl->SetSelection( getTentingSelection( v, F_Mask ) );
m_tentingBackCtrl->SetSelection( getTentingSelection( v, B_Mask ) );
bool link = m_tentingFrontCtrl->GetSelection()
== m_tentingBackCtrl->GetSelection();
m_btnLinkTenting->SetValue( link );
m_tentingBackCtrl->Enable( !link );
m_tentingBackLabel->Enable( !link );
selection_first_layer = v->TopLayer();
selection_last_layer = v->BottomLayer();
@ -260,6 +322,18 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
m_teardropWidthPercent.SetDoubleValue( v->GetTeardropParams().m_BestWidthRatio*100.0 );
m_teardropHDPercent.SetDoubleValue( v->GetTeardropParams().m_WidthtoSizeFilterRatio*100.0 );
m_curvedEdges->SetValue( v->GetTeardropParams().m_CurvedEdges );
IPC4761_PRESET preset = getViaConfiguration( v );
if( preset >= IPC4761_PRESET::CUSTOM )
{
m_protectionFeatures->SetSelection(
m_protectionFeatures->Append( INDETERMINATE_ACTION ) );
}
else
{
m_protectionFeatures->SetSelection( static_cast<int>( preset ) );
}
}
else // check if values are the same for every selected via
{
@ -315,6 +389,14 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
if( m_teardropHDPercent.GetDoubleValue() != v->GetTeardropParams().m_WidthtoSizeFilterRatio*100.0 )
m_teardropHDPercent.SetValue( INDETERMINATE_STATE );
if( static_cast<int>( getViaConfiguration( v ) )
!= m_protectionFeatures->GetSelection() )
{
m_protectionFeatures->SetSelection(
m_protectionFeatures->Append( INDETERMINATE_STATE ) );
}
}
if( v->IsLocked() )
@ -775,21 +857,6 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
break;
}
switch( m_tentingFrontCtrl->GetSelection() )
{
default:
case 0: via->Padstack().FrontOuterLayers().has_solder_mask.reset(); break;
case 1: via->Padstack().FrontOuterLayers().has_solder_mask = true; break;
case 2: via->Padstack().FrontOuterLayers().has_solder_mask = false; break;
}
switch( m_tentingBackCtrl->GetSelection() )
{
default:
case 0: via->Padstack().BackOuterLayers().has_solder_mask.reset(); break;
case 1: via->Padstack().BackOuterLayers().has_solder_mask = true; break;
case 2: via->Padstack().BackOuterLayers().has_solder_mask = false; break;
}
if( !m_viaDrill.IsIndeterminate() )
via->SetDrill( m_viaDrill.GetIntValue() );
@ -824,6 +891,72 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
if( changeLock )
via->SetLocked( setLock );
auto setSurfaceProtection = [&]( std::optional<bool>& aFront,
std::optional<bool>& aBack,
IPC4761_SURFACE aProtection )
{
switch( aProtection )
{
case IPC4761_SURFACE::FROM_RULES:
aFront.reset();
aBack.reset();
break;
case IPC4761_SURFACE::NONE:
aFront = false;
aBack = false;
break;
case IPC4761_SURFACE::FRONT:
aFront = true;
aBack = false;
break;
case IPC4761_SURFACE::BACK:
aFront = false;
aBack = true;
break;
case IPC4761_SURFACE::BOTH:
aFront = true;
aBack = true;
break;
case IPC4761_SURFACE::CUSTOM: return;
}
};
auto setDrillProtection =
[&]( std::optional<bool>& aDrill, IPC4761_DRILL aProtection )
{
switch( aProtection )
{
case IPC4761_DRILL::FROM_RULES: aDrill.reset(); break;
case IPC4761_DRILL::NOT_SET: aDrill = false; break;
case IPC4761_DRILL::SET: aDrill = true; break;
}
};
IPC4761_PRESET selectedPreset =
static_cast<IPC4761_PRESET>( m_protectionFeatures->GetSelection() );
if( selectedPreset < IPC4761_PRESET::CUSTOM ) // Do not change custom feaure list.
{
const IPC4761_CONFIGURATION config = m_IPC4761Presets.at( selectedPreset );
setSurfaceProtection( via->Padstack().FrontOuterLayers().has_solder_mask,
via->Padstack().BackOuterLayers().has_solder_mask,
config.tent );
setSurfaceProtection( via->Padstack().FrontOuterLayers().has_plugging,
via->Padstack().BackOuterLayers().has_plugging,
config.plug );
setSurfaceProtection( via->Padstack().FrontOuterLayers().has_covering,
via->Padstack().BackOuterLayers().has_covering,
config.cover );
setDrillProtection( via->Padstack().Drill().is_filled, config.fill );
setDrillProtection( via->Padstack().Drill().is_capped, config.cap );
}
break;
}
@ -1122,29 +1255,6 @@ void DIALOG_TRACK_VIA_PROPERTIES::onViaEdit( wxCommandEvent& aEvent )
}
void DIALOG_TRACK_VIA_PROPERTIES::onFrontTentingChanged( wxCommandEvent& event )
{
if( m_btnLinkTenting->GetValue() )
m_tentingBackCtrl->SetSelection( m_tentingFrontCtrl->GetSelection() );
event.Skip();
}
void DIALOG_TRACK_VIA_PROPERTIES::onTentingLinkToggle( wxCommandEvent& event )
{
bool link = m_btnLinkTenting->GetValue();
m_tentingBackCtrl->Enable( !link );
m_tentingBackLabel->Enable( !link );
if( link )
m_tentingBackCtrl->SetSelection( m_tentingFrontCtrl->GetSelection() );
event.Skip();
}
void DIALOG_TRACK_VIA_PROPERTIES::onTrackEdit( wxCommandEvent& aEvent )
{
bool externalCuLayer = m_TrackLayerCtrl->GetLayerSelection() == F_Cu

View File

@ -49,8 +49,6 @@ private:
void onWidthEdit( wxCommandEvent& aEvent ) override;
void onViaSelect( wxCommandEvent& aEvent ) override;
void onViaEdit( wxCommandEvent& aEvent ) override;
void onTentingLinkToggle( wxCommandEvent& event ) override;
void onFrontTentingChanged( wxCommandEvent& event ) override;
void onTrackEdit( wxCommandEvent& aEvent ) override;
void onPadstackModeChanged( wxCommandEvent& aEvent ) override;
void onEditLayerChanged( wxCommandEvent& aEvent ) override;
@ -91,4 +89,112 @@ private:
/// The currently-shown copper layer of the edited via(s)
PCB_LAYER_ID m_editLayer;
std::map<int, PCB_LAYER_ID> m_editLayerCtrlMap;
enum class IPC4761_SURFACE
{
FROM_RULES = 0,
NONE = 1,
FRONT = 2,
BACK = 3,
BOTH = 4,
CUSTOM = 5
};
enum class IPC4761_DRILL
{
FROM_RULES = 0,
NOT_SET = 1,
SET = 2
};
enum class IPC4761_PRESET
{
FROM_RULES = 0,
NONE = 1,
IA = 2,
IB = 3,
IA_INVERTED = 4,
IIA = 5,
IIB = 6,
IIA_INVERTED = 7,
IIIA = 8,
IIIB = 9,
IIIA_INVERTED = 10,
IVA = 11,
IVB = 12,
IVA_INVERTED = 13,
V = 14,
VIA = 15,
VIB = 16,
VIA_INVERTED = 17,
VII = 18,
CUSTOM = 19,
END = 20
};
struct IPC4761_CONFIGURATION
{
IPC4761_SURFACE tent{ IPC4761_SURFACE::NONE };
IPC4761_SURFACE cover{ IPC4761_SURFACE::NONE };
IPC4761_SURFACE plug{ IPC4761_SURFACE::NONE };
IPC4761_DRILL fill{ IPC4761_DRILL::NOT_SET };
IPC4761_DRILL cap{ IPC4761_DRILL::NOT_SET };
bool operator==( const IPC4761_CONFIGURATION& other ) const;
};
const std::map<IPC4761_PRESET, IPC4761_CONFIGURATION> m_IPC4761Presets{
{ IPC4761_PRESET::FROM_RULES,
{ IPC4761_SURFACE::FROM_RULES, IPC4761_SURFACE::FROM_RULES, IPC4761_SURFACE::FROM_RULES,
IPC4761_DRILL::FROM_RULES, IPC4761_DRILL::FROM_RULES } },
{ IPC4761_PRESET::NONE, {} },
{ IPC4761_PRESET::IA, { .tent = IPC4761_SURFACE::FRONT } },
{ IPC4761_PRESET::IB, { .tent = IPC4761_SURFACE::BOTH } },
{ IPC4761_PRESET::IA_INVERTED, { .tent = IPC4761_SURFACE::BACK } },
{ IPC4761_PRESET::IIA,
{ .tent = IPC4761_SURFACE::FRONT, .cover = IPC4761_SURFACE::FRONT } },
{ IPC4761_PRESET::IIB, { .tent = IPC4761_SURFACE::BOTH, .cover = IPC4761_SURFACE::BOTH } },
{ IPC4761_PRESET::IIA_INVERTED,
{ .tent = IPC4761_SURFACE::BACK, .cover = IPC4761_SURFACE::BACK } },
{ IPC4761_PRESET::IIIA, { .plug = IPC4761_SURFACE::FRONT } },
{ IPC4761_PRESET::IIIB, { .plug = IPC4761_SURFACE::BOTH } },
{ IPC4761_PRESET::IIIA_INVERTED, { .plug = IPC4761_SURFACE::BACK } },
{ IPC4761_PRESET::IVA, { .tent = IPC4761_SURFACE::FRONT, .plug = IPC4761_SURFACE::FRONT } },
{ IPC4761_PRESET::IVB, { .tent = IPC4761_SURFACE::BOTH, .plug = IPC4761_SURFACE::BOTH } },
{ IPC4761_PRESET::IVA_INVERTED,
{ .tent = IPC4761_SURFACE::BACK, .plug = IPC4761_SURFACE::BACK } },
{ IPC4761_PRESET::V, { .fill = IPC4761_DRILL::SET } },
{ IPC4761_PRESET::VIA, { .tent = IPC4761_SURFACE::FRONT, .fill = IPC4761_DRILL::SET } },
{ IPC4761_PRESET::VIB, { .tent = IPC4761_SURFACE::BOTH, .fill = IPC4761_DRILL::SET } },
{ IPC4761_PRESET::VIA_INVERTED,
{ .tent = IPC4761_SURFACE::BACK, .fill = IPC4761_DRILL::SET } },
{ IPC4761_PRESET::VII, { .fill = IPC4761_DRILL::SET, .cap = IPC4761_DRILL::SET } },
{ IPC4761_PRESET::CUSTOM, {} },
{ IPC4761_PRESET::END, {} }
};
const std::map<IPC4761_PRESET, wxString> m_IPC4761Names{
{ IPC4761_PRESET::FROM_RULES, _( "From rules" ) },
{ IPC4761_PRESET::NONE, _( "None" ) },
{ IPC4761_PRESET::IA, _( "Type I a ( tented top )" ) },
{ IPC4761_PRESET::IB, _( "Type I b ( tented both sides )" ) },
{ IPC4761_PRESET::IA_INVERTED, _( "Type I a ( tented bottom )" ) },
{ IPC4761_PRESET::IIA, _( "Type II a ( covered and tented top )" ) },
{ IPC4761_PRESET::IIB, _( "Type II b ( covered and tented both sides )" ) },
{ IPC4761_PRESET::IIA_INVERTED, _( "Type II a ( covered and tented bottom )" ) },
{ IPC4761_PRESET::IIIA, _( "Type III a ( plugged top )" ) },
{ IPC4761_PRESET::IIIB, _( "Type III b ( plugged both sides )" ) },
{ IPC4761_PRESET::IIIA_INVERTED, _( "Type III a ( plugged bottom )" ) },
{ IPC4761_PRESET::IVA, _( "Type IV a ( plugged and tented top )" ) },
{ IPC4761_PRESET::IVB, _( "Type IV b ( plugged and tented both sides )" ) },
{ IPC4761_PRESET::IVA_INVERTED, _( "Type IV a ( plugged and tented bottom )" ) },
{ IPC4761_PRESET::V, _( "Type V ( filled )" ) },
{ IPC4761_PRESET::VIA, _( "Type VI a ( filled and tented top )" ) },
{ IPC4761_PRESET::VIB, _( "Type VI b ( filled and tented both sides )" ) },
{ IPC4761_PRESET::VIA_INVERTED, _( "Type VI a ( filled and tented bottom )" ) },
{ IPC4761_PRESET::VII, _( "Type VII ( filled and capped )" ) },
{ IPC4761_PRESET::CUSTOM, _( "Custom" ) },
{ IPC4761_PRESET::END, _( "End" ) }
};
};

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -334,18 +334,14 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
wxBoxSizer* viaRightColumn;
viaRightColumn = new wxBoxSizer( wxVERTICAL );
wxFlexGridSizer* fgSizer4;
fgSizer4 = new wxFlexGridSizer( 0, 2, 0, 5 );
fgSizer4->AddGrowableCol( 1 );
fgSizer4->AddGrowableRow( 0 );
fgSizer4->AddGrowableRow( 1 );
fgSizer4->AddGrowableRow( 2 );
fgSizer4->SetFlexibleDirection( wxBOTH );
fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
wxGridBagSizer* gbSizer4;
gbSizer4 = new wxGridBagSizer( 0, 0 );
gbSizer4->SetFlexibleDirection( wxBOTH );
gbSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_ViaTypeLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Via type:"), wxDefaultPosition, wxDefaultSize, 0 );
m_ViaTypeLabel->Wrap( -1 );
fgSizer4->Add( m_ViaTypeLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
gbSizer4->Add( m_ViaTypeLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALL, 5 );
wxString m_ViaTypeChoiceChoices[] = { _("Through"), _("Micro"), _("Blind/buried") };
int m_ViaTypeChoiceNChoices = sizeof( m_ViaTypeChoiceChoices ) / sizeof( wxString );
@ -353,79 +349,47 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
m_ViaTypeChoice->SetSelection( 0 );
m_ViaTypeChoice->Enable( false );
fgSizer4->Add( m_ViaTypeChoice, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizer4->Add( 0, 3, 1, wxEXPAND, 5 );
fgSizer4->Add( 0, 0, 1, wxEXPAND, 5 );
gbSizer4->Add( m_ViaTypeChoice, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxBOTTOM|wxEXPAND, 5 );
m_ViaStartLayerLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Start layer:"), wxDefaultPosition, wxDefaultSize, 0 );
m_ViaStartLayerLabel->Wrap( -1 );
fgSizer4->Add( m_ViaStartLayerLabel, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
gbSizer4->Add( m_ViaStartLayerLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_LEFT|wxALL, 5 );
m_ViaStartLayer = new PCB_LAYER_BOX_SELECTOR( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
fgSizer4->Add( m_ViaStartLayer, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
gbSizer4->Add( m_ViaStartLayer, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxBOTTOM|wxEXPAND, 5 );
m_ViaEndLayerLabel1 = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("End layer:"), wxDefaultPosition, wxDefaultSize, 0 );
m_ViaEndLayerLabel1->Wrap( -1 );
fgSizer4->Add( m_ViaEndLayerLabel1, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
gbSizer4->Add( m_ViaEndLayerLabel1, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_LEFT|wxALL, 5 );
m_ViaEndLayer = new PCB_LAYER_BOX_SELECTOR( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
fgSizer4->Add( m_ViaEndLayer, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizer4->Add( 0, 15, 1, wxEXPAND, 5 );
fgSizer4->Add( 0, 0, 1, wxEXPAND, 5 );
gbSizer4->Add( m_ViaEndLayer, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxBOTTOM|wxEXPAND, 5 );
m_annularRingsLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Annular rings:"), wxDefaultPosition, wxDefaultSize, 0 );
m_annularRingsLabel->Wrap( -1 );
fgSizer4->Add( m_annularRingsLabel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT, 5 );
gbSizer4->Add( m_annularRingsLabel, wxGBPosition( 3, 0 ), wxGBSpan( 1, 1 ), wxALIGN_LEFT|wxALL, 5 );
wxString m_annularRingsCtrlChoices[] = { _("All copper layers"), _("Start, end, and connected layers"), _("Connected layers only") };
int m_annularRingsCtrlNChoices = sizeof( m_annularRingsCtrlChoices ) / sizeof( wxString );
m_annularRingsCtrl = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_annularRingsCtrlNChoices, m_annularRingsCtrlChoices, 0 );
m_annularRingsCtrl->SetSelection( 1 );
fgSizer4->Add( m_annularRingsCtrl, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND, 5 );
m_annularRingsCtrl->SetSelection( 0 );
gbSizer4->Add( m_annularRingsCtrl, wxGBPosition( 3, 1 ), wxGBSpan( 1, 1 ), wxBOTTOM|wxEXPAND, 5 );
m_tentingFrontLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Front tenting:"), wxDefaultPosition, wxDefaultSize, 0 );
m_tentingFrontLabel->Wrap( -1 );
fgSizer4->Add( m_tentingFrontLabel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_protectionPresetsLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Protection features:"), wxDefaultPosition, wxDefaultSize, 0 );
m_protectionPresetsLabel->Wrap( -1 );
gbSizer4->Add( m_protectionPresetsLabel, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_LEFT|wxALL, 5 );
wxString m_tentingFrontCtrlChoices[] = { _("From design rules"), _("Tented"), _("Not tented") };
int m_tentingFrontCtrlNChoices = sizeof( m_tentingFrontCtrlChoices ) / sizeof( wxString );
m_tentingFrontCtrl = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_tentingFrontCtrlNChoices, m_tentingFrontCtrlChoices, 0 );
m_tentingFrontCtrl->SetSelection( 0 );
m_tentingFrontCtrl->SetToolTip( _("Whether to tent (cover with soldermask) this via on the front side") );
wxArrayString m_protectionFeaturesChoices;
m_protectionFeatures = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_protectionFeaturesChoices, 0 );
m_protectionFeatures->SetSelection( 0 );
m_protectionFeatures->SetToolTip( _("Select which protection feature according to IPC-4761 the via should have.") );
fgSizer4->Add( m_tentingFrontCtrl, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND, 5 );
m_btnLinkTenting = new wxBitmapToggleButton( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 );
m_btnLinkTenting->SetValue( true );
m_btnLinkTenting->SetToolTip( _("Link front and back tenting settings") );
fgSizer4->Add( m_btnLinkTenting, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 );
gbSizer4->Add( m_protectionFeatures, wxGBPosition( 4, 1 ), wxGBSpan( 1, 1 ), wxBOTTOM|wxEXPAND, 5 );
fgSizer4->Add( 0, 0, 1, wxEXPAND, 5 );
gbSizer4->AddGrowableCol( 1 );
m_tentingBackLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Back tenting:"), wxDefaultPosition, wxDefaultSize, 0 );
m_tentingBackLabel->Wrap( -1 );
fgSizer4->Add( m_tentingBackLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 5 );
wxString m_tentingBackCtrlChoices[] = { _("From design rules"), _("Tented"), _("Not tented") };
int m_tentingBackCtrlNChoices = sizeof( m_tentingBackCtrlChoices ) / sizeof( wxString );
m_tentingBackCtrl = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_tentingBackCtrlNChoices, m_tentingBackCtrlChoices, 0 );
m_tentingBackCtrl->SetSelection( 0 );
m_tentingBackCtrl->SetToolTip( _("Whether to tent (cover with soldermask) this via on the back side") );
fgSizer4->Add( m_tentingBackCtrl, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
viaRightColumn->Add( fgSizer4, 0, wxEXPAND|wxBOTTOM, 3 );
viaRightColumn->Add( gbSizer4, 1, wxEXPAND, 5 );
bSizerViaCols->Add( viaRightColumn, 1, wxEXPAND, 5 );
@ -692,8 +656,6 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
m_ViaTypeChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_ViaStartLayer->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_ViaEndLayer->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_tentingFrontCtrl->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onFrontTentingChanged ), NULL, this );
m_btnLinkTenting->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTentingLinkToggle ), NULL, this );
m_cbTeardrops->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
m_cbTeardropsUseNextTrack->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
m_stHDRatio->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
@ -735,8 +697,6 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::~DIALOG_TRACK_VIA_PROPERTIES_BASE()
m_ViaTypeChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_ViaStartLayer->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_ViaEndLayer->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
m_tentingFrontCtrl->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onFrontTentingChanged ), NULL, this );
m_btnLinkTenting->Disconnect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTentingLinkToggle ), NULL, this );
m_cbTeardrops->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
m_cbTeardropsUseNextTrack->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
m_stHDRatio->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );

View File

@ -2084,7 +2084,7 @@
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="true">
<object class="wxBoxSizer" expanded="false">
<property name="minimum_size"></property>
<property name="name">bSizerViasLeftCol</property>
<property name="orient">wxVERTICAL</property>
@ -2423,11 +2423,11 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP</property>
<property name="proportion">1</property>
<object class="wxGridBagSizer" expanded="true">
<object class="wxGridBagSizer" expanded="false">
<property name="empty_cell_size">-1,0</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
@ -2438,7 +2438,7 @@
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="vgap">3</property>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">1</property>
<property name="colspan">1</property>
<property name="column">0</property>
@ -2503,7 +2503,7 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">3</property>
<property name="colspan">1</property>
<property name="column">1</property>
@ -2572,7 +2572,7 @@
<event name="OnChoice">onViaSelect</event>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
@ -2637,23 +2637,23 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">0</property>
<property name="colspan">3</property>
<property name="column">0</property>
<property name="flag">wxEXPAND</property>
<property name="row">2</property>
<property name="rowspan">1</property>
<object class="wxBoxSizer" expanded="true">
<object class="wxBoxSizer" expanded="false">
<property name="minimum_size"></property>
<property name="name">m_sbPadstackSettings</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">protected</property>
<object class="sizeritem" expanded="true">
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -2711,11 +2711,11 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="true">
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -2777,11 +2777,11 @@
<event name="OnChoice">onPadstackModeChanged</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -2839,11 +2839,11 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="true">
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -2907,7 +2907,7 @@
</object>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">1</property>
<property name="colspan">1</property>
<property name="column">0</property>
@ -2972,7 +2972,7 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
@ -3041,7 +3041,7 @@
<event name="OnText">onViaEdit</event>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
@ -3106,7 +3106,7 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">1</property>
<property name="colspan">1</property>
<property name="column">0</property>
@ -3171,7 +3171,7 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">1</property>
@ -3240,7 +3240,7 @@
<event name="OnText">onViaEdit</event>
</object>
</object>
<object class="gbsizeritem" expanded="true">
<object class="gbsizeritem" expanded="false">
<property name="border">5</property>
<property name="colspan">1</property>
<property name="column">2</property>
@ -3329,25 +3329,27 @@
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">3</property>
<property name="flag">wxEXPAND|wxBOTTOM</property>
<property name="proportion">0</property>
<object class="wxFlexGridSizer" expanded="true">
<property name="cols">2</property>
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxGridBagSizer" expanded="true">
<property name="empty_cell_size"></property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
<property name="growablerows">0,1,2</property>
<property name="hgap">5</property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizer4</property>
<property name="name">gbSizer4</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALL</property>
<property name="row">0</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3406,10 +3408,13 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="row">0</property>
<property name="rowspan">1</property>
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3472,30 +3477,13 @@
<event name="OnChoice">onViaEdit</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">3</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_LEFT|wxALL</property>
<property name="row">1</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3554,10 +3542,13 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="row">1</property>
<property name="rowspan">1</property>
<object class="wxBitmapComboBox" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3621,10 +3612,13 @@
<event name="OnCombobox">onViaEdit</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_LEFT|wxALL</property>
<property name="row">2</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3683,10 +3677,13 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="row">2</property>
<property name="rowspan">1</property>
<object class="wxBitmapComboBox" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3750,31 +3747,14 @@
<event name="OnCombobox">onViaEdit</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">15</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_LEFT|wxALL</property>
<property name="row">3</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -3832,11 +3812,14 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="true">
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="row">3</property>
<property name="rowspan">1</property>
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -3881,7 +3864,7 @@
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">1</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
@ -3897,11 +3880,14 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<property name="colspan">1</property>
<property name="column">0</property>
<property name="flag">wxALIGN_LEFT|wxALL</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
@ -3930,7 +3916,7 @@
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Front tenting:</property>
<property name="label">Protection features:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
@ -3939,7 +3925,7 @@
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_tentingFrontLabel</property>
<property name="name">m_protectionPresetsLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
@ -3959,10 +3945,13 @@
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<object class="gbsizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND</property>
<property name="proportion">0</property>
<property name="colspan">1</property>
<property name="column">1</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="row">4</property>
<property name="rowspan">1</property>
<object class="wxChoice" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
@ -3977,7 +3966,7 @@
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;From design rules&quot; &quot;Tented&quot; &quot;Not tented&quot;</property>
<property name="choices"></property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
@ -4000,7 +3989,7 @@
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_tentingFrontCtrl</property>
<property name="name">m_protectionFeatures</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
@ -4014,218 +4003,7 @@
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Whether to tent (cover with soldermask) this via on the front side</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChoice">onFrontTentingChanged</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBitmapToggleButton" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Link</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_btnLinkTenting</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Link front and back tenting settings</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value">1</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnToggleButton">onTentingLinkToggle</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Back tenting:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_tentingBackLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;From design rules&quot; &quot;Tented&quot; &quot;Not tented&quot;</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_tentingBackCtrl</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Whether to tent (cover with soldermask) this via on the back side</property>
<property name="tooltip">Select which protection feature according to IPC-4761 the via should have.</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -28,7 +28,6 @@ class PCB_LAYER_BOX_SELECTOR;
#include <wx/choice.h>
#include <wx/gbsizer.h>
#include <wx/bmpcbox.h>
#include <wx/tglbtn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
@ -111,11 +110,8 @@ class DIALOG_TRACK_VIA_PROPERTIES_BASE : public DIALOG_SHIM
PCB_LAYER_BOX_SELECTOR* m_ViaEndLayer;
wxStaticText* m_annularRingsLabel;
wxChoice* m_annularRingsCtrl;
wxStaticText* m_tentingFrontLabel;
wxChoice* m_tentingFrontCtrl;
wxBitmapToggleButton* m_btnLinkTenting;
wxStaticText* m_tentingBackLabel;
wxChoice* m_tentingBackCtrl;
wxStaticText* m_protectionPresetsLabel;
wxChoice* m_protectionFeatures;
wxStaticLine* m_staticline2;
wxBoxSizer* m_legacyTeardropsWarning;
wxStaticBitmap* m_legacyTeardropsIcon;
@ -152,8 +148,6 @@ class DIALOG_TRACK_VIA_PROPERTIES_BASE : public DIALOG_SHIM
virtual void onPadstackModeChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void onEditLayerChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void onViaEdit( wxCommandEvent& event ) { event.Skip(); }
virtual void onFrontTentingChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void onTentingLinkToggle( wxCommandEvent& event ) { event.Skip(); }
virtual void onTeardropsUpdateUi( wxUpdateUIEvent& event ) { event.Skip(); }

View File

@ -106,6 +106,27 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair,
via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false );
new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false );
new_hole.m_Hole_Top_Covered =
via->Padstack().IsCovered( new_hole.m_Hole_Top_Layer ).value_or( false );
new_hole.m_Hole_Bot_Covered =
via->Padstack().IsCovered( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Top_Plugged =
via->Padstack().IsPlugged( new_hole.m_Hole_Top_Layer ).value_or( false );
new_hole.m_Hole_Bot_Plugged =
via->Padstack().IsPlugged( new_hole.m_Hole_Bottom_Layer ).value_or( false );
new_hole.m_Hole_Top_Tented =
via->Padstack().IsTented( new_hole.m_Hole_Top_Layer ).value_or( false );
new_hole.m_Hole_Bot_Tented =
via->Padstack().IsTented( new_hole.m_Hole_Bottom_Layer ).value_or( false );
// LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
// Remember: top layer = 0 and bottom layer = 31 for through hole vias
// Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
@ -302,6 +323,61 @@ const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, b
return ret;
}
const wxString GENDRILL_WRITER_BASE::getProtectionFileName( DRILL_LAYER_PAIR aPair,
IPC4761_FEATURES aFeature ) const
{
wxASSERT( m_pcb );
wxString extend;
switch( aFeature )
{
case IPC4761_FEATURES::FILLED:
extend << wxT( "-filling-" );
extend << layerPairName( aPair );
break;
case IPC4761_FEATURES::CAPPED:
extend << wxT( "-capping-" );
extend << layerPairName( aPair );
break;
case IPC4761_FEATURES::COVERED_BACK:
extend << wxT( "-covering-" );
extend << layerName( aPair.second );
break;
case IPC4761_FEATURES::COVERED_FRONT:
extend << wxT( "-covering-" );
extend << layerName( aPair.first );
break;
case IPC4761_FEATURES::PLUGGED_BACK:
extend << wxT( "-plugging-" );
extend << layerName( aPair.second );
break;
case IPC4761_FEATURES::PLUGGED_FRONT:
extend << wxT( "-plugging-" );
extend << layerName( aPair.first );
break;
case IPC4761_FEATURES::TENTED_BACK:
extend << wxT( "-tenting-" );
extend << layerName( aPair.second );
break;
case IPC4761_FEATURES::TENTED_FRONT:
extend << wxT( "-tenting-" );
extend << layerName( aPair.first );
break;
}
wxFileName fn = m_pcb->GetFileName();
fn.SetName( fn.GetName() + extend );
fn.SetExt( m_drillFileExtension );
wxString ret = fn.GetFullName();
return ret;
}
bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory,
REPORTER * aReporter )
{

View File

@ -50,6 +50,18 @@ enum class HOLE_ATTRIBUTE
HOLE_MECHANICAL // a mechanical pad (provided, not used)
};
// Via Protection features according to IPC-4761.
enum class IPC4761_FEATURES : int
{
FILLED,
CAPPED,
PLUGGED_FRONT,
PLUGGED_BACK,
COVERED_FRONT,
COVERED_BACK,
TENTED_FRONT,
TENTED_BACK
};
// the DRILL_TOOL class handles tools used in the excellon drill file:
class DRILL_TOOL
@ -95,6 +107,14 @@ public:
m_Hole_Bottom_Layer = B_Cu;
m_Hole_Top_Layer = F_Cu;
m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_UNKNOWN;
m_Hole_Filled = false;
m_Hole_Capped = false;
m_Hole_Top_Covered = false;
m_Hole_Bot_Covered = false;
m_Hole_Top_Plugged = false;
m_Hole_Bot_Plugged = false;
m_Hole_Top_Tented = false;
m_Hole_Bot_Tented = false;
}
public:
@ -114,6 +134,15 @@ public:
// section.
HOLE_ATTRIBUTE m_HoleAttribute; // Attribute, used in Excellon drill file and to sort holes
// by type.
bool m_Hole_Filled; // hole should be filled
bool m_Hole_Capped; // hole should be capped
bool m_Hole_Top_Covered; // hole should be covered on top
bool m_Hole_Bot_Covered; // hole should be covered on bottom
bool m_Hole_Top_Plugged; // hole should be plugged on top
bool m_Hole_Bot_Plugged; // hole should be plugged on bottom
bool m_Hole_Top_Tented; // hole should be tented on top
bool m_Hole_Bot_Tented; // hole should be tented on bottom
};
@ -347,6 +376,16 @@ protected:
bool aMerge_PTH_NPTH ) const;
/**
* @param aPair is the layer pair.
* @param aFeature Is the protection feature represented by the file
* @return a filename which identifies the specific protection feature.
* It is the board file name followed by the feature name and the layer(s) associated with it.
*/
virtual const wxString getProtectionFileName( DRILL_LAYER_PAIR aPair,
IPC4761_FEATURES aFeature ) const;
/**
* @param aLayerPair is the layer pair (Drill from rom first layer to second layer)
* @param aHoleType is type of drill file (PTH, NPTH, mixed)

View File

@ -58,7 +58,7 @@ GERBER_WRITER::GERBER_WRITER( BOARD* aPcb )
bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, bool aGenDrill,
bool aGenMap, REPORTER* aReporter )
bool aGenMap, bool aGenTenting, REPORTER* aReporter )
{
bool success = true;
// Note: In Gerber drill files, NPTH and PTH are always separate files
@ -117,7 +117,48 @@ bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, b
aReporter->Report( msg, RPT_SEVERITY_ACTION );
}
}
}
}
if( getHolesCount() > 0 && !doing_npth )
{
for( IPC4761_FEATURES feature :
{ IPC4761_FEATURES::FILLED, IPC4761_FEATURES::CAPPED,
IPC4761_FEATURES::COVERED_BACK, IPC4761_FEATURES::COVERED_FRONT,
IPC4761_FEATURES::PLUGGED_BACK, IPC4761_FEATURES::PLUGGED_FRONT,
IPC4761_FEATURES::TENTED_BACK, IPC4761_FEATURES::TENTED_FRONT } )
{
if( !aGenTenting )
{
if( feature == IPC4761_FEATURES::TENTED_BACK
|| feature == IPC4761_FEATURES::TENTED_FRONT )
{
continue;
}
}
fn = getProtectionFileName( pair, feature );
fn.SetPath( aPlotDirectory );
wxString fullFilename = fn.GetFullPath();
if( createProtectionFile( fullFilename, feature, pair ) < 0 )
{
if( aReporter )
{
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
aReporter->Report( msg, RPT_SEVERITY_ERROR );
success = false;
}
}
else
{
if( aReporter )
{
msg.Printf( _( "Created file '%s'." ), fullFilename );
aReporter->Report( msg, RPT_SEVERITY_ACTION );
}
}
}
}
}
@ -137,6 +178,141 @@ bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, b
static void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart, VECTOR2I& aEnd );
#endif
int GERBER_WRITER::createProtectionFile( const wxString& aFullFilename, IPC4761_FEATURES aFeature,
DRILL_LAYER_PAIR aLayerPair )
{
GERBER_PLOTTER plotter;
// Gerber drill file imply X2 format:
plotter.UseX2format( true );
plotter.UseX2NetAttributes( true );
plotter.DisableApertMacros( false );
// Add the standard X2 header, without FileFunction
AddGerberX2Header( &plotter, m_pcb );
plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS / 10, /* scale */ 1.0,
/* mirror */ false );
// has meaning only for gerber plotter. Must be called only after SetViewport
plotter.SetGerberCoordinatesFormat( 6 );
plotter.SetCreator( wxT( "PCBNEW" ) );
// Add the standard X2 FileFunction for drill files
// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
wxString text = "%TF,FileFunction,Other,";
std::string attrib;
switch( aFeature )
{
case IPC4761_FEATURES::CAPPED:
text << wxT( "Capping" );
attrib = "Capping";
break;
case IPC4761_FEATURES::FILLED:
text << wxT( "Filling" );
attrib = "Filling";
break;
case IPC4761_FEATURES::COVERED_BACK:
text << wxT( "Covering-Back" );
attrib = "Covering";
break;
case IPC4761_FEATURES::COVERED_FRONT:
text << wxT( "Covering-Front" );
attrib = "Covering";
break;
case IPC4761_FEATURES::PLUGGED_BACK:
text << wxT( "Plugging-Back" );
attrib = "Plugging";
break;
case IPC4761_FEATURES::PLUGGED_FRONT:
text << wxT( "Plugging-Front" );
attrib = "Plugging";
break;
case IPC4761_FEATURES::TENTED_BACK:
text << wxT( "Tenting-Back" );
attrib = "Tenting";
break;
case IPC4761_FEATURES::TENTED_FRONT:
text << wxT( "Tenting-Front" );
attrib = "Tenting";
break;
default: return -1;
}
text << wxT( "*%" );
plotter.AddLineToHeader( text );
// Add file polarity (positive)
text = wxT( "%TF.FilePolarity,Positive*%" );
plotter.AddLineToHeader( text );
if( !plotter.OpenFile( aFullFilename ) )
return -1;
plotter.StartPlot( wxT( "1" ) );
int holes_count = 0;
for( auto& hole_descr : m_holeListBuffer )
{
if( !dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
{
continue;
}
const PCB_VIA* via = dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent );
bool cont = false;
int diameter = hole_descr.m_Hole_Diameter;
// clang-format off: suggestion is inconsitent
switch( aFeature )
{
case IPC4761_FEATURES::FILLED:
cont = ! hole_descr.m_Hole_Filled;
break;
case IPC4761_FEATURES::CAPPED:
cont = ! hole_descr.m_Hole_Capped;
break;
case IPC4761_FEATURES::COVERED_BACK:
cont = !hole_descr.m_Hole_Bot_Covered;
diameter = via->GetWidth( via->BottomLayer() );
break;
case IPC4761_FEATURES::COVERED_FRONT:
cont = ! hole_descr.m_Hole_Top_Covered;
diameter = via->GetWidth( via->TopLayer() );
break;
case IPC4761_FEATURES::PLUGGED_BACK:
cont = !hole_descr.m_Hole_Bot_Plugged;
break;
case IPC4761_FEATURES::PLUGGED_FRONT:
cont = ! hole_descr.m_Hole_Top_Plugged;
break;
case IPC4761_FEATURES::TENTED_BACK:
cont = ! hole_descr.m_Hole_Bot_Tented;
diameter = via->GetWidth( via->BottomLayer() );
break;
case IPC4761_FEATURES::TENTED_FRONT:
cont = ! hole_descr.m_Hole_Top_Tented;
diameter = via->GetWidth( via->TopLayer() );
break;
}
// clang-format on: suggestion is inconsitent
if( cont )
continue;
GBR_METADATA gbr_metadata;
gbr_metadata.SetApertureAttrib( attrib );
plotter.FlashPadCircle( hole_descr.m_Hole_Pos, diameter, FILLED, &gbr_metadata );
holes_count++;
}
plotter.EndPlot();
return holes_count;
}
int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
DRILL_LAYER_PAIR aLayerPair )

View File

@ -73,13 +73,13 @@ public:
* @param aPlotDirectory is the output folder.
* @param aGenDrill set to true to generate the EXCELLON drill file.
* @param aGenMap set to true to generate a drill map file.
* @param aGenTenting set to true to generate tenting layer files.
* @param aReporter is a #REPORTER to return activity or any message (can be NULL).
*
* @return True if successful, false if any error occurred
*/
bool CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
bool aGenDrill, bool aGenMap,
REPORTER * aReporter = nullptr );
bool CreateDrillandMapFilesSet( const wxString& aPlotDirectory, bool aGenDrill, bool aGenMap,
bool aGenTenting, REPORTER* aReporter = nullptr );
private:
/**
@ -93,6 +93,16 @@ private:
*/
int createDrillFile( wxString& aFullFilename, bool aIsNpth, DRILL_LAYER_PAIR aLayerPair );
/**
* Create a Gerber X2 file for via protection features.
*
* @param aFullFilename is the full file name
* @param aFeature the protection feature this file represents
* @param aLayerPair is the first and last layer pair for the via drill. Usually is top and bottom.
*/
int createProtectionFile( const wxString& aFullFilename, IPC4761_FEATURES aFeature,
DRILL_LAYER_PAIR aLayerPair );
/**
* @param aPair is the layer pair.
* @param aNPTH set to true to generate the filename of NPTH holes.

View File

@ -213,6 +213,18 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
using namespace kiapi::board::types;
PadStack padstack;
auto unpackOptional = []<typename ProtoEnum>( const ProtoEnum& aProto,
std::optional<bool>& aDest, ProtoEnum aTrueValue,
ProtoEnum aFalseValue )
{
if( aProto == aTrueValue )
aDest = true;
else if( aProto == aFalseValue )
aDest = false;
else
aDest = std::nullopt;
};
if( !aContainer.UnpackTo( &padstack ) )
return false;
@ -223,6 +235,8 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
Drill().size = kiapi::common::UnpackVector2( padstack.drill().diameter() );
Drill().start = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().start_layer() );
Drill().end = FromProtoEnum<PCB_LAYER_ID>( padstack.drill().end_layer() );
unpackOptional( padstack.drill().capped(), Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED );
unpackOptional( padstack.drill().filled(), Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED );
for( const PadStackLayer& layer : padstack.copper_layers() )
{
@ -260,57 +274,29 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer )
SetUnconnectedLayerMode(
FromProtoEnum<UNCONNECTED_LAYER_MODE>( padstack.unconnected_layer_removal() ) );
auto unpackMask =
[]( const SolderMaskMode& aProto, std::optional<bool>& aDest )
{
switch( aProto )
{
case kiapi::board::types::SMM_MASKED:
aDest = true;
break;
unpackOptional( padstack.front_outer_layers().solder_mask_mode(),
FrontOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
case kiapi::board::types::SMM_UNMASKED:
aDest = false;
break;
unpackOptional( padstack.back_outer_layers().solder_mask_mode(),
BackOuterLayers().has_solder_mask, SMM_MASKED, SMM_UNMASKED );
default:
case kiapi::board::types::SMM_FROM_DESIGN_RULES:
aDest = std::nullopt;
break;
}
};
unpackOptional( padstack.front_outer_layers().covering_mode(), FrontOuterLayers().has_covering,
VCM_COVERED, VCM_UNCOVERED );
unpackMask( padstack.front_outer_layers().solder_mask_mode(),
FrontOuterLayers().has_solder_mask );
unpackOptional( padstack.back_outer_layers().covering_mode(), BackOuterLayers().has_covering,
VCM_COVERED, VCM_UNCOVERED );
unpackMask( padstack.back_outer_layers().solder_mask_mode(),
BackOuterLayers().has_solder_mask );
unpackOptional( padstack.front_outer_layers().plugging_mode(), FrontOuterLayers().has_plugging,
VPM_PLUGGED, VPM_UNPLUGGED );
auto unpackPaste =
[]( const SolderPasteMode& aProto, std::optional<bool>& aDest )
{
switch( aProto )
{
case kiapi::board::types::SPM_PASTE:
aDest = true;
break;
unpackOptional( padstack.back_outer_layers().plugging_mode(), BackOuterLayers().has_plugging,
VPM_PLUGGED, VPM_UNPLUGGED );
case kiapi::board::types::SPM_NO_PASTE:
aDest = false;
break;
unpackOptional( padstack.front_outer_layers().solder_paste_mode(),
FrontOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
default:
case kiapi::board::types::SPM_FROM_DESIGN_RULES:
aDest = std::nullopt;
break;
}
};
unpackPaste( padstack.front_outer_layers().solder_paste_mode(),
FrontOuterLayers().has_solder_paste );
unpackPaste( padstack.back_outer_layers().solder_paste_mode(),
BackOuterLayers().has_solder_paste );
unpackOptional( padstack.back_outer_layers().solder_paste_mode(),
BackOuterLayers().has_solder_paste, SPM_PASTE, SPM_NO_PASTE );
if( padstack.front_outer_layers().has_solder_mask_settings()
&& padstack.front_outer_layers().solder_mask_settings().has_solder_mask_margin() )
@ -429,6 +415,16 @@ void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
using namespace kiapi::board::types;
PadStack padstack;
auto packOptional = []<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
ProtoEnum aFalseVal,
ProtoEnum aNullVal ) -> ProtoEnum
{
if( aVal.has_value() )
return *aVal ? aTrueVal : aFalseVal;
return aNullVal;
};
padstack.set_type( ToProtoEnum<MODE, PadStackType>( m_mode ) );
kiapi::board::PackLayerSet( *padstack.mutable_layers(), m_layerSet );
padstack.mutable_angle()->set_value_degrees( m_orientation.AsDegrees() );
@ -436,6 +432,12 @@ void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
DrillProperties* drill = padstack.mutable_drill();
drill->set_start_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( StartLayer() ) );
drill->set_end_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( EndLayer() ) );
drill->set_filled(
packOptional( Drill().is_filled, VDFM_FILLED, VDFM_UNFILLED, VDFM_FROM_DESIGN_RULES ) );
drill->set_capped(
packOptional( Drill().is_capped, VDCM_CAPPED, VDCM_UNCAPPED, VDCM_FROM_DESIGN_RULES ) );
kiapi::common::PackVector2( *drill->mutable_diameter(), Drill().size );
ForEachUniqueLayer( [&]( PCB_LAYER_ID aLayer )
@ -463,34 +465,32 @@ void PADSTACK::Serialize( google::protobuf::Any& aContainer ) const
padstack.set_unconnected_layer_removal(
ToProtoEnum<UNCONNECTED_LAYER_MODE, UnconnectedLayerRemoval>( m_unconnectedLayerMode ) );
auto packOptional =
[]<typename ProtoEnum>( const std::optional<bool>& aVal, ProtoEnum aTrueVal,
ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum
{
if( aVal.has_value() )
return *aVal ? aTrueVal : aFalseVal;
return aNullVal;
};
PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers();
PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers();
frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask,
SMM_MASKED, SMM_UNMASKED,
SMM_FROM_DESIGN_RULES ) );
frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask, SMM_MASKED,
SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask,
SMM_MASKED, SMM_UNMASKED,
SMM_FROM_DESIGN_RULES ) );
backOuter->set_solder_mask_mode( packOptional( BackOuterLayers().has_solder_mask, SMM_MASKED,
SMM_UNMASKED, SMM_FROM_DESIGN_RULES ) );
frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste,
SPM_PASTE, SPM_NO_PASTE,
SPM_FROM_DESIGN_RULES ) );
frontOuter->set_plugging_mode( packOptional( FrontOuterLayers().has_plugging, VPM_PLUGGED,
VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste,
SPM_PASTE, SPM_NO_PASTE,
SPM_FROM_DESIGN_RULES ) );
backOuter->set_plugging_mode( packOptional( BackOuterLayers().has_plugging, VPM_PLUGGED,
VPM_UNPLUGGED, VPM_FROM_DESIGN_RULES ) );
frontOuter->set_covering_mode( packOptional( FrontOuterLayers().has_covering, VCM_COVERED,
VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
backOuter->set_covering_mode( packOptional( BackOuterLayers().has_covering, VCM_COVERED,
VCM_UNCOVERED, VCM_FROM_DESIGN_RULES ) );
frontOuter->set_solder_paste_mode( packOptional( FrontOuterLayers().has_solder_paste, SPM_PASTE,
SPM_NO_PASTE, SPM_FROM_DESIGN_RULES ) );
backOuter->set_solder_paste_mode( packOptional( BackOuterLayers().has_solder_paste, SPM_PASTE,
SPM_NO_PASTE, SPM_FROM_DESIGN_RULES ) );
if( FrontOuterLayers().solder_mask_margin.has_value() )
{
@ -1346,5 +1346,37 @@ std::optional<bool> PADSTACK::IsTented( PCB_LAYER_ID aSide ) const
wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" );
}
std::optional<bool> PADSTACK::IsCovered( PCB_LAYER_ID aSide ) const
{
if( IsFrontLayer( aSide ) )
return m_frontMaskProps.has_covering;
if( IsBackLayer( aSide ) )
return m_backMaskProps.has_covering;
wxCHECK_MSG( false, std::nullopt, "IsCovered expects a front or back layer" );
}
std::optional<bool> PADSTACK::IsPlugged( PCB_LAYER_ID aSide ) const
{
if( IsFrontLayer( aSide ) )
return m_frontMaskProps.has_plugging;
if( IsBackLayer( aSide ) )
return m_backMaskProps.has_plugging;
wxCHECK_MSG( false, std::nullopt, "IsPlugged expects a front or back layer" );
}
std::optional<bool> PADSTACK::IsCapped() const
{
return m_drill.is_capped;
}
std::optional<bool> PADSTACK::IsFilled() const
{
return m_drill.is_filled;
}
IMPLEMENT_ENUM_TO_WXANY( PADSTACK::UNCONNECTED_LAYER_MODE )

View File

@ -228,8 +228,11 @@ public:
std::optional<int> solder_mask_margin;
std::optional<int> solder_paste_margin;
std::optional<double> solder_paste_margin_ratio;
std::optional<bool> has_solder_mask; ///< True if this outer layer has mask (is not tented)
std::optional<bool> has_solder_paste; ///< True if this outer layer has solder paste
std::optional<bool> has_covering; ///< True if the pad on this side should have covering
std::optional<bool> has_plugging; ///< True if the drill hole should be plugged on this side
bool operator==( const MASK_LAYER_PROPS& aOther ) const;
};
@ -242,6 +245,9 @@ public:
PCB_LAYER_ID start;
PCB_LAYER_ID end;
std::optional<bool> is_filled; ///< True if the drill hole should be filled completely
std::optional<bool> is_capped; ///< True if the drill hole should be capped
bool operator==( const DRILL_PROPS& aOther ) const;
};
@ -323,6 +329,14 @@ public:
*/
std::optional<bool> IsTented( PCB_LAYER_ID aSide ) const;
std::optional<bool> IsCovered( PCB_LAYER_ID aSide ) const;
std::optional<bool> IsPlugged( PCB_LAYER_ID aSide ) const;
std::optional<bool> IsCapped() const;
std::optional<bool> IsFilled() const;
CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const { return m_customShapeInZoneMode; }
void SetCustomShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE aM ) { m_customShapeInZoneMode = aM; }

View File

@ -55,6 +55,15 @@ enum class cadPinType
SURFACE
};
enum class auxLayerType
{
COVERING,
PLUGGING,
TENTING,
FILLING,
CAPPING,
};
enum class certificationCategoryType
{
ASSEMBLYDRAWING,

View File

@ -1364,7 +1364,8 @@ wxXmlNode* PCB_IO_IPC2581::generateEcadSection()
wxXmlNode* cadDataNode = appendNode( ecadNode, "CadData" );
generateCadLayers( cadDataNode );
generateDrillLayers( cadDataNode);
generateDrillLayers( cadDataNode );
generateAuxilliaryLayers( cadDataNode );
generateStackup( cadDataNode );
generateStepSection( cadDataNode );
@ -1756,6 +1757,136 @@ void PCB_IO_IPC2581::generateDrillLayers( wxXmlNode* aCadLayerNode )
}
void PCB_IO_IPC2581::generateAuxilliaryLayers( wxXmlNode* aCadLayerNode )
{
for( BOARD_ITEM* item : m_board->Tracks() )
{
if( item->Type() != PCB_VIA_T )
continue;
PCB_VIA* via = static_cast<PCB_VIA*>( item );
std::vector<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>> new_layers;
if( via->Padstack().IsFilled().value_or( false ) )
{
new_layers.emplace_back( auxLayerType::FILLING, via->TopLayer(), via->BottomLayer() );
}
if( via->Padstack().IsCapped().value_or( false ) )
{
new_layers.emplace_back( auxLayerType::CAPPING, via->TopLayer(), via->BottomLayer() );
}
for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } )
{
if( via->Padstack().IsPlugged( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::PLUGGING, layer, UNDEFINED_LAYER );
}
if( via->Padstack().IsCovered( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::COVERING, layer, UNDEFINED_LAYER );
}
if( via->Padstack().IsTented( layer ).value_or( false ) )
{
new_layers.emplace_back( auxLayerType::TENTING, layer, UNDEFINED_LAYER );
}
}
for( auto& tuple : new_layers )
{
m_auxilliary_Layers[tuple].push_back( via );
}
}
for( const auto& [layers, vec] : m_auxilliary_Layers )
{
bool add_node = true;
wxString name;
wxString layerFunction;
// clang-format off: suggestion is inconsitent
switch( std::get<0>(layers) )
{
case auxLayerType::COVERING:
name = "COVERING";
layerFunction = "COATINGNONCOND";
break;
case auxLayerType::PLUGGING:
name = "PLUGGING";
layerFunction = "HOLEFILL";
break;
case auxLayerType::TENTING:
name = "TENTING";
layerFunction = "COATINGNONCOND";
break;
case auxLayerType::FILLING:
name = "FILLING";
layerFunction = "HOLEFILL";
break;
case auxLayerType::CAPPING:
name = "CAPPING";
layerFunction = "COATINGCOND";
break;
default:
add_node = false;
break;
}
// clang-format on: suggestion is inconsitent
if( add_node && !vec.empty() )
{
wxXmlNode* node = appendNode( aCadLayerNode, "LAYER" );
addAttribute( node, "layerFunction", layerFunction );
addAttribute( node, "polarity", "POSITIVE" );
if( std::get<2>( layers ) == UNDEFINED_LAYER )
{
addAttribute( node, "name", genLayerString( std::get<1>( layers ), name ) );
addAttribute( node, "side",
IsFrontLayer( std::get<1>( layers ) ) ? "TOP" : "BOTTOM" );
}
else
{
addAttribute(
node, "name",
genLayersString( std::get<1>( layers ), std::get<2>( layers ), name ) );
const bool first_external =
std::get<1>( layers ) == F_Cu || std::get<1>( layers ) == B_Cu;
const bool second_external =
std::get<2>( layers ) == F_Cu || std::get<2>( layers ) == B_Cu;
if( first_external )
{
if( second_external )
addAttribute( node, "side", "ALL" );
else
addAttribute( node, "side", "FRONT" );
}
else
{
if( second_external )
addAttribute( node, "side", "BACK" );
else
addAttribute( node, "side", "INTERNAL" );
}
wxXmlNode* spanNode = appendNode( node, "SPAN" );
addAttribute( spanNode, "fromLayer",
genLayerString( std::get<1>( layers ), "LAYER" ) );
addAttribute( spanNode, "toLayer",
genLayerString( std::get<2>( layers ), "LAYER" ) );
}
}
}
}
void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
{
wxXmlNode* stepNode = appendNode( aCadNode, "Step" );
@ -1780,6 +1911,7 @@ void PCB_IO_IPC2581::generateStepSection( wxXmlNode* aCadNode )
generateLayerFeatures( stepNode );
generateLayerSetDrill( stepNode );
generateLayerSetAuxilliary( stepNode );
}
@ -1930,21 +2062,51 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
LSEQ layer_seq = aVia->GetLayerSet().Seq();
auto addPadShape{ [&]( PCB_LAYER_ID layer, const PCB_VIA* aVia, const wxString& name,
bool drill ) -> void
{
PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
if( drill )
shape.SetEnd( { KiROUND( aVia->GetDrillValue() / 2.0 ), 0 } );
else
shape.SetEnd( { KiROUND( aVia->GetWidth( layer ) / 2.0 ), 0 } );
wxXmlNode* padStackPadDefNode =
appendNode( padStackDefNode, "PadstackPadDef" );
addAttribute( padStackPadDefNode, "layerRef", name );
addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
addLocationNode( padStackPadDefNode, 0.0, 0.0 );
addShape( padStackPadDefNode, shape );
} };
for( PCB_LAYER_ID layer : layer_seq )
{
if( !aVia->FlashLayer( layer ) || !m_board->IsLayerEnabled( layer ) )
continue;
PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
addPadShape( layer, aVia, m_layer_name_map[layer], false );
}
shape.SetEnd( { KiROUND( aVia->GetWidth( layer ) / 2.0 ), 0 } );
if( aVia->Padstack().IsFilled().value_or( false ) )
addPadShape( UNDEFINED_LAYER, aVia,
genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "FILLING" ), true );
wxXmlNode* padStackPadDefNode = appendNode( padStackDefNode, "PadstackPadDef" );
addAttribute( padStackPadDefNode, "layerRef", m_layer_name_map[layer] );
addAttribute( padStackPadDefNode, "padUse", "REGULAR" );
if( aVia->Padstack().IsCapped().value_or( false ) )
addPadShape( UNDEFINED_LAYER, aVia,
genLayersString( aVia->TopLayer(), aVia->BottomLayer(), "CAPPING" ), true );
addLocationNode( padStackPadDefNode, 0.0, 0.0 );
addShape( padStackPadDefNode, shape );
for( PCB_LAYER_ID layer : { aVia->TopLayer(), aVia->BottomLayer() } )
{
if( aVia->Padstack().IsPlugged( layer ).value_or( false ) )
addPadShape( layer, aVia, genLayerString( layer, "PLUGGING" ), true );
if( aVia->Padstack().IsCovered( layer ).value_or( false ) )
addPadShape( layer, aVia, genLayerString( layer, "COVERING" ), false );
if( aVia->Padstack().IsTented( layer ).value_or( false ) )
addPadShape( layer, aVia, genLayerString( layer, "TENTING" ), false );
}
}
@ -3002,6 +3164,78 @@ void PCB_IO_IPC2581::generateLayerSetNet( wxXmlNode* aLayerNode, PCB_LAYER_ID aL
}
}
void PCB_IO_IPC2581::generateLayerSetAuxilliary( wxXmlNode* aStepNode )
{
int hole_count = 1;
for( const auto& [layers, vec] : m_auxilliary_Layers )
{
hole_count = 1;
bool add_node = true;
wxString name;
bool hole = false;
// clang-format off: suggestion is inconsitent
switch( std::get<0>(layers) )
{
case auxLayerType::COVERING:
name = "COVERING";
break;
case auxLayerType::PLUGGING:
name = "PLUGGING";
hole = true;
break;
case auxLayerType::TENTING:
name = "TENTING";
break;
case auxLayerType::FILLING:
name = "FILLING";
hole = true;
break;
case auxLayerType::CAPPING:
name = "CAPPING";
hole = true;
break;
default:
add_node = false;
break;
}
// clang-format on: suggestion is inconsitent
if( !add_node )
continue;
wxXmlNode* layerNode = appendNode( aStepNode, "LayerFeature" );
if( std::get<2>( layers ) == UNDEFINED_LAYER )
layerNode->AddAttribute( "layerRef", genLayerString( std::get<1>( layers ), name ) );
else
layerNode->AddAttribute( "layerRef", genLayersString( std::get<1>( layers ),
std::get<2>( layers ), name ) );
for( BOARD_ITEM* item : vec )
{
if( item->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
PCB_SHAPE shape( nullptr, SHAPE_T::CIRCLE );
if( hole )
shape.SetEnd( { KiROUND( via->GetDrillValue() / 2.0 ), 0 } );
else
shape.SetEnd( { KiROUND( via->GetWidth( std::get<1>( layers ) ) / 2.0 ), 0 } );
wxXmlNode* padNode = appendNode( layerNode, "Pad" );
addPadStack( padNode, via );
addLocationNode( padNode, 0.0, 0.0 );
addShape( padNode, shape );
}
}
}
}
wxXmlNode* PCB_IO_IPC2581::generateAvlSection()
{

View File

@ -24,6 +24,8 @@
#include <pcb_io/pcb_io_mgr.h>
#include <pcb_io/common/plugin_common_layer_mapping.h>
#include "ipc2581_types.h"
#include <eda_shape.h>
#include <layer_ids.h> // PCB_LAYER_ID
#include <font/font.h>
@ -172,6 +174,8 @@ private:
void generateDrillLayers( wxXmlNode* aCadLayerNode );
void generateAuxilliaryLayers( wxXmlNode* aCadLayerNode );
void generateStepSection( wxXmlNode* aCadNode );
void generateProfile( wxXmlNode* aStepNode );
@ -186,6 +190,8 @@ private:
void generateLayerSetNet( wxXmlNode* aLayerNode, PCB_LAYER_ID aLayer, std::vector<BOARD_ITEM*>& aItems );
void generateLayerSetAuxilliary( wxXmlNode* aStepNode );
wxXmlNode* generateContentStackup( wxXmlNode* aContentNode );
void generateComponents( wxXmlNode* aStepNode );
@ -326,6 +332,9 @@ private:
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<PAD*>>
m_slot_holes; //<! Storage vector of slotted holes that need to be output as cutouts
std::map<std::tuple<auxLayerType, PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_auxilliary_Layers;
PROGRESS_REPORTER* m_progress_reporter;
std::set<wxUniChar> m_acceptable_chars; //<! IPC2581B and C have differing sets of allowed characters in names

View File

@ -587,16 +587,24 @@ void PCB_IO_KICAD_SEXPR::formatSetup( const BOARD* aBoard ) const
KICAD_FORMAT::FormatBool( m_out, "allow_soldermask_bridges_in_footprints",
dsnSettings.m_AllowSoldermaskBridgesInFPs );
if( dsnSettings.m_TentViasFront || dsnSettings.m_TentViasBack )
{
m_out->Print( "(tenting %s %s)",
dsnSettings.m_TentViasFront ? "front" : "",
dsnSettings.m_TentViasBack ? "back" : "" );
}
else
{
m_out->Print( "(tenting none)" );
}
m_out->Print( 0, " (tenting " );
KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_TentViasFront );
KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_TentViasBack );
m_out->Print( 0, ")" );
m_out->Print( 0, " (covering " );
KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_CoverViasFront );
KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_CoverViasBack );
m_out->Print( 0, ")" );
m_out->Print( 0, " (plugging " );
KICAD_FORMAT::FormatBool( m_out, "front", dsnSettings.m_PlugViasFront );
KICAD_FORMAT::FormatBool( m_out, "back", dsnSettings.m_PlugViasBack );
m_out->Print( 0, ")" );
KICAD_FORMAT::FormatBool( m_out, "capping", dsnSettings.m_CapVias );
KICAD_FORMAT::FormatBool( m_out, "filling", dsnSettings.m_FillVias );
VECTOR2I origin = dsnSettings.GetAuxOrigin();
@ -1836,7 +1844,12 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
if( !isDefaultTeardropParameters( aPad->GetTeardropParams() ) )
formatTeardropParameters( aPad->GetTeardropParams() );
formatTenting( aPad->Padstack() );
m_out->Print( 0, " (tenting " );
KICAD_FORMAT::FormatOptBool( m_out, "front",
aPad->Padstack().FrontOuterLayers().has_solder_mask );
KICAD_FORMAT::FormatOptBool( m_out, "back",
aPad->Padstack().BackOuterLayers().has_solder_mask );
m_out->Print( 0, ")" );
KICAD_FORMAT::FormatUuid( m_out, aPad->m_Uuid );
@ -1954,27 +1967,6 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
}
void PCB_IO_KICAD_SEXPR::formatTenting( const PADSTACK& aPadstack ) const
{
std::optional<bool> front = aPadstack.FrontOuterLayers().has_solder_mask;
std::optional<bool> back = aPadstack.BackOuterLayers().has_solder_mask;
if( front.has_value() || back.has_value() )
{
if( front.value_or( false ) || back.value_or( false ) )
{
m_out->Print( "(tenting %s %s)",
front.value_or( false ) ? "front" : "",
back.value_or( false ) ? "back" : "" );
}
else
{
m_out->Print( "(tenting none)" );
}
}
}
void PCB_IO_KICAD_SEXPR::format( const PCB_TEXT* aText ) const
{
FOOTPRINT* parentFP = aText->GetParentFootprint();
@ -2379,7 +2371,24 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
const PADSTACK& padstack = via->Padstack();
formatTenting( padstack );
m_out->Print( 0, " (tenting " );
KICAD_FORMAT::FormatOptBool( m_out, "front", padstack.FrontOuterLayers().has_solder_mask );
KICAD_FORMAT::FormatOptBool( m_out, "back", padstack.BackOuterLayers().has_solder_mask );
m_out->Print( 0, ")" );
KICAD_FORMAT::FormatOptBool( m_out, "capping", padstack.Drill().is_capped );
m_out->Print( 0, " (covering " );
KICAD_FORMAT::FormatOptBool( m_out, "front", padstack.FrontOuterLayers().has_covering );
KICAD_FORMAT::FormatOptBool( m_out, "back", padstack.BackOuterLayers().has_covering );
m_out->Print( 0, ")" );
m_out->Print( 0, " (plugging " );
KICAD_FORMAT::FormatOptBool( m_out, "front", padstack.FrontOuterLayers().has_plugging );
KICAD_FORMAT::FormatOptBool( m_out, "back", padstack.BackOuterLayers().has_plugging );
m_out->Print( 0, ")" );
KICAD_FORMAT::FormatOptBool( m_out, "filling", padstack.Drill().is_filled );
if( padstack.Mode() != PADSTACK::MODE::NORMAL )
{

View File

@ -31,6 +31,7 @@
#include <richio.h>
#include <string>
#include <optional>
#include <layer_ids.h>
#include <lset.h>
#include <boost/ptr_container/ptr_map.hpp>
@ -175,7 +176,8 @@ class PCB_IO_KICAD_SEXPR; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20241229 // Expand User layers to arbitrary count
//----------------- Start of 10.0 development -----------------
//#define SEXPR_BOARD_FILE_VERSION 20250210 // Knockout for textboxes
#define SEXPR_BOARD_FILE_VERSION 20250222 // Hatching for PCB shapes
//#define SEXPR_BOARD_FILE_VERSION 20250222 // Hatching for PCB shapes
#define SEXPR_BOARD_FILE_VERSION 20250228 // ipc-4761 via protection features
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
@ -460,8 +462,6 @@ private:
void formatLayers( LSET aLayerMask, bool aEnumerateLayers ) const;
void formatTenting( const PADSTACK& aPadstack ) const;
friend class FP_CACHE;
protected:

View File

@ -235,6 +235,23 @@ bool PCB_IO_KICAD_SEXPR_PARSER::parseBool()
}
std::optional<bool> PCB_IO_KICAD_SEXPR_PARSER::parseOptBool()
{
T token = NextTok();
if( token == T_yes )
return true;
else if( token == T_no )
return false;
else if( token == T_none )
return std::nullopt;
else
Expecting( "yes, no or none" );
return false;
}
/*
* e.g. "hide", "hide)", "(hide yes)"
*/
@ -2423,17 +2440,39 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseSetup()
case T_tenting:
{
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_front )
bds.m_TentViasFront = true;
else if( token == T_back )
bds.m_TentViasBack = true;
else if( token == T_none )
bds.m_TentViasFront = bds.m_TentViasBack = false;
else
Expecting( "front, back, or none" );
}
auto [front, back] = parseFrontBackOptBool( true );
bds.m_TentViasFront = front.value_or( false );
bds.m_TentViasBack = back.value_or( false );
break;
}
case T_covering:
{
auto [front, back] = parseFrontBackOptBool();
bds.m_CoverViasFront = front.value_or( false );
bds.m_CoverViasBack = back.value_or( false );
break;
}
case T_plugging:
{
auto [front, back] = parseFrontBackOptBool();
bds.m_PlugViasFront = front.value_or( false );
bds.m_PlugViasBack = back.value_or( false );
break;
}
case T_capping:
{
bds.m_CapVias = parseBool();
NeedRIGHT();
break;
}
case T_filling:
{
bds.m_FillVias = parseBool();
NeedRIGHT();
break;
}
@ -5437,8 +5476,12 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
}
case T_tenting:
parseTenting( pad->Padstack() );
{
auto [front, back] = parseFrontBackOptBool( true );
pad->Padstack().FrontOuterLayers().has_solder_mask = front;
pad->Padstack().BackOuterLayers().has_solder_mask = back;
break;
}
case T_zone_layer_connections:
{
@ -5836,8 +5879,12 @@ void PCB_IO_KICAD_SEXPR_PARSER::parsePadstack( PAD* aPad )
break;
case T_tenting:
parseTenting( padstack );
{
auto [front, back] = parseFrontBackOptBool( true );
padstack.FrontOuterLayers().has_solder_mask = front;
padstack.BackOuterLayers().has_solder_mask = back;
break;
}
// TODO: refactor parsePAD_options to work on padstacks too
case T_options:
@ -6488,8 +6535,38 @@ PCB_VIA* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_VIA()
break;
case T_tenting:
parseTenting( via->Padstack() );
{
auto [front, back] = parseFrontBackOptBool( true );
via->Padstack().FrontOuterLayers().has_solder_mask = front;
via->Padstack().BackOuterLayers().has_solder_mask = back;
break;
}
case T_covering:
{
auto [front, back] = parseFrontBackOptBool();
via->Padstack().FrontOuterLayers().has_covering = front;
via->Padstack().BackOuterLayers().has_covering = back;
break;
}
case T_plugging:
{
auto [front, back] = parseFrontBackOptBool();
via->Padstack().FrontOuterLayers().has_plugging = front;
via->Padstack().BackOuterLayers().has_plugging = back;
break;
}
case T_filling:
{
via->Padstack().Drill().is_filled = parseOptBool();
NeedRIGHT();
break;
}
case T_capping:
{
via->Padstack().Drill().is_capped = parseOptBool();
NeedRIGHT();
break;
}
case T_tstamp:
case T_uuid:
@ -6522,24 +6599,66 @@ PCB_VIA* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_VIA()
}
void PCB_IO_KICAD_SEXPR_PARSER::parseTenting( PADSTACK& aPadstack )
std::pair<std::optional<bool>, std::optional<bool>>
PCB_IO_KICAD_SEXPR_PARSER::parseFrontBackOptBool( bool aLegacy )
{
bool front = false;
bool back = false;
T token = NextTok();
// If there is a tenting token, it means this individual pad/via has a tenting override
for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
std::optional<bool> front{ std::nullopt };
std::optional<bool> back{ std::nullopt };
if( token != T_LEFT && aLegacy )
{
if( token == T_front )
front = true;
else if( token == T_back )
back = true;
else if( token != T_none )
Expecting( "front, back, or none" );
// legacy format for tenting.
if( token == T_front || token == T_back || token == T_none )
{
while( token != T_RIGHT )
{
if( token == T_front )
{
front = true;
}
else if( token == T_back )
{
back = true;
}
else if( token == T_none )
{
front.reset();
back.reset();
}
else
{
Expecting( "front, back or none" );
}
token = NextTok();
}
return { front, back };
}
}
aPadstack.FrontOuterLayers().has_solder_mask = front;
aPadstack.BackOuterLayers().has_solder_mask = back;
while( token != T_RIGHT )
{
if( token != T_LEFT )
Expecting( "(" );
token = NextTok();
if( token == T_front )
front = parseOptBool();
else if( token == T_back )
back = parseOptBool();
else
Expecting( "front or back" );
NeedRIGHT();
token = NextTok();
}
return { front, back };
}

View File

@ -330,8 +330,6 @@ private:
*/
void parseRenderCache( EDA_TEXT* text );
void parseTenting( PADSTACK& aPadstack );
FP_3DMODEL* parse3DModel();
/**
@ -370,6 +368,8 @@ private:
bool parseBool();
std::optional<bool> parseOptBool();
/**
* Parses a boolean flag inside a list that existed before boolean normalization.
*
@ -382,6 +382,9 @@ private:
*/
bool parseMaybeAbsentBool( bool aDefaultValue );
std::pair<std::optional<bool>, std::optional<bool>>
parseFrontBackOptBool( bool aLegacy = false );
/*
* @return if m_appendToExisting, returns new KIID(), otherwise returns CurStr() as KIID.
*/

View File

@ -183,6 +183,8 @@ void ODB_MATRIX_ENTITY::InitMatrixLayerData()
AddDrillMatrixLayer();
AddAuxilliaryMatrixLayer();
AddCOMPMatrixLayer( B_Cu );
}
@ -384,6 +386,121 @@ void ODB_MATRIX_ENTITY::AddCOMPMatrixLayer( PCB_LAYER_ID aCompSide )
}
}
void ODB_MATRIX_ENTITY::AddAuxilliaryMatrixLayer()
{
auto& auxilliary_layers = m_plugin->GetAuxilliaryLayerItemsMap();
for( BOARD_ITEM* item : m_board->Tracks() )
{
if( item->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
if( via->Padstack().IsFilled().value_or( false ) )
{
auxilliary_layers[std::make_tuple( ODB_AUX_LAYER_TYPE::FILLING, via->TopLayer(),
via->BottomLayer() )]
.push_back( via );
}
if( via->Padstack().IsCapped().value_or( false ) )
{
auxilliary_layers[std::make_tuple( ODB_AUX_LAYER_TYPE::CAPPING, via->TopLayer(),
via->BottomLayer() )]
.push_back( via );
}
for( PCB_LAYER_ID layer : { via->TopLayer(), via->BottomLayer() } )
{
if( via->Padstack().IsPlugged( layer ).value_or( false ) )
{
auxilliary_layers[std::make_tuple( ODB_AUX_LAYER_TYPE::PLUGGING, layer,
PCB_LAYER_ID::UNDEFINED_LAYER )]
.push_back( via );
}
if( via->Padstack().IsCovered( layer ).value_or( false ) )
{
auxilliary_layers[std::make_tuple( ODB_AUX_LAYER_TYPE::COVERING, layer,
PCB_LAYER_ID::UNDEFINED_LAYER )]
.push_back( via );
}
if( via->Padstack().IsTented( layer ).value_or( false ) )
{
auxilliary_layers[std::make_tuple( ODB_AUX_LAYER_TYPE::TENTING, layer,
PCB_LAYER_ID::UNDEFINED_LAYER )]
.push_back( via );
}
}
}
}
auto InitAuxMatrix =
[&]( std::tuple<ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID> aLayerPair )
{
wxString featureName = "";
switch( std::get<0>( aLayerPair ) )
{
case ODB_AUX_LAYER_TYPE::TENTING: featureName = "tenting"; break;
case ODB_AUX_LAYER_TYPE::COVERING: featureName = "covering"; break;
case ODB_AUX_LAYER_TYPE::PLUGGING: featureName = "plugging"; break;
case ODB_AUX_LAYER_TYPE::FILLING: featureName = "filling"; break;
case ODB_AUX_LAYER_TYPE::CAPPING: featureName = "capping"; break;
default: return;
}
wxString dLayerName;
if( std::get<2>( aLayerPair ) != PCB_LAYER_ID::UNDEFINED_LAYER )
{
dLayerName = wxString::Format( "%s_%s-%s", featureName,
m_board->GetLayerName( std::get<1>( aLayerPair ) ),
m_board->GetLayerName( std::get<2>( aLayerPair ) ) );
}
else
{
if( IsFrontLayer( std::get<1>( aLayerPair ) ) )
dLayerName = wxString::Format( "%s_front", featureName );
else if( IsBackLayer( std::get<1>( aLayerPair ) ) )
dLayerName = wxString::Format( "%s_back", featureName );
else
return;
}
MATRIX_LAYER matrix( m_row++, dLayerName );
matrix.m_type = ODB_TYPE::DOCUMENT;
matrix.m_context = ODB_CONTEXT::BOARD;
matrix.m_polarity = ODB_POLARITY::POSITIVE;
if( std::get<2>( aLayerPair ) != PCB_LAYER_ID::UNDEFINED_LAYER )
{
matrix.m_span.emplace( std::make_pair(
ODB::GenLegalEntityName( m_board->GetLayerName( std::get<1>( aLayerPair ) ) ),
ODB::GenLegalEntityName(
m_board->GetLayerName( std::get<2>( aLayerPair ) ) ) ) );
}
m_matrixLayers.push_back( matrix );
if( std::get<2>( aLayerPair ) != PCB_LAYER_ID::UNDEFINED_LAYER )
{
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
}
else
{
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( std::get<1>( aLayerPair ), matrix.m_layerName ) );
}
};
for( const auto& [layer_pair, vec] : auxilliary_layers )
{
InitAuxMatrix( layer_pair );
}
}
void ODB_MATRIX_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
@ -457,6 +574,15 @@ void ODB_LAYER_ENTITY::InitEntityData()
return;
}
if( m_matrixLayerName.Contains( "filling" ) || m_matrixLayerName.Contains( "capping" )
|| m_matrixLayerName.Contains( "covering" ) || m_matrixLayerName.Contains( "plugging" )
|| m_matrixLayerName.Contains( "tenting" ) )
{
InitAuxilliaryData();
InitFeatureData();
return;
}
if( m_layerID != PCB_LAYER_ID::UNDEFINED_LAYER )
{
InitFeatureData();
@ -617,6 +743,64 @@ void ODB_LAYER_ENTITY::InitDrillData()
}
}
void ODB_LAYER_ENTITY::InitAuxilliaryData()
{
auto& auxilliary_layers = m_plugin->GetAuxilliaryLayerItemsMap();
if( !m_layerItems.empty() )
{
m_layerItems.clear();
}
for( const auto& [layer_pair, vec] : auxilliary_layers )
{
wxString featureName = "";
switch( std::get<0>( layer_pair ) )
{
case ODB_AUX_LAYER_TYPE::TENTING: featureName = "tenting"; break;
case ODB_AUX_LAYER_TYPE::COVERING: featureName = "covering"; break;
case ODB_AUX_LAYER_TYPE::PLUGGING: featureName = "plugging"; break;
case ODB_AUX_LAYER_TYPE::FILLING: featureName = "filling"; break;
case ODB_AUX_LAYER_TYPE::CAPPING: featureName = "capping"; break;
default: return;
}
wxString dLayerName;
bool drill_value = false;
if( std::get<2>( layer_pair ) != PCB_LAYER_ID::UNDEFINED_LAYER )
{
drill_value = true;
dLayerName = wxString::Format( "%s_%s-%s", featureName,
m_board->GetLayerName( std::get<1>( layer_pair ) ),
m_board->GetLayerName( std::get<2>( layer_pair ) ) );
}
else
{
if( IsFrontLayer( std::get<1>( layer_pair ) ) )
dLayerName = wxString::Format( "%s_front", featureName );
else if( IsBackLayer( std::get<1>( layer_pair ) ) )
dLayerName = wxString::Format( "%s_back", featureName );
else
return;
}
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
{
for( BOARD_ITEM* item : vec )
{
if( item->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
m_layerItems[via->GetNetCode()].push_back( item );
}
}
break;
}
}
}
void ODB_STEP_ENTITY::InitEntityData()
{

View File

@ -102,6 +102,7 @@ public:
void AddStep( const wxString& aStepName );
void AddMatrixLayerField( MATRIX_LAYER& aMLayer, PCB_LAYER_ID aLayer );
void AddDrillMatrixLayer();
void AddAuxilliaryMatrixLayer();
void AddCOMPMatrixLayer( PCB_LAYER_ID aCompSide );
private:
@ -185,8 +186,10 @@ public:
inline virtual std::string GetEntityName() override { return "layers"; }
virtual void InitEntityData() override;
void InitFeatureData();
void InitDrillData();
void InitAuxilliaryData();
ODB_COMPONENT& InitComponentData( const FOOTPRINT* aFp, const EDA_DATA::PACKAGE& aPkg );
void InitDrillData();
void AddLayerFeatures();

View File

@ -406,7 +406,37 @@ void FEATURES_MANAGER::InitFeatureList( PCB_LAYER_ID aLayer, std::vector<BOARD_I
// add via
PCB_VIA* via = static_cast<PCB_VIA*>( track );
bool hole = false;
if( aLayer != PCB_LAYER_ID::UNDEFINED_LAYER )
{
hole = m_layerName.Contains( "plugging" );
}
else
{
hole = m_layerName.Contains( "drill" ) || m_layerName.Contains( "filling" )
|| m_layerName.Contains( "capping" );
}
if( hole )
{
AddViaDrillHole( via, aLayer );
subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::HOLE, m_layerName,
m_featuresList.size() - 1 );
// TODO: confirm TOOLING_HOLE
// AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::PAD_USAGE::TOOLING_HOLE );
if( !m_featuresList.empty() )
{
AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::DRILL::VIA );
AddSystemAttribute(
*m_featuresList.back(),
ODB_ATTR::GEOMETRY{ "VIA_RoundD"
+ std::to_string( via->GetWidth( aLayer ) ) } );
}
}
else
{
// to draw via copper shape on copper layer
AddVia( via, aLayer );
@ -422,29 +452,6 @@ void FEATURES_MANAGER::InitFeatureList( PCB_LAYER_ID aLayer, std::vector<BOARD_I
+ std::to_string( via->GetWidth( aLayer ) ) } );
}
}
else
{
// to draw via drill hole on drill layer
if( m_layerName.Contains( "drill" ) )
{
AddViaDrillHole( via, aLayer );
subnet->AddFeatureID( EDA_DATA::FEATURE_ID::TYPE::HOLE, m_layerName,
m_featuresList.size() - 1 );
// TODO: confirm TOOLING_HOLE
// AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::PAD_USAGE::TOOLING_HOLE );
if( !m_featuresList.empty() )
{
AddSystemAttribute( *m_featuresList.back(), ODB_ATTR::DRILL::VIA );
AddSystemAttribute(
*m_featuresList.back(),
ODB_ATTR::GEOMETRY{ "VIA_RoundD"
+ std::to_string( via->GetWidth( aLayer ) ) } );
}
}
}
}
};

View File

@ -77,6 +77,8 @@ public:
void AddViaDrillHole( const PCB_VIA* aVia, PCB_LAYER_ID aLayer );
void AddViaProtection( const PCB_VIA* aVia, bool drill, PCB_LAYER_ID aLayer );
bool AddContour( const SHAPE_POLY_SET& aPolySet, int aOutline = 0,
FILL_T aFillType = FILL_T::FILLED_SHAPE );

View File

@ -69,6 +69,15 @@ enum class ODB_FID_TYPE
HOLE
};
enum class ODB_AUX_LAYER_TYPE
{
COVERING,
PLUGGING,
TENTING,
FILLING,
CAPPING,
};
namespace ODB
{

View File

@ -105,6 +105,13 @@ public:
return m_drill_layers;
}
inline std::map<std::tuple<ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID>,
std::vector<BOARD_ITEM*>>&
GetAuxilliaryLayerItemsMap()
{
return m_auxilliary_layers;
}
inline std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>&
GetSlotHolesMap()
{
@ -164,6 +171,9 @@ private:
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_drill_layers; //<! Drill sets are output as layers (to/from pairs)
std::map<std::tuple<ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_auxilliary_layers; //<! Auxilliary layers, from/to pairs or simple (depending on type)
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_slot_holes; //<! Storage vector of slotted holes that need to be output as cutouts

View File

@ -855,7 +855,7 @@ std::shared_ptr<SHAPE_SEGMENT> PCB_VIA::GetEffectiveHoleShape() const
return std::make_shared<SHAPE_SEGMENT>( SEG( m_Start, m_Start ), Padstack().Drill().size.x );
}
// clang-format off: the suggestion is slightly less readable
void PCB_VIA::SetFrontTentingMode( TENTING_MODE aMode )
{
switch( aMode )
@ -872,7 +872,7 @@ TENTING_MODE PCB_VIA::GetFrontTentingMode() const
if( m_padStack.FrontOuterLayers().has_solder_mask.has_value() )
{
return *m_padStack.FrontOuterLayers().has_solder_mask ?
TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
}
return TENTING_MODE::FROM_RULES;
@ -895,13 +895,152 @@ TENTING_MODE PCB_VIA::GetBackTentingMode() const
if( m_padStack.BackOuterLayers().has_solder_mask.has_value() )
{
return *m_padStack.BackOuterLayers().has_solder_mask ?
TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
TENTING_MODE::TENTED : TENTING_MODE::NOT_TENTED;
}
return TENTING_MODE::FROM_RULES;
}
void PCB_VIA::SetFrontCoveringMode( COVERING_MODE aMode )
{
switch( aMode )
{
case COVERING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_covering.reset(); break;
case COVERING_MODE::COVERED: m_padStack.FrontOuterLayers().has_covering = true; break;
case COVERING_MODE::NOT_COVERED: m_padStack.FrontOuterLayers().has_covering = false; break;
}
}
COVERING_MODE PCB_VIA::GetFrontCoveringMode() const
{
if( m_padStack.FrontOuterLayers().has_covering.has_value() )
{
return *m_padStack.FrontOuterLayers().has_covering ?
COVERING_MODE::COVERED : COVERING_MODE::NOT_COVERED;
}
return COVERING_MODE::FROM_RULES;
}
void PCB_VIA::SetBackCoveringMode( COVERING_MODE aMode )
{
switch( aMode )
{
case COVERING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_covering.reset(); break;
case COVERING_MODE::COVERED: m_padStack.BackOuterLayers().has_covering = true; break;
case COVERING_MODE::NOT_COVERED: m_padStack.BackOuterLayers().has_covering = false; break;
}
}
COVERING_MODE PCB_VIA::GetBackCoveringMode() const
{
if( m_padStack.BackOuterLayers().has_covering.has_value() )
{
return *m_padStack.BackOuterLayers().has_covering ?
COVERING_MODE::COVERED : COVERING_MODE::NOT_COVERED;
}
return COVERING_MODE::FROM_RULES;
}
void PCB_VIA::SetFrontPluggingMode( PLUGGING_MODE aMode )
{
switch( aMode )
{
case PLUGGING_MODE::FROM_RULES: m_padStack.FrontOuterLayers().has_plugging.reset(); break;
case PLUGGING_MODE::PLUGGED: m_padStack.FrontOuterLayers().has_plugging = true; break;
case PLUGGING_MODE::NOT_PLUGGED: m_padStack.FrontOuterLayers().has_plugging = false; break;
}
}
PLUGGING_MODE PCB_VIA::GetFrontPluggingMode() const
{
if( m_padStack.FrontOuterLayers().has_plugging.has_value() )
{
return *m_padStack.FrontOuterLayers().has_plugging ?
PLUGGING_MODE::PLUGGED : PLUGGING_MODE::NOT_PLUGGED;
}
return PLUGGING_MODE::FROM_RULES;
}
void PCB_VIA::SetBackPluggingMode( PLUGGING_MODE aMode )
{
switch( aMode )
{
case PLUGGING_MODE::FROM_RULES: m_padStack.BackOuterLayers().has_plugging.reset(); break;
case PLUGGING_MODE::PLUGGED: m_padStack.BackOuterLayers().has_plugging = true; break;
case PLUGGING_MODE::NOT_PLUGGED: m_padStack.BackOuterLayers().has_plugging = false; break;
}
}
PLUGGING_MODE PCB_VIA::GetBackPluggingMode() const
{
if( m_padStack.BackOuterLayers().has_plugging.has_value() )
{
return *m_padStack.BackOuterLayers().has_plugging ?
PLUGGING_MODE::PLUGGED : PLUGGING_MODE::NOT_PLUGGED;
}
return PLUGGING_MODE::FROM_RULES;
}
void PCB_VIA::SetCappingMode( CAPPING_MODE aMode )
{
switch( aMode )
{
case CAPPING_MODE::FROM_RULES: m_padStack.Drill().is_capped.reset(); break;
case CAPPING_MODE::CAPPED: m_padStack.Drill().is_capped = true; break;
case CAPPING_MODE::NOT_CAPPED: m_padStack.Drill().is_capped = false; break;
}
}
CAPPING_MODE PCB_VIA::GetCappingMode() const
{
if( m_padStack.Drill().is_capped.has_value() )
{
return *m_padStack.Drill().is_capped ?
CAPPING_MODE::CAPPED : CAPPING_MODE::NOT_CAPPED;
}
return CAPPING_MODE::FROM_RULES;
}
void PCB_VIA::SetFillingMode( FILLING_MODE aMode )
{
switch( aMode )
{
case FILLING_MODE::FROM_RULES: m_padStack.Drill().is_filled.reset(); break;
case FILLING_MODE::FILLED: m_padStack.Drill().is_filled = true; break;
case FILLING_MODE::NOT_FILLED: m_padStack.Drill().is_filled = false; break;
}
}
FILLING_MODE PCB_VIA::GetFillingMode() const
{
if( m_padStack.Drill().is_filled.has_value() )
{
return *m_padStack.Drill().is_filled ?
FILLING_MODE::FILLED : FILLING_MODE::NOT_FILLED;
}
return FILLING_MODE::FROM_RULES;
}
// clang-format on: the suggestion is slightly less readable
bool PCB_VIA::IsTented( PCB_LAYER_ID aLayer ) const
{
wxCHECK_MSG( IsFrontLayer( aLayer ) || IsBackLayer( aLayer ), true,
@ -2041,17 +2180,43 @@ static struct TRACK_VIA_DESC
{
TRACK_VIA_DESC()
{
// clang-format off: the suggestion is less readable
ENUM_MAP<VIATYPE>::Instance()
.Undefined( VIATYPE::NOT_DEFINED )
.Map( VIATYPE::THROUGH, _HKI( "Through" ) )
.Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
.Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
.Undefined( VIATYPE::NOT_DEFINED )
.Map( VIATYPE::THROUGH, _HKI( "Through" ) )
.Map( VIATYPE::BLIND_BURIED, _HKI( "Blind/buried" ) )
.Map( VIATYPE::MICROVIA, _HKI( "Micro" ) );
ENUM_MAP<TENTING_MODE>::Instance()
.Undefined( TENTING_MODE::FROM_RULES )
.Map( TENTING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( TENTING_MODE::TENTED, _HKI( "Tented" ) )
.Map( TENTING_MODE::NOT_TENTED, _HKI( "Not tented" ) );
.Undefined( TENTING_MODE::FROM_RULES )
.Map( TENTING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( TENTING_MODE::TENTED, _HKI( "Tented" ) )
.Map( TENTING_MODE::NOT_TENTED, _HKI( "Not tented" ) );
ENUM_MAP<COVERING_MODE>::Instance()
.Undefined( COVERING_MODE::FROM_RULES )
.Map( COVERING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( COVERING_MODE::COVERED, _HKI( "Covered" ) )
.Map( COVERING_MODE::NOT_COVERED, _HKI( "Not covered" ) );
ENUM_MAP<PLUGGING_MODE>::Instance()
.Undefined( PLUGGING_MODE::FROM_RULES )
.Map( PLUGGING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( PLUGGING_MODE::PLUGGED, _HKI( "Plugged" ) )
.Map( PLUGGING_MODE::NOT_PLUGGED, _HKI( "Not plugged" ) );
ENUM_MAP<CAPPING_MODE>::Instance()
.Undefined( CAPPING_MODE::FROM_RULES )
.Map( CAPPING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( CAPPING_MODE::CAPPED, _HKI( "Capped" ) )
.Map( CAPPING_MODE::NOT_CAPPED, _HKI( "Not capped" ) );
ENUM_MAP<FILLING_MODE>::Instance()
.Undefined( FILLING_MODE::FROM_RULES )
.Map( FILLING_MODE::FROM_RULES, _HKI( "From design rules" ) )
.Map( FILLING_MODE::FILLED, _HKI( "Filled" ) )
.Map( FILLING_MODE::NOT_FILLED, _HKI( "Not filled" ) );
// clang-format on: the suggestion is less readable
ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
@ -2118,6 +2283,7 @@ static struct TRACK_VIA_DESC
propMgr.Mask( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
// clang-format off: the suggestion is less readable
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Diameter" ),
&PCB_VIA::SetFrontWidth, &PCB_VIA::GetFrontWidth, PROPERTY_DISPLAY::PT_SIZE ), groupVia );
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Hole" ),
@ -2132,8 +2298,25 @@ static struct TRACK_VIA_DESC
&PCB_VIA::SetFrontTentingMode, &PCB_VIA::GetFrontTentingMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, TENTING_MODE>( _HKI( "Back tenting" ),
&PCB_VIA::SetBackTentingMode, &PCB_VIA::GetBackTentingMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, COVERING_MODE>( _HKI( "Front covering" ),
&PCB_VIA::SetFrontCoveringMode, &PCB_VIA::GetFrontCoveringMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, COVERING_MODE>( _HKI( "Back covering" ),
&PCB_VIA::SetBackCoveringMode, &PCB_VIA::GetBackCoveringMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PLUGGING_MODE>( _HKI( "Front plugging" ),
&PCB_VIA::SetFrontPluggingMode, &PCB_VIA::GetFrontPluggingMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PLUGGING_MODE>( _HKI( "Back plugging" ),
&PCB_VIA::SetBackPluggingMode, &PCB_VIA::GetBackPluggingMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, CAPPING_MODE>( _HKI( "Capping" ),
&PCB_VIA::SetCappingMode, &PCB_VIA::GetCappingMode ), groupVia );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, FILLING_MODE>( _HKI( "Filling" ),
&PCB_VIA::SetFillingMode, &PCB_VIA::GetFillingMode ), groupVia );
// clang-format on: the suggestion is less readable
}
} _TRACK_VIA_DESC;
ENUM_TO_WXANY( VIATYPE );
ENUM_TO_WXANY( TENTING_MODE );
ENUM_TO_WXANY( COVERING_MODE );
ENUM_TO_WXANY( PLUGGING_MODE );
ENUM_TO_WXANY( CAPPING_MODE );
ENUM_TO_WXANY( FILLING_MODE );

View File

@ -78,6 +78,34 @@ enum class TENTING_MODE
NOT_TENTED = 2
};
enum class COVERING_MODE
{
FROM_RULES = 0,
COVERED = 1,
NOT_COVERED = 2
};
enum class PLUGGING_MODE
{
FROM_RULES = 0,
PLUGGED = 1,
NOT_PLUGGED = 2
};
enum class CAPPING_MODE
{
FROM_RULES = 0,
CAPPED = 1,
NOT_CAPPED = 2
};
enum class FILLING_MODE
{
FROM_RULES = 0,
FILLED = 1,
NOT_FILLED = 2
};
#define UNDEFINED_DRILL_DIAMETER -1 //< Undefined via drill diameter.
// Used for tracks and vias for algorithmic safety, not to enforce constraints
@ -440,11 +468,27 @@ public:
MINOPTMAX<int> GetWidthConstraint( wxString* aSource = nullptr ) const override;
MINOPTMAX<int> GetDrillConstraint( wxString* aSource = nullptr ) const;
void SetFrontTentingMode( TENTING_MODE aMode );
void SetFrontTentingMode( TENTING_MODE aMode );
TENTING_MODE GetFrontTentingMode() const;
void SetBackTentingMode( TENTING_MODE aMode );
void SetBackTentingMode( TENTING_MODE aMode );
TENTING_MODE GetBackTentingMode() const;
void SetFrontCoveringMode( COVERING_MODE aMode );
COVERING_MODE GetFrontCoveringMode() const;
void SetBackCoveringMode( COVERING_MODE aMode );
COVERING_MODE GetBackCoveringMode() const;
void SetFrontPluggingMode( PLUGGING_MODE aMode );
PLUGGING_MODE GetFrontPluggingMode() const;
void SetBackPluggingMode( PLUGGING_MODE aMode );
PLUGGING_MODE GetBackPluggingMode() const;
void SetCappingMode( CAPPING_MODE aMode );
CAPPING_MODE GetCappingMode() const;
void SetFillingMode( FILLING_MODE aMode );
FILLING_MODE GetFillingMode() const;
bool IsTented( PCB_LAYER_ID aLayer ) const override;
int GetSolderMaskExpansion() const;

View File

@ -1365,7 +1365,7 @@ int PCBNEW_JOBS_HANDLER::JobExportDrill( JOB* aJob )
gerberWriter->SetMapFileFormat( mapFormat );
if( !gerberWriter->CreateDrillandMapFilesSet( outPath, true, aDrillJob->m_generateMap,
m_reporter ) )
aDrillJob->m_generateTenting, m_reporter ) )
{
return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT;
}

View File

@ -347,6 +347,9 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
m_params.emplace_back( new PARAM<bool>( "gen_drill.generate_map",
&m_GenDrill.generate_map, false ) );
m_params.emplace_back( new PARAM<bool>( "gen_drill.generate_tenting",
&m_GenDrill.generate_tenting, false ) );
m_params.emplace_back( new PARAM<int>( "export_2581.units",
&m_Export2581.units, 0 ) );

View File

@ -258,6 +258,7 @@ public:
int map_file_type;
int zeros_format;
bool generate_map;
bool generate_tenting;
};
struct DIALOG_IMPORT_GRAPHICS

View File

@ -806,6 +806,72 @@
)
)
)
(via
(at 120 102)
(size 0.6)
(drill 0.3)
(layers "F.Cu" "B.Cu")
(tenting
(front yes)
(back yes)
)
(capping no)
(covering
(front no)
(back no)
)
(plugging
(front no)
(back no)
)
(filling no)
(net 0)
(uuid "1960d56a-6a04-4e66-966f-9a4deaf28ebf")
)
(via
(at 118 102)
(size 0.6)
(drill 0.3)
(layers "F.Cu" "B.Cu")
(tenting
(front no)
(back no)
)
(capping yes)
(covering
(front no)
(back no)
)
(plugging
(front no)
(back no)
)
(filling yes)
(net 0)
(uuid "697fcc5c-9cf0-474e-bfab-8afc0fb18c62")
)
(via
(at 120 103.5)
(size 0.6)
(drill 0.3)
(layers "F.Cu" "B.Cu")
(tenting
(front yes)
(back no)
)
(capping no)
(covering
(front no)
(back no)
)
(plugging
(front yes)
(back no)
)
(filling no)
(net 0)
(uuid "beec9209-4c19-4ad5-ac29-5a2a547cb37b")
)
(segment
(start 88.3 63.2)
(end 90.65 60.85)

View File

@ -104,14 +104,16 @@ BOOST_FIXTURE_TEST_CASE( BoardTypes, PROTO_TEST_FIXTURE )
break;
case PCB_ARC_T:
testProtoFromKiCadObject<kiapi::board::types::Arc>( track, m_board.get() );
testProtoFromKiCadObject<kiapi::board::types::Arc>( static_cast<PCB_ARC*>( track ),
m_board.get() );
break;
case PCB_VIA_T:
// Vias are not strict-checked at the moment because m_zoneLayerOverrides is not
// currently exposed to the API
// TODO(JE) enable strict when fixed
testProtoFromKiCadObject<kiapi::board::types::Via>( track, m_board.get(), false );
testProtoFromKiCadObject<kiapi::board::types::Via>( static_cast<PCB_VIA*>( track ),
m_board.get(), false );
break;
default: