diff --git a/api/proto/board/board_types.proto b/api/proto/board/board_types.proto index f342ad1bb4..716a671a6a 100644 --- a/api/proto/board/board_types.proto +++ b/api/proto/board/board_types.proto @@ -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. diff --git a/common/gbr_metadata.cpp b/common/gbr_metadata.cpp index a2b38d3fd6..7ffc2cfb41 100644 --- a/common/gbr_metadata.cpp +++ b/common/gbr_metadata.cpp @@ -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; diff --git a/common/io/kicad/kicad_io_utils.cpp b/common/io/kicad/kicad_io_utils.cpp index 4dd5ff083a..a2561ff59a 100644 --- a/common/io/kicad/kicad_io_utils.cpp +++ b/common/io/kicad/kicad_io_utils.cpp @@ -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 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 ) { diff --git a/common/jobs/job_export_pcb_drill.cpp b/common/jobs/job_export_pcb_drill.cpp index 92ab7e511a..2693faed7d 100644 --- a/common/jobs/job_export_pcb_drill.cpp +++ b/common/jobs/job_export_pcb_drill.cpp @@ -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( "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( "generate_tenting", + &m_generateTenting, + m_generateTenting ) ); + m_params.emplace_back( new JOB_PARAM( "map_format", &m_mapFormat, m_mapFormat ) ); diff --git a/common/jobs/job_export_pcb_drill.h b/common/jobs/job_export_pcb_drill.h index a38569ec65..9a42f9ffb0 100644 --- a/common/jobs/job_export_pcb_drill.h +++ b/common/jobs/job_export_pcb_drill.h @@ -89,6 +89,8 @@ public: int m_gerberPrecision; bool m_generateMap; + + bool m_generateTenting; }; #endif diff --git a/common/pcb.keywords b/common/pcb.keywords index effdcf267e..2a46d65e73 100644 --- a/common/pcb.keywords +++ b/common/pcb.keywords @@ -333,6 +333,10 @@ teardrop teardrops tedit tenting +covering +plugging +filling +capping text_frame text_position_mode thermal_width diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index c36dd304cc..8285413005 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -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( 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& 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& 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& 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& 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& 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& 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 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 ); } } diff --git a/include/board_design_settings.h b/include/board_design_settings.h index fdd515d0a7..5adb2a3eee 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -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 m_NetSettings; diff --git a/include/gbr_metadata.h b/include/gbr_metadata.h index 189e07f43f..a3e90a8203 100644 --- a/include/gbr_metadata.h +++ b/include/gbr_metadata.h @@ -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,*%" */ 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; diff --git a/include/io/kicad/kicad_io_utils.h b/include/io/kicad/kicad_io_utils.h index ee550b8c20..9ca5444e4a 100644 --- a/include/io/kicad/kicad_io_utils.h +++ b/include/io/kicad/kicad_io_utils.h @@ -19,6 +19,8 @@ #pragma once +#include + #include #include @@ -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 aValue ); + KICOMMON_API void FormatUuid( OUTPUTFORMATTER* aOut, const KIID& aUuid ); /** diff --git a/include/plotters/gbr_plotter_apertures.h b/include/plotters/gbr_plotter_apertures.h index 458c1e2ab9..aae4ec37c9 100644 --- a/include/plotters/gbr_plotter_apertures.h +++ b/include/plotters/gbr_plotter_apertures.h @@ -139,6 +139,8 @@ public: // Only one attribute is allowed by aperture // 0 = no specific aperture attribute int m_ApertureAttribute; + + std::string m_CustomAttribute; }; diff --git a/include/plotters/plotter_gerber.h b/include/plotters/plotter_gerber.h index d12f151436..4270d7e486 100644 --- a/include/plotters/plotter_gerber.h +++ b/include/plotters/plotter_gerber.h @@ -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& 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& 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 diff --git a/kicad/cli/command_pcb_export_drill.cpp b/kicad/cli/command_pcb_export_drill.cpp index 3ac215621b..0d3d3dde4b 100644 --- a/kicad/cli/command_pcb_export_drill.cpp +++ b/kicad/cli/command_pcb_export_drill.cpp @@ -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( ARG_EXCELLON_MINIMALHEAD ); drillJob->m_excellonCombinePTHNPTH = !m_argParser.get( ARG_EXCELLON_SEPARATE_TH ); drillJob->m_generateMap = m_argParser.get( ARG_GENERATE_MAP ); + drillJob->m_generateTenting = m_argParser.get( ARG_GENERATE_TENTING ); drillJob->m_gerberPrecision = m_argParser.get( ARG_GERBER_PRECISION ); if( drillJob->m_gerberPrecision != 5 && drillJob->m_gerberPrecision != 6 ) diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index a4ecbbc5de..ed0d443163 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -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 ); diff --git a/pcbnew/dialogs/dialog_gendrill.cpp b/pcbnew/dialogs/dialog_gendrill.cpp index 41be31a3d7..40943c9ee2 100644 --- a/pcbnew/dialogs/dialog_gendrill.cpp +++ b/pcbnew/dialogs/dialog_gendrill.cpp @@ -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( m_choiceDrillMap->GetSelection() ); m_job->m_zeroFormat = static_cast( 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( 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 ); } } diff --git a/pcbnew/dialogs/dialog_gendrill.h b/pcbnew/dialogs/dialog_gendrill.h index 1c15ef3af3..2e15293901 100644 --- a/pcbnew/dialogs/dialog_gendrill.h +++ b/pcbnew/dialogs/dialog_gendrill.h @@ -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 diff --git a/pcbnew/dialogs/dialog_gendrill_base.cpp b/pcbnew/dialogs/dialog_gendrill_base.cpp index f79ed2f3ce..f62ee447db 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.cpp +++ b/pcbnew/dialogs/dialog_gendrill_base.cpp @@ -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 ); diff --git a/pcbnew/dialogs/dialog_gendrill_base.fbp b/pcbnew/dialogs/dialog_gendrill_base.fbp index b30b2339d2..d5d8c85c63 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.fbp +++ b/pcbnew/dialogs/dialog_gendrill_base.fbp @@ -846,6 +846,89 @@ onFileFormatSelection + + 20 + wxEXPAND|wxLEFT + 1 + + 1 + wxBOTH + + + 0 + + fgSizerGerberX2Options + wxFLEX_GROWMODE_SPECIFIED + none + 4 + 3 + + 5 + wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 0 + + 1 + + 0 + 0 + wxID_ANY + Generate tenting layers + + 0 + + + 0 + + 1 + m_generateTentingLayers + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 wxEXPAND @@ -1684,6 +1767,16 @@ -1 + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + diff --git a/pcbnew/dialogs/dialog_gendrill_base.h b/pcbnew/dialogs/dialog_gendrill_base.h index 1851c6fef5..d19451b76f 100644 --- a/pcbnew/dialogs/dialog_gendrill_base.h +++ b/pcbnew/dialogs/dialog_gendrill_base.h @@ -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; diff --git a/pcbnew/dialogs/dialog_track_via_properties.cpp b/pcbnew/dialogs/dialog_track_via_properties.cpp index b662ba90b4..0aa9cf7513 100644 --- a/pcbnew/dialogs/dialog_track_via_properties.cpp +++ b/pcbnew/dialogs/dialog_track_via_properties.cpp @@ -35,6 +35,15 @@ #include #include #include +#include + + +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 tentingOverride = via->Padstack().IsTented( aLayer ); + for( auto& preset : magic_enum::enum_values() ) + { + 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& front, + const std::optional& 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& 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( 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( 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& aFront, + std::optional& 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& 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( 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 diff --git a/pcbnew/dialogs/dialog_track_via_properties.h b/pcbnew/dialogs/dialog_track_via_properties.h index 9d1b69590e..2f42b866ab 100644 --- a/pcbnew/dialogs/dialog_track_via_properties.h +++ b/pcbnew/dialogs/dialog_track_via_properties.h @@ -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 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 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 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" ) } + }; }; diff --git a/pcbnew/dialogs/dialog_track_via_properties_base.cpp b/pcbnew/dialogs/dialog_track_via_properties_base.cpp index 1d60ca5c1e..726995b2b5 100644 --- a/pcbnew/dialogs/dialog_track_via_properties_base.cpp +++ b/pcbnew/dialogs/dialog_track_via_properties_base.cpp @@ -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 ); diff --git a/pcbnew/dialogs/dialog_track_via_properties_base.fbp b/pcbnew/dialogs/dialog_track_via_properties_base.fbp index 8675a13906..e280301464 100644 --- a/pcbnew/dialogs/dialog_track_via_properties_base.fbp +++ b/pcbnew/dialogs/dialog_track_via_properties_base.fbp @@ -2084,7 +2084,7 @@ 5 wxEXPAND 1 - + bSizerViasLeftCol wxVERTICAL @@ -2423,11 +2423,11 @@ - + 5 wxEXPAND|wxTOP 1 - + -1,0 wxBOTH 1 @@ -2438,7 +2438,7 @@ wxFLEX_GROWMODE_SPECIFIED none 3 - + 1 1 0 @@ -2503,7 +2503,7 @@ -1 - + 3 1 1 @@ -2572,7 +2572,7 @@ onViaSelect - + 5 1 2 @@ -2637,23 +2637,23 @@ -1 - + 0 3 0 wxEXPAND 2 1 - + m_sbPadstackSettings wxHORIZONTAL protected - + 5 wxALIGN_CENTER_VERTICAL 0 - + 1 1 1 @@ -2711,11 +2711,11 @@ -1 - + 5 wxALL 0 - + 1 1 1 @@ -2777,11 +2777,11 @@ onPadstackModeChanged - + 5 wxALIGN_CENTER_VERTICAL|wxLEFT 0 - + 1 1 1 @@ -2839,11 +2839,11 @@ -1 - + 5 wxALL 0 - + 1 1 1 @@ -2907,7 +2907,7 @@ - + 1 1 0 @@ -2972,7 +2972,7 @@ -1 - + 5 1 1 @@ -3041,7 +3041,7 @@ onViaEdit - + 5 1 2 @@ -3106,7 +3106,7 @@ -1 - + 1 1 0 @@ -3171,7 +3171,7 @@ -1 - + 5 1 1 @@ -3240,7 +3240,7 @@ onViaEdit - + 5 1 2 @@ -3329,25 +3329,27 @@ wxVERTICAL none - 3 - wxEXPAND|wxBOTTOM - 0 - - 2 + 5 + wxEXPAND + 1 + + wxBOTH 1 - 0,1,2 - 5 + + 0 - fgSizer4 + gbSizer4 wxFLEX_GROWMODE_SPECIFIED none - 0 0 - + 5 - wxALIGN_CENTER_VERTICAL|wxLEFT - 0 + 1 + 0 + wxALL + 0 + 1 1 1 @@ -3406,10 +3408,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND - 0 + 1 + 1 + wxBOTTOM|wxEXPAND + 0 + 1 1 1 @@ -3472,30 +3477,13 @@ onViaEdit - + 5 - wxEXPAND - 1 - - 3 - protected - 0 - - - - 5 - wxEXPAND - 1 - - 0 - protected - 0 - - - - 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT - 0 + 1 + 0 + wxALIGN_LEFT|wxALL + 1 + 1 1 1 @@ -3554,10 +3542,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND - 0 + 1 + 1 + wxBOTTOM|wxEXPAND + 1 + 1 1 1 @@ -3621,10 +3612,13 @@ onViaEdit - + 5 - wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT - 0 + 1 + 0 + wxALIGN_LEFT|wxALL + 2 + 1 1 1 @@ -3683,10 +3677,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND - 0 + 1 + 1 + wxBOTTOM|wxEXPAND + 2 + 1 1 1 @@ -3750,31 +3747,14 @@ onViaEdit - + 5 - wxEXPAND - 1 - - 15 - protected - 0 - - - - 5 - wxEXPAND - 1 - - 0 - protected - 0 - - - - 5 - wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT - 0 - + 1 + 0 + wxALIGN_LEFT|wxALL + 3 + 1 + 1 1 1 @@ -3832,11 +3812,14 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND - 0 - + 1 + 1 + wxBOTTOM|wxEXPAND + 3 + 1 + 1 1 1 @@ -3881,7 +3864,7 @@ 1 Resizable - 1 + 0 1 @@ -3897,11 +3880,14 @@ - + 5 - wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT - 0 - + 1 + 0 + wxALIGN_LEFT|wxALL + 4 + 1 + 1 1 1 @@ -3930,7 +3916,7 @@ 0 0 wxID_ANY - Front tenting: + Protection features: 0 0 @@ -3939,7 +3925,7 @@ 0 1 - m_tentingFrontLabel + m_protectionPresetsLabel 1 @@ -3959,10 +3945,13 @@ -1 - + 5 - wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND - 0 + 1 + 1 + wxBOTTOM|wxEXPAND + 4 + 1 1 1 @@ -3977,7 +3966,7 @@ 1 0 - "From design rules" "Tented" "Not tented" + 1 1 @@ -4000,7 +3989,7 @@ 0 1 - m_tentingFrontCtrl + m_protectionFeatures 1 @@ -4014,218 +4003,7 @@ ; ; forward_declare 0 - Whether to tent (cover with soldermask) this via on the front side - - wxFILTER_NONE - wxDefaultValidator - - - - - onFrontTentingChanged - - - - 5 - wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - - 1 - 0 - 1 - - 1 - - 0 - - Dock - 0 - Left - 0 - 1 - - 1 - - - 0 - 0 - wxID_ANY - Link - - 0 - - 0 - - - 0 - - 1 - m_btnLinkTenting - 1 - - - protected - 1 - - - - Resizable - 1 - - ; ; forward_declare - 0 - Link front and back tenting settings - - wxFILTER_NONE - wxDefaultValidator - - 1 - - - - onTentingLinkToggle - - - - 5 - wxEXPAND - 1 - - 0 - protected - 0 - - - - 5 - wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT - 0 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - Back tenting: - 0 - - 0 - - - 0 - - 1 - m_tentingBackLabel - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - - - 5 - wxALIGN_CENTER_VERTICAL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - 0 - - 0 - 0 - - - - 1 - 0 - "From design rules" "Tented" "Not tented" - 1 - - 1 - 0 - Dock - 0 - Left - 0 - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_tentingBackCtrl - 1 - - - protected - 1 - - Resizable - 0 - 1 - - - ; ; forward_declare - 0 - Whether to tent (cover with soldermask) this via on the back side + Select which protection feature according to IPC-4761 the via should have. wxFILTER_NONE wxDefaultValidator diff --git a/pcbnew/dialogs/dialog_track_via_properties_base.h b/pcbnew/dialogs/dialog_track_via_properties_base.h index 9ec8342408..70641a38a0 100644 --- a/pcbnew/dialogs/dialog_track_via_properties_base.h +++ b/pcbnew/dialogs/dialog_track_via_properties_base.h @@ -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 #include #include -#include #include #include #include @@ -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(); } diff --git a/pcbnew/exporters/gendrill_file_writer_base.cpp b/pcbnew/exporters/gendrill_file_writer_base.cpp index 2d22e06b16..644315f609 100644 --- a/pcbnew/exporters/gendrill_file_writer_base.cpp +++ b/pcbnew/exporters/gendrill_file_writer_base.cpp @@ -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 ) { diff --git a/pcbnew/exporters/gendrill_file_writer_base.h b/pcbnew/exporters/gendrill_file_writer_base.h index deed72083e..a6f8eb0055 100644 --- a/pcbnew/exporters/gendrill_file_writer_base.h +++ b/pcbnew/exporters/gendrill_file_writer_base.h @@ -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) diff --git a/pcbnew/exporters/gendrill_gerber_writer.cpp b/pcbnew/exporters/gendrill_gerber_writer.cpp index 46680353ac..d9058aaf33 100644 --- a/pcbnew/exporters/gendrill_gerber_writer.cpp +++ b/pcbnew/exporters/gendrill_gerber_writer.cpp @@ -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( hole_descr.m_ItemParent ) ) + { + continue; + } + + const PCB_VIA* via = dyn_cast( 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 ) diff --git a/pcbnew/exporters/gendrill_gerber_writer.h b/pcbnew/exporters/gendrill_gerber_writer.h index 2e1e05864e..6e9035cc87 100644 --- a/pcbnew/exporters/gendrill_gerber_writer.h +++ b/pcbnew/exporters/gendrill_gerber_writer.h @@ -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. diff --git a/pcbnew/padstack.cpp b/pcbnew/padstack.cpp index e66abdf4ea..f77d6eedc1 100644 --- a/pcbnew/padstack.cpp +++ b/pcbnew/padstack.cpp @@ -213,6 +213,18 @@ bool PADSTACK::Deserialize( const google::protobuf::Any& aContainer ) using namespace kiapi::board::types; PadStack padstack; + auto unpackOptional = []( const ProtoEnum& aProto, + std::optional& 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( padstack.drill().start_layer() ); Drill().end = FromProtoEnum( 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( padstack.unconnected_layer_removal() ) ); - auto unpackMask = - []( const SolderMaskMode& aProto, std::optional& 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& 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 = []( const std::optional& aVal, ProtoEnum aTrueVal, + ProtoEnum aFalseVal, + ProtoEnum aNullVal ) -> ProtoEnum + { + if( aVal.has_value() ) + return *aVal ? aTrueVal : aFalseVal; + + return aNullVal; + }; + padstack.set_type( ToProtoEnum( 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( StartLayer() ) ); drill->set_end_layer( ToProtoEnum( 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( m_unconnectedLayerMode ) ); - auto packOptional = - []( const std::optional& aVal, ProtoEnum aTrueVal, - ProtoEnum aFalseVal, ProtoEnum aNullVal ) -> ProtoEnum - { - if( aVal.has_value() ) - return *aVal ? aTrueVal : aFalseVal; - - return aNullVal; - }; - PadStackOuterLayer* frontOuter = padstack.mutable_front_outer_layers(); PadStackOuterLayer* backOuter = padstack.mutable_back_outer_layers(); - frontOuter->set_solder_mask_mode( packOptional( FrontOuterLayers().has_solder_mask, - SMM_MASKED, SMM_UNMASKED, - SMM_FROM_DESIGN_RULES ) ); + 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 PADSTACK::IsTented( PCB_LAYER_ID aSide ) const wxCHECK_MSG( false, std::nullopt, "IsTented expects a front or back layer" ); } +std::optional 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 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 PADSTACK::IsCapped() const +{ + return m_drill.is_capped; +} + +std::optional PADSTACK::IsFilled() const +{ + return m_drill.is_filled; +} + IMPLEMENT_ENUM_TO_WXANY( PADSTACK::UNCONNECTED_LAYER_MODE ) diff --git a/pcbnew/padstack.h b/pcbnew/padstack.h index d8a21fe7b5..0fa317321c 100644 --- a/pcbnew/padstack.h +++ b/pcbnew/padstack.h @@ -228,8 +228,11 @@ public: std::optional solder_mask_margin; std::optional solder_paste_margin; std::optional solder_paste_margin_ratio; + std::optional has_solder_mask; ///< True if this outer layer has mask (is not tented) std::optional has_solder_paste; ///< True if this outer layer has solder paste + std::optional has_covering; ///< True if the pad on this side should have covering + std::optional 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 is_filled; ///< True if the drill hole should be filled completely + std::optional is_capped; ///< True if the drill hole should be capped + bool operator==( const DRILL_PROPS& aOther ) const; }; @@ -323,6 +329,14 @@ public: */ std::optional IsTented( PCB_LAYER_ID aSide ) const; + std::optional IsCovered( PCB_LAYER_ID aSide ) const; + + std::optional IsPlugged( PCB_LAYER_ID aSide ) const; + + std::optional IsCapped() const; + + std::optional IsFilled() const; + CUSTOM_SHAPE_ZONE_MODE CustomShapeInZoneMode() const { return m_customShapeInZoneMode; } void SetCustomShapeInZoneMode( CUSTOM_SHAPE_ZONE_MODE aM ) { m_customShapeInZoneMode = aM; } diff --git a/pcbnew/pcb_io/ipc2581/ipc2581_types.h b/pcbnew/pcb_io/ipc2581/ipc2581_types.h index 96cb7ba648..ef6a0a6dc2 100644 --- a/pcbnew/pcb_io/ipc2581/ipc2581_types.h +++ b/pcbnew/pcb_io/ipc2581/ipc2581_types.h @@ -55,6 +55,15 @@ enum class cadPinType SURFACE }; +enum class auxLayerType +{ + COVERING, + PLUGGING, + TENTING, + FILLING, + CAPPING, +}; + enum class certificationCategoryType { ASSEMBLYDRAWING, diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp index 09b0b9c00f..40240729b5 100644 --- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp +++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp @@ -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( item ); + + std::vector> 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( 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() { diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h index 67e4f41769..9d154ebfa2 100644 --- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h +++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.h @@ -24,6 +24,8 @@ #include #include +#include "ipc2581_types.h" + #include #include // PCB_LAYER_ID #include @@ -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& aItems ); + void generateLayerSetAuxilliary( wxXmlNode* aStepNode ); + wxXmlNode* generateContentStackup( wxXmlNode* aContentNode ); void generateComponents( wxXmlNode* aStepNode ); @@ -326,6 +332,9 @@ private: std::map, std::vector> m_slot_holes; //, std::vector> + m_auxilliary_Layers; + PROGRESS_REPORTER* m_progress_reporter; std::set m_acceptable_chars; //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 front = aPadstack.FrontOuterLayers().has_solder_mask; - std::optional 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 ) { diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h index f4503e7df2..27f2445dee 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -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: diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index 784538ec51..6aabfa43b9 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -235,6 +235,23 @@ bool PCB_IO_KICAD_SEXPR_PARSER::parseBool() } +std::optional 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> +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 front{ std::nullopt }; + std::optional 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 }; } diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h index 1f2fd2afeb..e1cf10b848 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.h @@ -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 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> + parseFrontBackOptBool( bool aLegacy = false ); + /* * @return if m_appendToExisting, returns new KIID(), otherwise returns CurStr() as KIID. */ diff --git a/pcbnew/pcb_io/odbpp/odb_entity.cpp b/pcbnew/pcb_io/odbpp/odb_entity.cpp index dcd85a302a..f8c8f039b4 100644 --- a/pcbnew/pcb_io/odbpp/odb_entity.cpp +++ b/pcbnew/pcb_io/odbpp/odb_entity.cpp @@ -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( 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 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( item ); + + m_layerItems[via->GetNetCode()].push_back( item ); + } + } + + break; + } + } +} void ODB_STEP_ENTITY::InitEntityData() { diff --git a/pcbnew/pcb_io/odbpp/odb_entity.h b/pcbnew/pcb_io/odbpp/odb_entity.h index de51bfc624..a90fb3d47a 100644 --- a/pcbnew/pcb_io/odbpp/odb_entity.h +++ b/pcbnew/pcb_io/odbpp/odb_entity.h @@ -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(); diff --git a/pcbnew/pcb_io/odbpp/odb_feature.cpp b/pcbnew/pcb_io/odbpp/odb_feature.cpp index ac7164236f..d1af9edced 100644 --- a/pcbnew/pcb_io/odbpp/odb_feature.cpp +++ b/pcbnew/pcb_io/odbpp/odb_feature.cpp @@ -406,7 +406,37 @@ void FEATURES_MANAGER::InitFeatureList( PCB_LAYER_ID aLayer, std::vector( 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::vectorGetWidth( 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 ) ) } ); - } - } - } } }; diff --git a/pcbnew/pcb_io/odbpp/odb_feature.h b/pcbnew/pcb_io/odbpp/odb_feature.h index 2991c8bf58..daf9945115 100644 --- a/pcbnew/pcb_io/odbpp/odb_feature.h +++ b/pcbnew/pcb_io/odbpp/odb_feature.h @@ -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 ); diff --git a/pcbnew/pcb_io/odbpp/odb_util.h b/pcbnew/pcb_io/odbpp/odb_util.h index d5b7d75908..da6b42ceeb 100644 --- a/pcbnew/pcb_io/odbpp/odb_util.h +++ b/pcbnew/pcb_io/odbpp/odb_util.h @@ -69,6 +69,15 @@ enum class ODB_FID_TYPE HOLE }; +enum class ODB_AUX_LAYER_TYPE +{ + COVERING, + PLUGGING, + TENTING, + FILLING, + CAPPING, +}; + namespace ODB { diff --git a/pcbnew/pcb_io/odbpp/pcb_io_odbpp.h b/pcbnew/pcb_io/odbpp/pcb_io_odbpp.h index 2beebaa37c..582e351cef 100644 --- a/pcbnew/pcb_io/odbpp/pcb_io_odbpp.h +++ b/pcbnew/pcb_io/odbpp/pcb_io_odbpp.h @@ -105,6 +105,13 @@ public: return m_drill_layers; } + inline std::map, + std::vector>& + GetAuxilliaryLayerItemsMap() + { + return m_auxilliary_layers; + } + inline std::map, std::vector>& GetSlotHolesMap() { @@ -164,6 +171,9 @@ private: std::map, std::vector> m_drill_layers; //, std::vector> + m_auxilliary_layers; //, std::vector> m_slot_holes; // PCB_VIA::GetEffectiveHoleShape() const return std::make_shared( 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::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::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::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::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::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::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& layerEnum = ENUM_MAP::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( _HKI( "Diameter" ), &PCB_VIA::SetFrontWidth, &PCB_VIA::GetFrontWidth, PROPERTY_DISPLAY::PT_SIZE ), groupVia ); propMgr.AddProperty( new PROPERTY( _HKI( "Hole" ), @@ -2132,8 +2298,25 @@ static struct TRACK_VIA_DESC &PCB_VIA::SetFrontTentingMode, &PCB_VIA::GetFrontTentingMode ), groupVia ); propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Back tenting" ), &PCB_VIA::SetBackTentingMode, &PCB_VIA::GetBackTentingMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Front covering" ), + &PCB_VIA::SetFrontCoveringMode, &PCB_VIA::GetFrontCoveringMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Back covering" ), + &PCB_VIA::SetBackCoveringMode, &PCB_VIA::GetBackCoveringMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Front plugging" ), + &PCB_VIA::SetFrontPluggingMode, &PCB_VIA::GetFrontPluggingMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Back plugging" ), + &PCB_VIA::SetBackPluggingMode, &PCB_VIA::GetBackPluggingMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _HKI( "Capping" ), + &PCB_VIA::SetCappingMode, &PCB_VIA::GetCappingMode ), groupVia ); + propMgr.AddProperty( new PROPERTY_ENUM( _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 ); diff --git a/pcbnew/pcb_track.h b/pcbnew/pcb_track.h index 00673d701e..94fcbcc6c0 100644 --- a/pcbnew/pcb_track.h +++ b/pcbnew/pcb_track.h @@ -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 GetWidthConstraint( wxString* aSource = nullptr ) const override; MINOPTMAX 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; diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 2309444fb4..ce9d18351b 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -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; } diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp index ec00193c86..e422c40342 100644 --- a/pcbnew/pcbnew_settings.cpp +++ b/pcbnew/pcbnew_settings.cpp @@ -347,6 +347,9 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS() m_params.emplace_back( new PARAM( "gen_drill.generate_map", &m_GenDrill.generate_map, false ) ); + m_params.emplace_back( new PARAM( "gen_drill.generate_tenting", + &m_GenDrill.generate_tenting, false ) ); + m_params.emplace_back( new PARAM( "export_2581.units", &m_Export2581.units, 0 ) ); diff --git a/pcbnew/pcbnew_settings.h b/pcbnew/pcbnew_settings.h index 3c89b90c2f..0eba90cd02 100644 --- a/pcbnew/pcbnew_settings.h +++ b/pcbnew/pcbnew_settings.h @@ -258,6 +258,7 @@ public: int map_file_type; int zeros_format; bool generate_map; + bool generate_tenting; }; struct DIALOG_IMPORT_GRAPHICS diff --git a/qa/data/pcbnew/api_kitchen_sink.kicad_pcb b/qa/data/pcbnew/api_kitchen_sink.kicad_pcb index df8c410b2c..9069a921d3 100644 --- a/qa/data/pcbnew/api_kitchen_sink.kicad_pcb +++ b/qa/data/pcbnew/api_kitchen_sink.kicad_pcb @@ -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) diff --git a/qa/tests/api/test_api_proto.cpp b/qa/tests/api/test_api_proto.cpp index b5deb10aa7..b0307bd06c 100644 --- a/qa/tests/api/test_api_proto.cpp +++ b/qa/tests/api/test_api_proto.cpp @@ -104,14 +104,16 @@ BOOST_FIXTURE_TEST_CASE( BoardTypes, PROTO_TEST_FIXTURE ) break; case PCB_ARC_T: - testProtoFromKiCadObject( track, m_board.get() ); + testProtoFromKiCadObject( static_cast( 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( track, m_board.get(), false ); + testProtoFromKiCadObject( static_cast( track ), + m_board.get(), false ); break; default: