From 366d083da2cd6a87f7599a2a93f2e4bb869c8410 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Thu, 8 Jan 2026 08:40:34 -0800 Subject: [PATCH] Fix solder mask generation issues in STEP export Fixed two problems with soldermask generation. First, via holes weren't cut in soldermask when copper export was disabled. Second, we ignored zones on F.Mask/B.Mask when zone export was disabled even though soldermask export was enabled. Fixes https://gitlab.com/kicad/code/kicad/-/issues/20452 (cherry picked from commit 497fe12f2d422b476d8eca15f858f82f103f7102) --- pcbnew/exporters/step/exporter_step.cpp | 29 ++++++++++++++++++++++++- pcbnew/exporters/step/exporter_step.h | 8 +++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp index 125caad0de..0abb1916cd 100644 --- a/pcbnew/exporters/step/exporter_step.cpp +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -512,6 +512,17 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin ) // m_poly_holes[B_SilkS].Append( holePoly ); //} + // Cut via holes in soldermask when the via is not tented. + // This ensures the mask has a proper hole through the via drill, not just the annular ring opening. + if( m_params.m_ExportSoldermask ) + { + if( via->IsOnLayer( F_Mask ) ) + m_poly_holes[F_Mask].Append( holePoly ); + + if( via->IsOnLayer( B_Mask ) ) + m_poly_holes[B_Mask].Append( holePoly ); + } + m_pcbModel->AddHole( *holeShape, m_platingThickness, top_layer, bot_layer, true, aOrigin, !m_params.m_FillAllVias, m_params.m_CutViasInBody ); @@ -533,12 +544,13 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin ) } -void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin ) +void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin, bool aSolderMaskOnly ) { for( ZONE* zone : m_board->Zones() ) { LSET layers = zone->GetLayerSet(); + // Filter by net if a net filter is specified and zone is on copper layer(s) if( ( layers & LSET::AllCuMask() ).count() && !m_params.m_NetFilter.IsEmpty() && !zone->GetNetname().Matches( m_params.m_NetFilter ) ) { @@ -547,6 +559,16 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin ) for( PCB_LAYER_ID layer : layers.Seq() ) { + bool isMaskLayer = ( layer == F_Mask || layer == B_Mask ); + + // If we're only processing soldermask zones, skip non-mask layers + if( aSolderMaskOnly && !isMaskLayer ) + continue; + + // If we're doing full zone export, skip mask layers if they'll be handled separately + if( !aSolderMaskOnly && isMaskLayer && !m_params.m_ExportZones ) + continue; + SHAPE_POLY_SET fill_shape; zone->TransformSolidAreasShapesToPolygon( layer, fill_shape ); fill_shape.Unfracture(); @@ -779,6 +801,11 @@ bool EXPORTER_STEP::buildBoard3DShapes() buildZones3DShape( origin ); } + // Process zones on soldermask layers even when copper zone export is disabled. + // This ensures mask openings defined by zones are properly exported. + if( m_params.m_ExportSoldermask && !m_params.m_ExportZones ) + buildZones3DShape( origin, true ); + for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() ) { SHAPE_POLY_SET holes = m_poly_holes[pcblayer]; diff --git a/pcbnew/exporters/step/exporter_step.h b/pcbnew/exporters/step/exporter_step.h index 1a7f7ff394..cdbb22e868 100644 --- a/pcbnew/exporters/step/exporter_step.h +++ b/pcbnew/exporters/step/exporter_step.h @@ -55,10 +55,10 @@ public: private: bool buildBoard3DShapes(); - bool buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOrigin, SHAPE_POLY_SET* aClipPolygon ); - bool buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin ); - void buildZones3DShape( VECTOR2D aOrigin ); - bool buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin ); + bool buildFootprint3DShapes( FOOTPRINT* aFootprint, const VECTOR2D& aOrigin, SHAPE_POLY_SET* aClipPolygon ); + bool buildTrack3DShape( PCB_TRACK* aTrack, const VECTOR2D& aOrigin ); + void buildZones3DShape( VECTOR2D aOrigin, bool aSolderMaskOnly = false ); + bool buildGraphic3DShape( BOARD_ITEM* aItem, const VECTOR2D& aOrigin ); void initOutputVariant(); EXPORTER_STEP_PARAMS m_params;