Gerber plotter: plot round rectangle pads using a region with arcs.

Previously, the region was a usual polygon with arc approximated by segment.
Using a region with arcs is a better way because it allows CAM tools to
identify this region as a round-rect pad.

A side effect is a better shape and smaller files, however not really noticeable in fact.
This commit is contained in:
jean-pierre charras 2020-03-19 12:53:52 +01:00
parent ba8c3a5582
commit 9cb3333d05
3 changed files with 202 additions and 24 deletions

View File

@ -520,16 +520,29 @@ void GERBER_PLOTTER::Arc( const wxPoint& aCenter, double aStAngle, double aEndAn
{
SetCurrentLineWidth( aWidth );
// aFill is not used here.
plotArc( aCenter, aStAngle, aEndAngle, aRadius, false );
}
void GERBER_PLOTTER::plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
int aRadius, bool aPlotInRegion )
{
wxPoint start, end;
start.x = aCenter.x + KiROUND( cosdecideg( aRadius, aStAngle ) );
start.y = aCenter.y - KiROUND( sindecideg( aRadius, aStAngle ) );
MoveTo( start );
if( !aPlotInRegion )
MoveTo( start );
else
LineTo( start );
end.x = aCenter.x + KiROUND( cosdecideg( aRadius, aEndAngle ) );
end.y = aCenter.y - KiROUND( sindecideg( aRadius, aEndAngle ) );
DPOINT devEnd = userToDeviceCoordinates( end );
DPOINT devCenter = userToDeviceCoordinates( aCenter ) - userToDeviceCoordinates( start );
fprintf( outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
fprintf( outputFile, "G75*\n" ); // Multiquadrant (360 degrees) mode
if( aStAngle < aEndAngle )
fprintf( outputFile, "G03*\n" ); // Active circular interpolation, CCW
@ -855,16 +868,14 @@ void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize,
}
break;
default: // plot pad shape as polygon
default: // plot pad shape as Gerber region
{
// XXX to do: use an aperture macro to declare the rotated pad
wxPoint coord[4];
// coord[0] is assumed the lower left
// coord[1] is assumed the upper left
// coord[2] is assumed the upper right
// coord[3] is assumed the lower right
/* Trace the outline. */
coord[0].x = -size.x/2; // lower left
coord[0].y = size.y/2;
coord[1].x = -size.x/2; // upper left
@ -887,35 +898,172 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS
{
GBR_METADATA* gbr_metadata = static_cast<GBR_METADATA*>( aData );
// Currently, a Pad RoundRect is plotted as polygon.
// TODO: use Aperture macro and flash it
SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient,
aCornerRadius, 0.0, 0, GetPlotterArcHighDef() );
if( aTraceMode != FILLED )
{
SHAPE_POLY_SET outline;
TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient,
aCornerRadius, 0.0, 0, GetPlotterArcHighDef() );
SetCurrentLineWidth( USE_DEFAULT_LINE_WIDTH, &gbr_metadata );
outline.Inflate( -GetCurrentLineWidth()/2, 16 );
std::vector< wxPoint > cornerList;
// TransformRoundRectToPolygon creates only one convex polygon
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
cornerList.reserve( poly.PointCount() + 1 );
for( int ii = 0; ii < poly.PointCount(); ++ii )
cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
// Close polygon
cornerList.push_back( cornerList[0] );
// plot outlines
PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata );
}
else
{
// A Pad RoundRect is plotted as a Gerber region.
// Initialize region metadata:
bool clearTA_AperFunction = false; // true if a TA.AperFunction is used
if( gbr_metadata )
{
formatNetAttribute( &gbr_metadata->m_NetlistMetadata );
std::string attrib = gbr_metadata->m_ApertureMetadata.FormatAttribute( !m_useX2format );
if( !attrib.empty() )
{
fputs( attrib.c_str(), outputFile );
clearTA_AperFunction = true;
}
}
// Plot the region using arcs in corners.
plotRoundRectAsRegion( aPadPos, aSize, aCornerRadius, aOrient );
// Clear the TA attribute, to avoid the next item to inherit it:
if( clearTA_AperFunction )
{
if( m_useX2format )
{
fputs( "%TD.AperFunction*%\n", outputFile );
}
else
{
fputs( "G04 #@! TD.AperFunction*\n", outputFile );
}
}
}
}
void GERBER_PLOTTER::plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
int aCornerRadius, double aOrient )
{
// The region outline is generated by 4 sides and 4 90 deg arcs
// 1 --- 2
// | c |
// 4 --- 3
// Note also in user coordinates the Y axis is from top to bottom
// for historical reasons.
// A helper structure to handle outlines coordinates (segments and arcs)
// in user coordinates
struct RR_EDGE
{
wxPoint m_start;
wxPoint m_end;
wxPoint m_center;
// in decidegrees: angle start. angle end = m_arc_angle_start+arc_angle
double m_arc_angle_start;
};
const double arc_angle = -900.0; // in decidegrees
int hsizeX = aSize.x/2;
int hsizeY = aSize.y/2;
RR_EDGE curr_edge;
std::vector<RR_EDGE> rr_outline;
// Build outline coordinates, relative to rectangle center, rotation 0:
// Top left corner 1 (and 4 to 1 left vertical side @ x=-hsizeX)
curr_edge.m_start.x = -hsizeX;
curr_edge.m_start.y = hsizeY - aCornerRadius;
curr_edge.m_end.x = curr_edge.m_start.x;
curr_edge.m_end.y = -hsizeY + aCornerRadius;
curr_edge.m_center.x = -hsizeX + aCornerRadius;
curr_edge.m_center.y = curr_edge.m_end.y;
curr_edge.m_arc_angle_start = aOrient + 1800.0; // En decidegree
rr_outline.push_back( curr_edge );
// Top right corner 2 (and 1 to 2 top horizontal side @ y=-hsizeY)
curr_edge.m_start.x = -hsizeX + aCornerRadius;
curr_edge.m_start.y = -hsizeY;
curr_edge.m_end.x = hsizeX - aCornerRadius;
curr_edge.m_end.y = curr_edge.m_start.y;
curr_edge.m_center.x = curr_edge.m_end.x;
curr_edge.m_center.y = -hsizeY + aCornerRadius;
curr_edge.m_arc_angle_start = aOrient + 900.0; // En decidegree
rr_outline.push_back( curr_edge );
// bottom right corner 3 (and 2 to 3 right vertical side @ x=hsizeX)
curr_edge.m_start.x = hsizeX;
curr_edge.m_start.y = -hsizeY + aCornerRadius;
curr_edge.m_end.x = curr_edge.m_start.x;
curr_edge.m_end.y = hsizeY - aCornerRadius;
curr_edge.m_center.x = hsizeX - aCornerRadius;
curr_edge.m_center.y = curr_edge.m_end.y;
curr_edge.m_arc_angle_start = aOrient + 0.0; // En decidegree
rr_outline.push_back( curr_edge );
// bottom left corner 4 (and 3 to 4 bottom horizontal side @ y=hsizeY)
curr_edge.m_start.x = hsizeX - aCornerRadius;
curr_edge.m_start.y = hsizeY;
curr_edge.m_end.x = -hsizeX + aCornerRadius;
curr_edge.m_end.y = curr_edge.m_start.y;
curr_edge.m_center.x = curr_edge.m_end.x;
curr_edge.m_center.y = hsizeY - aCornerRadius;
curr_edge.m_arc_angle_start = aOrient - 900.0; // En decidegree
rr_outline.push_back( curr_edge );
// Move relative coordinates to the actual location and rotation:
for( RR_EDGE& rr_edge: rr_outline )
{
RotatePoint( &rr_edge.m_start, aOrient );
RotatePoint( &rr_edge.m_end, aOrient );
RotatePoint( &rr_edge.m_center, aOrient );
rr_edge.m_start += aRectCenter;
rr_edge.m_end += aRectCenter;
rr_edge.m_center += aRectCenter;
}
std::vector< wxPoint > cornerList;
// TransformRoundRectToPolygon creates only one convex polygon
SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
cornerList.reserve( poly.PointCount() + 1 );
fputs( "G36*\n", outputFile ); // Start region
fputs( "G01*\n", outputFile ); // Set linear interpolation.
MoveTo( rr_outline[0].m_start ); // Start point of region
for( int ii = 0; ii < poly.PointCount(); ++ii )
cornerList.emplace_back( poly.CPoint( ii ).x, poly.CPoint( ii ).y );
for( RR_EDGE& rr_edge: rr_outline )
{
if( aCornerRadius ) // Guard: ensure we do not create arcs with radius = 0
{
// LineTo( rr_edge.m_end ); // made in plotArc()
plotArc( rr_edge.m_center, rr_edge.m_arc_angle_start, rr_edge.m_arc_angle_start+arc_angle,
aCornerRadius, true );
}
else
LineTo( rr_edge.m_end );
}
// Close polygon
cornerList.push_back( cornerList[0] );
if( aTraceMode == SKETCH )
PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata );
else
PlotGerberRegion( cornerList, gbr_metadata );
fputs( "G37*\n", outputFile ); // Close region
}
void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
SHAPE_POLY_SET* aPolygons,
EDA_DRAW_MODE_T aTraceMode, void* aData )

