Don't use line styles for solder masks on shapes in 3D viewer.

Also convert hatched fills to solid for solder masks.

Also give track solder masks their specified
expansion when exporting to STEP.

Also implement solder masks for shapes when
exporting to STEP.
This commit is contained in:
Jeff Young 2025-04-28 21:35:20 +01:00
parent dcddf16c30
commit d384790d47
10 changed files with 87 additions and 64 deletions

View File

@ -595,9 +595,11 @@ void BOARD_ADAPTER::createArcSegments( const VECTOR2I& aCentre, const VECTOR2I&
void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
const BOARD_ITEM* aOwner, PCB_LAYER_ID aLayer )
{
// The full width of the lines to create
int linewidth = aShape->GetWidth();
int margin = 0;
LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
int linewidth = aShape->GetWidth();
int margin = 0;
bool isSolidFill = aShape->IsSolidFill();
bool isHatchedFill = aShape->IsHatchedFill();
if( IsSolderMaskLayer( aLayer )
&& aShape->HasSolderMask()
@ -605,10 +607,16 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
{
margin = aShape->GetSolderMaskExpansion();
linewidth += margin * 2;
lineStyle = LINE_STYLE::SOLID;
if( isHatchedFill )
{
isSolidFill = true;
isHatchedFill = false;
}
}
float linewidth3DU = TO_3DU( linewidth );
LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
float linewidth3DU = TO_3DU( linewidth );
if( lineStyle <= LINE_STYLE::FIRST_TYPE )
{
@ -620,7 +628,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
float innerR3DU = TO_3DU( aShape->GetRadius() ) - linewidth3DU / 2.0;
float outerR3DU = TO_3DU( aShape->GetRadius() ) + linewidth3DU / 2.0;
if( aShape->IsSolidFill() || innerR3DU <= 0.0 )
if( isSolidFill || innerR3DU <= 0.0 )
addFILLED_CIRCLE_2D( aContainer, center3DU, outerR3DU, *aOwner );
else
addRING_2D( aContainer, center3DU, innerR3DU, outerR3DU, *aOwner );
@ -629,7 +637,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
}
case SHAPE_T::RECTANGLE:
if( aShape->IsSolidFill() )
if( isSolidFill )
{
SHAPE_POLY_SET polyList;
@ -733,7 +741,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta
delete shape;
}
if( aShape->IsHatchedFill() )
if( isHatchedFill )
ConvertPolygonToTriangles( aShape->GetHatching(), *aContainer, m_biuTo3Dunits, *aOwner );
}

View File

@ -578,7 +578,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
addFootprintShapes( fp, layerContainer, layer, visibilityFlags );
// Add copper item to the plated copper polygon list if required
if( cfg.DifferentiatePlatedCopper() && ( layer == F_Cu || layer == B_Cu ) )
if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
{
SHAPE_POLY_SET* layerPoly = layer == F_Cu ? m_frontPlatedCopperPolys : m_backPlatedCopperPolys;
@ -646,7 +646,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
}
// Add copper item to the plated copper polygon list if required
if( cfg.DifferentiatePlatedCopper() && ( layer == F_Cu || layer == B_Cu ) )
if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
{
SHAPE_POLY_SET* copperPolys = layer == F_Cu ? m_frontPlatedCopperPolys : m_backPlatedCopperPolys;
@ -758,7 +758,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
zones.emplace_back( std::make_pair( zone, layer ) );
layer_lock.emplace( layer, std::make_unique<std::mutex>() );
if( cfg.DifferentiatePlatedCopper() && ( layer == F_Cu || layer == B_Cu ) )
if( cfg.DifferentiatePlatedCopper() && IsExternalCopperLayer( layer ) )
{
SHAPE_POLY_SET* copperPolys = layer == F_Cu ? m_frontPlatedCopperPolys : m_backPlatedCopperPolys;
@ -959,7 +959,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
continue;
// Only vias on a external copper layer can have a solder mask
PCB_LAYER_ID copper_layer = layer == F_Mask ? F_Cu : B_Cu;
PCB_LAYER_ID copper_layer = ( layer == F_Mask ) ? F_Cu : B_Cu;
if( track->Type() == PCB_VIA_T )
{

View File

@ -184,7 +184,7 @@ std::string PLACE_FILE_EXPORTER::GenPositionData()
footprint_pos -= m_place_Offset;
int layer = list[ii].m_Footprint->GetLayer();
wxASSERT( layer == F_Cu || layer == B_Cu );
wxASSERT( IsExternalCopperLayer( layer ) );
if( layer == B_Cu && m_negateBottomX )
footprint_pos.x = - footprint_pos.x;
@ -253,7 +253,7 @@ std::string PLACE_FILE_EXPORTER::GenPositionData()
footprint_pos -= m_place_Offset;
int layer = list[ii].m_Footprint->GetLayer();
wxASSERT( layer == F_Cu || layer == B_Cu );
wxASSERT( IsExternalCopperLayer( layer ) );
if( layer == B_Cu && m_negateBottomX )
footprint_pos.x = - footprint_pos.x;

View File

@ -419,23 +419,16 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin )
int maxError = m_board->GetDesignSettings().m_MaxError;
if( m_params.m_ExportSoldermask )
if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( F_Mask ) )
{
if( aTrack->IsOnLayer( F_Mask ) )
{
SHAPE_POLY_SET poly;
aTrack->TransformShapeToPolygon( poly, F_Mask, 0, maxError, ERROR_INSIDE );
aTrack->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
aTrack->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
}
m_poly_shapes[F_Mask][wxEmptyString].Append( poly );
}
if( aTrack->IsOnLayer( B_Mask ) )
{
SHAPE_POLY_SET poly;
aTrack->TransformShapeToPolygon( poly, B_Mask, 0, maxError, ERROR_INSIDE );
m_poly_shapes[B_Mask][wxEmptyString].Append( poly );
}
if( m_params.m_ExportSoldermask && aTrack->IsOnLayer( B_Mask ) )
{
aTrack->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
aTrack->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
}
if( aTrack->Type() == PCB_VIA_T )
@ -466,8 +459,7 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin )
m_poly_holes[pcblayer].Append( holePoly );
}
m_pcbModel->AddBarrel( *holeShape, top_layer, bot_layer, true, aOrigin,
via->GetNetname() );
m_pcbModel->AddBarrel( *holeShape, top_layer, bot_layer, true, aOrigin, via->GetNetname() );
}
//// Cut holes in silkscreen (buggy: insufficient polyset self-intersection checking)
@ -570,13 +562,12 @@ bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin )
for( SHAPE* shape : shapes )
{
STROKE_PARAMS::Stroke( shape, lineStyle, graphic->GetWidth(), &renderSettings,
[&]( const VECTOR2I& a, const VECTOR2I& b )
{
SHAPE_SEGMENT seg( a, b, graphic->GetWidth() );
seg.TransformToPolygon(
m_poly_shapes[pcblayer][graphic->GetNetname()],
maxError, ERROR_INSIDE );
} );
[&]( const VECTOR2I& a, const VECTOR2I& b )
{
SHAPE_SEGMENT seg( a, b, graphic->GetWidth() );
seg.TransformToPolygon( m_poly_shapes[pcblayer][graphic->GetNetname()],
maxError, ERROR_INSIDE );
} );
}
for( SHAPE* shape : shapes )
@ -586,6 +577,18 @@ bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin )
if( graphic->IsHatchedFill() )
m_poly_shapes[pcblayer][graphic->GetNetname()].Append( graphic->GetHatching() );
if( m_params.m_ExportSoldermask && graphic->IsOnLayer( F_Mask ) )
{
graphic->TransformShapeToPolygon( m_poly_shapes[F_Mask][wxEmptyString], F_Mask,
graphic->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
}
if( m_params.m_ExportSoldermask && graphic->IsOnLayer( B_Mask ) )
{
graphic->TransformShapeToPolygon( m_poly_shapes[B_Mask][wxEmptyString], B_Mask,
graphic->GetSolderMaskExpansion(), maxError, ERROR_INSIDE );
}
break;
}

View File

@ -402,7 +402,7 @@ bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
// Plated through hole pads need copper on the top/bottom layers for proper soldering
// Unless the user has removed them in the pad dialog
if( mode == PADSTACK::UNCONNECTED_LAYER_MODE::REMOVE_EXCEPT_START_AND_END
&& ( aLayer == F_Cu || aLayer == B_Cu ) )
&& IsExternalCopperLayer( aLayer ) )
{
return true;
}

View File

@ -2499,7 +2499,7 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
if( aTrack->HasSolderMask()
&& aTrack->GetLocalSolderMaskMargin().has_value()
&& ( aTrack->IsOnLayer( F_Cu ) || aTrack->IsOnLayer( B_Cu ) ) )
&& IsExternalCopperLayer( aTrack->GetLayer() ) )
{
m_out->Print( "(solder_mask_margin %s)",
formatInternalUnits( aTrack->GetLocalSolderMaskMargin().value() ).c_str() );

View File

@ -1831,6 +1831,8 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayGraphicsFill;
int thickness = getLineThickness( aShape->GetWidth() );
LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
bool isSolidFill = aShape->IsSolidFill();
bool isHatchedFill = aShape->IsHatchedFill();
if( lineStyle == LINE_STYLE::DEFAULT )
lineStyle = LINE_STYLE::SOLID;
@ -1841,6 +1843,12 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
{
lineStyle = LINE_STYLE::SOLID;
thickness += aShape->GetSolderMaskExpansion() * 2;
if( isHatchedFill )
{
isSolidFill = true;
isHatchedFill = false;
}
}
if( IsNetnameLayer( aLayer ) )
@ -1966,7 +1974,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
m_gal->DrawSegment( pts[3], pts[0], thickness );
}
if( aShape->IsSolidFill() )
if( isSolidFill )
{
SHAPE_POLY_SET poly;
poly.NewOutline();
@ -1975,10 +1983,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
poly.Append( pt );
if( thickness < 0 )
{
poly.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
m_maxError );
}
poly.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
m_gal->DrawPolygon( poly );
}
@ -2027,7 +2032,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
{
m_gal->DrawCircle( aShape->GetStart(), radius );
}
else if( aShape->IsSolidFill() )
else if( isSolidFill )
{
if( thickness < 0 )
{
@ -2063,7 +2068,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
m_gal->DrawSegmentChain( shape.Outline( ii ), thickness );
}
if( aShape->IsSolidFill() )
if( isSolidFill )
{
if( thickness < 0 )
{
@ -2155,7 +2160,7 @@ void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
delete shape;
}
if( aShape->IsHatchedFill() )
if( isHatchedFill )
{
m_gal->SetIsStroke( false );
m_gal->SetIsFill( true );

View File

@ -2367,8 +2367,8 @@ static struct TRACK_VIA_DESC
auto isExternalLayerTrack =
[]( INSPECTABLE* aItem )
{
if( auto track = dynamic_cast<PCB_TRACK*>( aItem ) )
return track->GetLayer() == F_Cu || track->GetLayer() == B_Cu;
if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( aItem ) )
return IsExternalCopperLayer( track->GetLayer() );
return false;
};

View File

@ -874,19 +874,27 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
if( !( m_layerMask & aShape->GetLayerSet() ).any() )
return;
OUTLINE_MODE plotMode = GetPlotMode();
OUTLINE_MODE plotMode = GetPlotMode();
int thickness = aShape->GetWidth();
int margin = thickness; // unclamped thickness (can be negative)
int margin = thickness; // unclamped thickness (can be negative)
LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
bool onCopperLayer = ( LSET::AllCuMask() & m_layerMask ).any();
bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & m_layerMask ).any();
bool onCopperLayer = ( LSET::AllCuMask() & m_layerMask ).any();
bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & m_layerMask ).any();
bool isSolidFill = aShape->IsSolidFill();
bool isHatchedFill = aShape->IsHatchedFill();
if( onSolderMaskLayer
&& aShape->HasSolderMask()
&& IsExternalCopperLayer( aShape->GetLayer() ) )
{
margin += 2 * aShape->GetSolderMaskExpansion();
margin += 2 * aShape->GetSolderMaskExpansion();
thickness = std::max( margin, 0 );
if( isHatchedFill )
{
isSolidFill = true;
isHatchedFill = false;
}
}
m_plotter->SetColor( getColor( aShape->GetLayer() ) );
@ -943,7 +951,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
break;
case SHAPE_T::CIRCLE:
if( aShape->IsSolidFill() )
if( isSolidFill )
{
int diameter = aShape->GetRadius() * 2 + thickness;
@ -1014,7 +1022,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
m_board->GetDesignSettings().m_MaxError );
}
FILL_T fill = aShape->IsSolidFill() ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
FILL_T fill = isSolidFill ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
for( int jj = 0; jj < tmpPoly.OutlineCount(); ++jj )
{
@ -1065,7 +1073,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
m_board->GetDesignSettings().m_MaxError );
}
FILL_T fill_mode = aShape->IsSolidFill() ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
FILL_T fill_mode = isSolidFill ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
if( poly.OutlineCount() > 0 )
{
@ -1077,8 +1085,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
}
else
{
m_plotter->PlotPoly( poly.COutline( 0 ), fill_mode, thickness,
&gbr_metadata );
m_plotter->PlotPoly( poly.COutline( 0 ), fill_mode, thickness, &gbr_metadata );
}
}
}
@ -1109,7 +1116,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
delete shape;
}
if( aShape->IsHatchedFill() )
if( isHatchedFill )
{
for( int ii = 0; ii < aShape->GetHatching().OutlineCount(); ++ii )
{

View File

@ -256,7 +256,7 @@ void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
aCommit.Added( new_teardrop );
if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
if( track->HasSolderMask() && IsExternalCopperLayer( track->GetLayer() ) )
{
ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_PADVIA, points, track );
m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
@ -299,7 +299,7 @@ void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
aCommit.Added( new_teardrop );
if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
if( track->HasSolderMask() && IsExternalCopperLayer( track->GetLayer() ) )
{
ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_PADVIA, points, track );
m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
@ -481,7 +481,7 @@ void TEARDROP_MANAGER::AddTeardropsOnTracks( BOARD_COMMIT& aCommit,
aCommit.Added( new_teardrop );
if( track->HasSolderMask() && ( track->GetLayer() == F_Cu || track->GetLayer() == B_Cu ) )
if( track->HasSolderMask() && IsExternalCopperLayer( track->GetLayer() ) )
{
ZONE* new_teardrop_mask = createTeardropMask( TD_TYPE_TRACKEND, points, track );
m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );