Support custom padstacks in plotter

This commit is contained in:
Jon Evans 2024-10-02 18:07:09 -04:00
parent 6e0f6da2fa
commit 752371833b
3 changed files with 208 additions and 198 deletions

View File

@ -101,7 +101,8 @@ public:
* Unlike other items, a pad had not a specific color and be drawn as a non filled item * Unlike other items, a pad had not a specific color and be drawn as a non filled item
* although the plot mode is filled color and plot mode are needed by this function. * although the plot mode is filled color and plot mode are needed by this function.
*/ */
void PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_MODE aPlotMode ); void PlotPad( const PAD* aPad, PCB_LAYER_ID aLayer, const COLOR4D& aColor,
OUTLINE_MODE aPlotMode );
void PlotPadNumber( const PAD* aPad, const COLOR4D& aColor ); void PlotPadNumber( const PAD* aPad, const COLOR4D& aColor );

View File

@ -367,6 +367,9 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
itemplotter.PlotPadNumber( pad, color ); itemplotter.PlotPadNumber( pad, color );
} }
auto plotPadLayer =
[&]( PCB_LAYER_ID aLayer )
{
VECTOR2I margin; VECTOR2I margin;
int width_adj = 0; int width_adj = 0;
@ -374,10 +377,10 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
width_adj = itemplotter.getFineWidthAdj(); width_adj = itemplotter.getFineWidthAdj();
if( onSolderMaskLayer ) if( onSolderMaskLayer )
margin.x = margin.y = pad->GetSolderMaskExpansion( PADSTACK::ALL_LAYERS ); margin.x = margin.y = pad->GetSolderMaskExpansion( aLayer );
if( onSolderPasteLayer ) if( onSolderPasteLayer )
margin = pad->GetSolderPasteMargin( PADSTACK::ALL_LAYERS ); margin = pad->GetSolderPasteMargin( aLayer );
// not all shapes can have a different margin for x and y axis // not all shapes can have a different margin for x and y axis
// in fact only oval and rect shapes can have different values. // in fact only oval and rect shapes can have different values.
@ -386,51 +389,52 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
int mask_clearance = margin.x; int mask_clearance = margin.x;
// Now offset the pad size by margin + width_adj // Now offset the pad size by margin + width_adj
VECTOR2I padPlotsSize = pad->GetSize( PADSTACK::ALL_LAYERS ) + margin * 2 + VECTOR2I( width_adj, width_adj ); VECTOR2I padPlotsSize =
pad->GetSize( aLayer ) + margin * 2 + VECTOR2I( width_adj, width_adj );
// Store these parameters that can be modified to plot inflated/deflated pads shape // Store these parameters that can be modified to plot inflated/deflated pads shape
PAD_SHAPE padShape = pad->GetShape( PADSTACK::ALL_LAYERS ); PAD_SHAPE padShape = pad->GetShape( aLayer );
VECTOR2I padSize = pad->GetSize( PADSTACK::ALL_LAYERS ); VECTOR2I padSize = pad->GetSize( aLayer );
VECTOR2I padDelta = pad->GetDelta( PADSTACK::ALL_LAYERS ); // has meaning only for trapezoidal pads VECTOR2I padDelta = pad->GetDelta( aLayer ); // has meaning only for trapezoidal pads
// CornerRadius and CornerRadiusRatio can be modified // CornerRadius and CornerRadiusRatio can be modified
// the radius is built from the ratio, so saving/restoring the ratio is enough // the radius is built from the ratio, so saving/restoring the ratio is enough
double padCornerRadiusRatio = pad->GetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS ); double padCornerRadiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
// Don't draw a 0 sized pad. // Don't draw a 0 sized pad.
// Note: a custom pad can have its pad anchor with size = 0 // Note: a custom pad can have its pad anchor with size = 0
if( pad->GetShape( PADSTACK::ALL_LAYERS ) != PAD_SHAPE::CUSTOM if( padShape != PAD_SHAPE::CUSTOM
&& ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) ) && ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
{ {
continue; return;
} }
switch( pad->GetShape( PADSTACK::ALL_LAYERS ) ) switch( padShape )
{ {
case PAD_SHAPE::CIRCLE: case PAD_SHAPE::CIRCLE:
case PAD_SHAPE::OVAL: case PAD_SHAPE::OVAL:
pad->SetSize( PADSTACK::ALL_LAYERS, padPlotsSize ); pad->SetSize( aLayer, padPlotsSize );
if( aPlotOpt.GetSkipPlotNPTH_Pads() && if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
( aPlotOpt.GetDrillMarksType() == DRILL_MARKS::NO_DRILL_SHAPE ) && ( aPlotOpt.GetDrillMarksType() == DRILL_MARKS::NO_DRILL_SHAPE ) &&
( pad->GetSize( PADSTACK::ALL_LAYERS ) == pad->GetDrillSize() ) && ( pad->GetSize(aLayer ) == pad->GetDrillSize() ) &&
( pad->GetAttribute() == PAD_ATTRIB::NPTH ) ) ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
{ {
break; break;
} }
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
break; break;
case PAD_SHAPE::RECTANGLE: case PAD_SHAPE::RECTANGLE:
pad->SetSize( PADSTACK::ALL_LAYERS, padPlotsSize ); pad->SetSize( aLayer, padPlotsSize );
if( mask_clearance > 0 ) if( mask_clearance > 0 )
{ {
pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::ROUNDRECT ); pad->SetShape( aLayer, PAD_SHAPE::ROUNDRECT );
pad->SetRoundRectCornerRadius( PADSTACK::ALL_LAYERS, mask_clearance ); pad->SetRoundRectCornerRadius( aLayer, mask_clearance );
} }
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
break; break;
case PAD_SHAPE::TRAPEZOID: case PAD_SHAPE::TRAPEZOID:
@ -441,13 +445,13 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
// we are using only margin.x as inflate/deflate value // we are using only margin.x as inflate/deflate value
if( mask_clearance == 0 ) if( mask_clearance == 0 )
{ {
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
} }
else else
{ {
PAD dummy( *pad ); PAD dummy( *pad );
dummy.SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE ); dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
dummy.SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM ); dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
outline.NewOutline(); outline.NewOutline();
int dx = padSize.x / 2; int dx = padSize.x / 2;
@ -466,14 +470,14 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError,
SHAPE_POLY_SET::PM_FAST ); SHAPE_POLY_SET::PM_FAST );
dummy.DeletePrimitivesList(); dummy.DeletePrimitivesList();
dummy.AddPrimitivePoly( PADSTACK::ALL_LAYERS, outline, 0, true ); dummy.AddPrimitivePoly( aLayer, outline, 0, true );
// Be sure the anchor pad is not bigger than the deflated shape because this // Be sure the anchor pad is not bigger than the deflated shape because this
// anchor will be added to the pad shape when plotting the pad. So now the // anchor will be added to the pad shape when plotting the pad. So now the
// polygonal shape is built, we can clamp the anchor size // polygonal shape is built, we can clamp the anchor size
dummy.SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( 0, 0 ) ); dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
itemplotter.PlotPad( &dummy, color, padPlotMode ); itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
} }
break; break;
@ -483,11 +487,11 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
// rounding is stored as a percent, but we have to update this ratio // rounding is stored as a percent, but we have to update this ratio
// to force recalculation of other values after size changing (we do not // to force recalculation of other values after size changing (we do not
// really change the rounding percent value) // really change the rounding percent value)
double radius_ratio = pad->GetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS ); double radius_ratio = pad->GetRoundRectRadiusRatio( aLayer );
pad->SetSize( PADSTACK::ALL_LAYERS, padPlotsSize ); pad->SetSize( aLayer, padPlotsSize );
pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, radius_ratio ); pad->SetRoundRectRadiusRatio( aLayer, radius_ratio );
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
break; break;
} }
@ -495,8 +499,8 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
if( mask_clearance == 0 ) if( mask_clearance == 0 )
{ {
// the size can be slightly inflated by width_adj (PS/PDF only) // the size can be slightly inflated by width_adj (PS/PDF only)
pad->SetSize( PADSTACK::ALL_LAYERS, padPlotsSize ); pad->SetSize( aLayer, padPlotsSize );
itemplotter.PlotPad( pad, color, padPlotMode ); itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
} }
else else
{ {
@ -508,7 +512,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
// pad offset and orientation 0. The actual pos, offset and rotation will be // pad offset and orientation 0. The actual pos, offset and rotation will be
// taken in account later by the plot function // taken in account later by the plot function
dummy.SetPosition( VECTOR2I( 0, 0 ) ); dummy.SetPosition( VECTOR2I( 0, 0 ) );
dummy.SetOffset( PADSTACK::ALL_LAYERS, VECTOR2I( 0, 0 ) ); dummy.SetOffset( aLayer, VECTOR2I( 0, 0 ) );
dummy.SetOrientation( ANGLE_0 ); dummy.SetOrientation( ANGLE_0 );
SHAPE_POLY_SET outline; SHAPE_POLY_SET outline;
dummy.TransformShapeToPolygon( outline, UNDEFINED_LAYER, 0, maxError, dummy.TransformShapeToPolygon( outline, UNDEFINED_LAYER, 0, maxError,
@ -518,21 +522,21 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
SHAPE_POLY_SET::PM_FAST ); SHAPE_POLY_SET::PM_FAST );
// Initialize the dummy pad shape: // Initialize the dummy pad shape:
dummy.SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE ); dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
dummy.SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CUSTOM ); dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
dummy.DeletePrimitivesList(); dummy.DeletePrimitivesList();
dummy.AddPrimitivePoly( PADSTACK::ALL_LAYERS, outline, 0, true ); dummy.AddPrimitivePoly( aLayer, outline, 0, true );
// Be sure the anchor pad is not bigger than the deflated shape because this // Be sure the anchor pad is not bigger than the deflated shape because this
// anchor will be added to the pad shape when plotting the pad. // anchor will be added to the pad shape when plotting the pad.
// So we set the anchor size to 0 // So we set the anchor size to 0
dummy.SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( 0, 0 ) ); dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
// Restore pad position and offset // Restore pad position and offset
dummy.SetPosition( pad->GetPosition() ); dummy.SetPosition( pad->GetPosition() );
dummy.SetOffset( PADSTACK::ALL_LAYERS, pad->GetOffset( PADSTACK::ALL_LAYERS ) ); dummy.SetOffset( aLayer, pad->GetOffset( aLayer ) );
dummy.SetOrientation( pad->GetOrientation() ); dummy.SetOrientation( pad->GetOrientation() );
itemplotter.PlotPad( &dummy, color, padPlotMode ); itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
} }
break; break;
@ -545,7 +549,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
dummy.SetParentGroup( nullptr ); dummy.SetParentGroup( nullptr );
SHAPE_POLY_SET shape; SHAPE_POLY_SET shape;
pad->MergePrimitivesAsPolygon( PADSTACK::ALL_LAYERS, &shape ); pad->MergePrimitivesAsPolygon( aLayer, &shape );
// Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate() // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
// which can create bad shapes if margin.x is < 0 // which can create bad shapes if margin.x is < 0
@ -553,24 +557,28 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError,
SHAPE_POLY_SET::PM_FAST ); SHAPE_POLY_SET::PM_FAST );
dummy.DeletePrimitivesList(); dummy.DeletePrimitivesList();
dummy.AddPrimitivePoly( PADSTACK::ALL_LAYERS, shape, 0, true ); dummy.AddPrimitivePoly( aLayer, shape, 0, true );
// Be sure the anchor pad is not bigger than the deflated shape because this // Be sure the anchor pad is not bigger than the deflated shape because this
// anchor will be added to the pad shape when plotting the pad. So now the // anchor will be added to the pad shape when plotting the pad. So now the
// polygonal shape is built, we can clamp the anchor size // polygonal shape is built, we can clamp the anchor size
if( mask_clearance < 0 ) // we expect margin.x = margin.y for custom pads if( mask_clearance < 0 ) // we expect margin.x = margin.y for custom pads
dummy.SetSize( PADSTACK::ALL_LAYERS, padPlotsSize ); dummy.SetSize( aLayer, padPlotsSize );
itemplotter.PlotPad( &dummy, color, padPlotMode ); itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
break; break;
} }
} }
// Restore the pad parameters modified by the plot code // Restore the pad parameters modified by the plot code
pad->SetSize( PADSTACK::ALL_LAYERS, padSize ); pad->SetSize( aLayer, padSize );
pad->SetDelta( PADSTACK::ALL_LAYERS, padDelta ); pad->SetDelta( aLayer, padDelta );
pad->SetShape( PADSTACK::ALL_LAYERS, padShape ); pad->SetShape( aLayer, padShape );
pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, padCornerRadiusRatio ); pad->SetRoundRectRadiusRatio( aLayer, padCornerRadiusRatio );
};
for( PCB_LAYER_ID layer : aLayerMask.SeqStackupForPlotting() )
plotPadLayer( layer );
} }
if( footprint->IsDNP() if( footprint->IsDNP()

View File

@ -142,9 +142,10 @@ void BRDITEMS_PLOTTER::PlotPadNumber( const PAD* aPad, const COLOR4D& aColor )
} }
void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_MODE aPlotMode ) void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, PCB_LAYER_ID aLayer, const COLOR4D& aColor,
OUTLINE_MODE aPlotMode )
{ {
VECTOR2I shape_pos = aPad->ShapePos( PADSTACK::ALL_LAYERS ); VECTOR2I shape_pos = aPad->ShapePos( aLayer );
GBR_METADATA metadata; GBR_METADATA metadata;
bool plotOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any(); bool plotOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
@ -274,26 +275,26 @@ void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_
if( aPlotMode == SKETCH ) if( aPlotMode == SKETCH )
m_plotter->SetCurrentLineWidth( GetSketchPadLineWidth(), &metadata ); m_plotter->SetCurrentLineWidth( GetSketchPadLineWidth(), &metadata );
switch( aPad->GetShape( PADSTACK::ALL_LAYERS ) ) switch( aPad->GetShape( aLayer ) )
{ {
case PAD_SHAPE::CIRCLE: case PAD_SHAPE::CIRCLE:
m_plotter->FlashPadCircle( shape_pos, aPad->GetSize( PADSTACK::ALL_LAYERS ).x, m_plotter->FlashPadCircle( shape_pos, aPad->GetSize( aLayer ).x,
aPlotMode, &metadata ); aPlotMode, &metadata );
break; break;
case PAD_SHAPE::OVAL: case PAD_SHAPE::OVAL:
m_plotter->FlashPadOval( shape_pos, aPad->GetSize( PADSTACK::ALL_LAYERS ), m_plotter->FlashPadOval( shape_pos, aPad->GetSize( aLayer ),
aPad->GetOrientation(), aPlotMode, &metadata ); aPad->GetOrientation(), aPlotMode, &metadata );
break; break;
case PAD_SHAPE::RECTANGLE: case PAD_SHAPE::RECTANGLE:
m_plotter->FlashPadRect( shape_pos, aPad->GetSize( PADSTACK::ALL_LAYERS ), m_plotter->FlashPadRect( shape_pos, aPad->GetSize( aLayer ),
aPad->GetOrientation(), aPlotMode, &metadata ); aPad->GetOrientation(), aPlotMode, &metadata );
break; break;
case PAD_SHAPE::ROUNDRECT: case PAD_SHAPE::ROUNDRECT:
m_plotter->FlashPadRoundRect( shape_pos, aPad->GetSize( PADSTACK::ALL_LAYERS ), m_plotter->FlashPadRoundRect( shape_pos, aPad->GetSize( aLayer ),
aPad->GetRoundRectCornerRadius( PADSTACK::ALL_LAYERS ), aPad->GetRoundRectCornerRadius( aLayer ),
aPad->GetOrientation(), aPlotMode, &metadata ); aPad->GetOrientation(), aPlotMode, &metadata );
break; break;
@ -305,8 +306,8 @@ void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_
VECTOR2I coord[4]; VECTOR2I coord[4];
// Order is lower left, lower right, upper right, upper left. // Order is lower left, lower right, upper right, upper left.
VECTOR2I half_size = aPad->GetSize( PADSTACK::ALL_LAYERS ) / 2; VECTOR2I half_size = aPad->GetSize( aLayer ) / 2;
VECTOR2I trap_delta = aPad->GetDelta( PADSTACK::ALL_LAYERS ) / 2; VECTOR2I trap_delta = aPad->GetDelta( aLayer ) / 2;
coord[0] = VECTOR2I( -half_size.x - trap_delta.y, half_size.y + trap_delta.x ); coord[0] = VECTOR2I( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
coord[1] = VECTOR2I( half_size.x + trap_delta.y, half_size.y - trap_delta.x ); coord[1] = VECTOR2I( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
@ -323,10 +324,10 @@ void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_
GERBER_PLOTTER* gerberPlotter = static_cast<GERBER_PLOTTER*>( m_plotter ); GERBER_PLOTTER* gerberPlotter = static_cast<GERBER_PLOTTER*>( m_plotter );
gerberPlotter->FlashPadChamferRoundRect( shape_pos, gerberPlotter->FlashPadChamferRoundRect( shape_pos,
aPad->GetSize( PADSTACK::ALL_LAYERS ), aPad->GetSize( aLayer ),
aPad->GetRoundRectCornerRadius( PADSTACK::ALL_LAYERS ), aPad->GetRoundRectCornerRadius( aLayer ),
aPad->GetChamferRectRatio( PADSTACK::ALL_LAYERS ), aPad->GetChamferRectRatio( aLayer ),
aPad->GetChamferPositions( PADSTACK::ALL_LAYERS ), aPad->GetOrientation(), aPad->GetChamferPositions( aLayer ), aPad->GetOrientation(),
aPlotMode, &metadata ); aPlotMode, &metadata );
break; break;
} }
@ -337,11 +338,11 @@ void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, const COLOR4D& aColor, OUTLINE_
case PAD_SHAPE::CUSTOM: case PAD_SHAPE::CUSTOM:
{ {
const std::shared_ptr<SHAPE_POLY_SET>& polygons = const std::shared_ptr<SHAPE_POLY_SET>& polygons =
aPad->GetEffectivePolygon( PADSTACK::ALL_LAYERS, ERROR_INSIDE ); aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
if( polygons->OutlineCount() ) if( polygons->OutlineCount() )
{ {
m_plotter->FlashPadCustom( shape_pos, aPad->GetSize( PADSTACK::ALL_LAYERS ), aPad->GetOrientation(), m_plotter->FlashPadCustom( shape_pos, aPad->GetSize( aLayer ), aPad->GetOrientation(),
polygons.get(), aPlotMode, &metadata ); polygons.get(), aPlotMode, &metadata );
} }
} }