View File

@ -1328,6 +1328,30 @@ public:
APERTURE::APERTURE_TYPE aType, int aApertureAttribute );
protected:
/** Plot a round rect (a round rect shape in fact) as a Gerber region
* using lines and arcs for corners
* @param aRectCenter is the center of the rectangle
* @param aSize is the size of the rectangle
* @param aCornerRadius is the radius of the corners
* @param aOrient is the rotation of the rectangle
* Note: only the G36 ... G37 region is created.
*/
void plotRoundRectAsRegion( const wxPoint& aRectCenter, const wxSize& aSize,
int aCornerRadius, double aOrient );
/**
* Plot a Gerber arc.
* if aPlotInRegion = true, the current pen position will not be
* initialized to the arc start position, and therefore the arc can be used
* to define a region outline item
* a line will be created from current ^position to arc start point
* if aPlotInRegion = false, the current pen position will be initialized
* to the arc start position, to plot an usual arc item
* The line thickness is not initialized in plotArc, and must be initialized
* before calling it if needed.
*/
void plotArc( const wxPoint& aCenter, double aStAngle, double aEndAngle,
int aRadius, bool aPlotInRegion );
/**
* Pick an existing aperture or create a new one, matching the
* size, type and attributes.

View File

@ -792,6 +792,12 @@ void BRDITEMS_PLOTTER::PlotDrawSegment( DRAWSEGMENT* aSeg )
if( aSeg->GetLayer() == Edge_Cuts )
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_EDGECUT );
if( IsCopperLayer( aSeg->GetLayer() ) )
// Graphic items (DRAWSEGMENT, TEXT) having no net have the NonConductor attribute
// Graphic items having a net have the Conductor attribute, but are not (yet?)
// supported in Pcbnew
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR );
switch( aSeg->GetShape() )
{
case S_CIRCLE: