From f60954c232ad741fff8e9eb6ad3ebce33958e263 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 8 Oct 2020 14:51:25 +0200 Subject: [PATCH] Gerber export: add support for flashed chamfered rect (with no rounded corner) pads --- common/plotters/GERBER_plotter.cpp | 159 ++++++++++++++++-- common/plotters/gbr_plotter_aperture_macros.h | 55 ++++++ common/plotters/gbr_plotter_apertures.h | 4 + common/plotters/plotter_gerber.h | 38 ++++- pcbnew/plot_brditems_plotter.cpp | 14 +- 5 files changed, 251 insertions(+), 19 deletions(-) diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index 9bba66a32f..eb8d1b9fcd 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -40,6 +40,7 @@ // if GBR_USE_MACROS is defined, pads having a shape that is not a Gerber primitive // will use a macro when possible // Old code will be removed only after many tests +#define GBR_USE_MACROS_FOR_CHAMFERED_RECT #define GBR_USE_MACROS_FOR_ROUNDRECT #define GBR_USE_MACROS_FOR_TRAPEZOID #define GBR_USE_MACROS_FOR_ROTATED_OVAL @@ -67,7 +68,10 @@ GERBER_PLOTTER::GERBER_PLOTTER() m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use - m_hasApertureOutline = false; // true is at least one rotated rect/trapezoid aperture is in use + m_hasApertureOutline4P = false; // true is at least one rotated rect or trapezoid pad + // aperture is in use + m_hasApertureChamferedRect = false; // true is at least one chamfered rect + // (no rounded corner) is in use } @@ -191,7 +195,8 @@ bool GERBER_PLOTTER::StartPlot() m_hasApertureRoundRect = false; // true is at least one round rect aperture is in use m_hasApertureRotOval = false; // true is at least one oval rotated aperture is in use m_hasApertureRotRect = false; // true is at least one rect. rotated aperture is in use - m_hasApertureOutline = false; // true is at least one rotated rect/trapezoid aperture is in use + m_hasApertureOutline4P = false; // true is at least one rotated rect/trapezoid aperture is in use + m_hasApertureChamferedRect = false; // true is at least one chamfered rect is in use wxASSERT( outputFile ); @@ -288,7 +293,8 @@ bool GERBER_PLOTTER::EndPlot() { // Add aperture list macro: if( m_hasApertureRoundRect | m_hasApertureRotOval || - m_hasApertureOutline || m_hasApertureRotRect ) + m_hasApertureOutline4P || m_hasApertureRotRect || + m_hasApertureChamferedRect ) { fputs( "G04 Aperture macros list*\n", outputFile ); @@ -301,9 +307,17 @@ bool GERBER_PLOTTER::EndPlot() if( m_hasApertureRotRect ) fputs( APER_MACRO_ROT_RECT_HEADER, outputFile ); - if( m_hasApertureOutline ) + if( m_hasApertureOutline4P ) fputs( APER_MACRO_OUTLINE4P_HEADER, outputFile ); + if( m_hasApertureChamferedRect ) + { + fputs( APER_MACRO_OUTLINE5P_HEADER, outputFile ); + fputs( APER_MACRO_OUTLINE6P_HEADER, outputFile ); + fputs( APER_MACRO_OUTLINE7P_HEADER, outputFile ); + fputs( APER_MACRO_OUTLINE8P_HEADER, outputFile ); + } + fputs( "G04 Aperture macros list end*\n", outputFile ); } @@ -379,7 +393,8 @@ int GERBER_PLOTTER::GetOrCreateAperture( const std::vector& aCorners, d for( int idx = 0; idx < (int)m_apertures.size(); ++idx ) { 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_ApertureAttribute == aApertureAttribute) ) @@ -601,12 +616,29 @@ void GERBER_PLOTTER::writeApertureList() break; case APERTURE::APER_MACRO_OUTLINE4P: // Aperture macro for trapezoid pads - wxASSERT( tool.m_Corners.size() == 4 ); + case APERTURE::APER_MACRO_OUTLINE5P: // Aperture macro for chamfered rect pads + case APERTURE::APER_MACRO_OUTLINE6P: // Aperture macro for chamfered rect pads + case APERTURE::APER_MACRO_OUTLINE7P: // Aperture macro for chamfered rect pads + case APERTURE::APER_MACRO_OUTLINE8P: // Aperture macro for chamfered rect pads + switch( tool.m_Type ) + { + case APERTURE::APER_MACRO_OUTLINE4P: + sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); break; + case APERTURE::APER_MACRO_OUTLINE5P: + sprintf( cbuf, "%s,", APER_MACRO_OUTLINE5P_NAME ); break; + case APERTURE::APER_MACRO_OUTLINE6P: + sprintf( cbuf, "%s,", APER_MACRO_OUTLINE6P_NAME ); break; + case APERTURE::APER_MACRO_OUTLINE7P: + sprintf( cbuf, "%s,", APER_MACRO_OUTLINE7P_NAME ); break; + case APERTURE::APER_MACRO_OUTLINE8P: + sprintf( cbuf, "%s,", APER_MACRO_OUTLINE8P_NAME ); break; + default: + break; + } - sprintf( cbuf, "%s,", APER_MACRO_OUTLINE4P_NAME ); buffer += cbuf; - // Output all corners (should be 4 corners) + // Output all corners (should be 4 to 8 corners) // Remember: the Y coordinate must be negated, due to the fact in Pcbnew // the Y axis is from top to bottom for( size_t ii = 0; ii < tool.m_Corners.size(); ii++ ) @@ -1333,7 +1365,6 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize { // A Pad custom is plotted as polygon (a region in Gerber language). - GBR_METADATA gbr_metadata; if( aData ) @@ -1369,6 +1400,107 @@ void GERBER_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize } +void GERBER_PLOTTER::FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize, + int aCornerRadius, double aChamferRatio, + int aChamferPositions, + double aPadOrient, EDA_DRAW_MODE_T aPlotMode, void* aData ) + +{ + GBR_METADATA gbr_metadata; + + if( aData ) + gbr_metadata = *static_cast( aData ); + + DPOINT pos_dev = userToDeviceCoordinates( aShapePos ); + + SHAPE_POLY_SET outline; + // polygon corners list + std::vector cornerList; + + bool hasRoundedCorner = aCornerRadius != 0 && aChamferPositions != 15; + +#ifdef GBR_USE_MACROS_FOR_CHAMFERED_RECT + if( aPlotMode != FILLED || hasRoundedCorner ) // Sketch mode or round rect shape +#endif + { + TransformRoundChamferedRectToPolygon( outline, aShapePos, aPadSize, + aPadOrient, aCornerRadius, + aChamferRatio, + aChamferPositions, m_IUsPerDecimil * 2 ); + + // Build the corner list + const SHAPE_LINE_CHAIN& corners = outline.Outline(0); + + for( int ii = 0; ii < corners.PointCount(); ii++ ) + cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y ); + + // Close the polygon + cornerList.push_back( cornerList[0] ); + + if( aPlotMode == SKETCH ) + PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), &gbr_metadata ); + else // round rect shapes shapes are plot as region (a AP Macro is not obvious) + PlotGerberRegion( cornerList, &gbr_metadata ); + + return; + } + + // Build the chamfered polygon (4 to 8 corners ) + TransformRoundChamferedRectToPolygon( outline, wxPoint( 0, 0 ), aPadSize, + 0.0, 0, aChamferRatio, + aChamferPositions, 0 ); + + // Build the corner list + const SHAPE_LINE_CHAIN& corners = outline.Outline(0); + + // Generate the polygon (4 to 8 corners ) + for( int ii = 0; ii < corners.PointCount(); ii++ ) + cornerList.emplace_back( corners.CPoint( ii ).x, corners.CPoint( ii ).y ); + + switch( cornerList.size() ) + { + case 4: + m_hasApertureOutline4P = true; + selectAperture( cornerList, aPadOrient/10.0, + APERTURE::APER_MACRO_OUTLINE4P, gbr_metadata.GetApertureAttrib() ); + break; + + case 5: + m_hasApertureChamferedRect = true; + selectAperture( cornerList, aPadOrient/10.0, + APERTURE::APER_MACRO_OUTLINE5P, gbr_metadata.GetApertureAttrib() ); + break; + + case 6: + m_hasApertureChamferedRect = true; + selectAperture( cornerList, aPadOrient/10.0, + APERTURE::APER_MACRO_OUTLINE6P, gbr_metadata.GetApertureAttrib() ); + break; + + case 7: + m_hasApertureChamferedRect = true; + selectAperture( cornerList, aPadOrient/10.0, + APERTURE::APER_MACRO_OUTLINE7P, gbr_metadata.GetApertureAttrib() ); + break; + + case 8: + m_hasApertureChamferedRect = true; + selectAperture( cornerList, aPadOrient/10.0, + APERTURE::APER_MACRO_OUTLINE8P, gbr_metadata.GetApertureAttrib() ); + break; + + default: + wxLogMessage( "FlashPadChamferRoundRect(): Unexpected number of corners (%d)", + (int)cornerList.size() ); + break; + } + + formatNetAttribute( &gbr_metadata.m_NetlistMetadata ); + + emitDcode( pos_dev, 3 ); +} + + void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners, double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData ) @@ -1397,7 +1529,7 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo else #ifdef GBR_USE_MACROS_FOR_TRAPEZOID { - m_hasApertureOutline = true; + m_hasApertureOutline4P = true; DPOINT pos_dev = userToDeviceCoordinates( aPadPos ); // polygon corners list std::vector corners = { aCorners[0], aCorners[1], aCorners[2], aCorners[3] }; @@ -1422,6 +1554,11 @@ void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, { GBR_METADATA* gbr_metadata = static_cast( aData ); + GBR_METADATA metadata; + + if( gbr_metadata ) + metadata = *gbr_metadata; + if( aTraceMode == SKETCH ) { // Build the polygon: @@ -1440,7 +1577,7 @@ void GERBER_PLOTTER::FlashRegularPolygon( const wxPoint& aShapePos, cornerList.push_back( cornerList[0] ); // Close the shape - PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), gbr_metadata ); + PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), &gbr_metadata ); } else { diff --git a/common/plotters/gbr_plotter_aperture_macros.h b/common/plotters/gbr_plotter_aperture_macros.h index da65a659be..0c5f474353 100644 --- a/common/plotters/gbr_plotter_aperture_macros.h +++ b/common/plotters/gbr_plotter_aperture_macros.h @@ -95,3 +95,58 @@ 0 create outline with 4 corners*\n\ 4,1,4,$1,$2,$3,$4,$5,$6,$7,$8,$1,$2,$9*%\n" +// A aperture macro to define a polygon by 5 corners +// and a rotation angle (usefull for chamfered rect pads) +#define APER_MACRO_OUTLINE5P_NAME "Outline5P" + +#define APER_MACRO_OUTLINE5P_HEADER \ +"%AMOutline5P*\n\ +0 Free polygon, 5 corners , with rotation*\n\ +0 The origin of the aperture is its center*\n\ +0 number of corners: always 8*\n\ +0 $1 to $10 corner X, Y*\n\ +0 $11 Rotation angle, in degrees counterclockwise*\n\ +0 create outline with 8 corners*\n\ +4,1,5,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$1,$2,$11*%\n" + +// A aperture macro to define a polygon by 6 corners +// and a rotation angle (usefull for chamfered rect pads) +#define APER_MACRO_OUTLINE6P_NAME "Outline6P" + +#define APER_MACRO_OUTLINE6P_HEADER \ +"%AMOutline6P*\n\ +0 Free polygon, 6 corners , with rotation*\n\ +0 The origin of the aperture is its center*\n\ +0 number of corners: always 6*\n\ +0 $1 to $12 corner X, Y*\n\ +0 $13 Rotation angle, in degrees counterclockwise*\n\ +0 create outline with 6 corners*\n\ +4,1,6,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$1,$2,$13*%\n" + +// A aperture macro to define a polygon by 7 corners +// and a rotation angle (usefull for chamfered rect pads) +#define APER_MACRO_OUTLINE7P_NAME "Outline7P" + +#define APER_MACRO_OUTLINE7P_HEADER \ +"%AMOutline7P*\n\ +0 Free polygon, 7 corners , with rotation*\n\ +0 The origin of the aperture is its center*\n\ +0 number of corners: always 7*\n\ +0 $1 to $14 corner X, Y*\n\ +0 $15 Rotation angle, in degrees counterclockwise*\n\ +0 create outline with 7 corners*\n\ +4,1,7,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$1,$2,$15*%\n" + +// A aperture macro to define a polygon by 8 corners +// and a rotation angle (usefull for chamfered rect pads) +#define APER_MACRO_OUTLINE8P_NAME "Outline8P" + +#define APER_MACRO_OUTLINE8P_HEADER \ +"%AMOutline8P*\n\ +0 Free polygon, 8 corners , with rotation*\n\ +0 The origin of the aperture is its center*\n\ +0 number of corners: always 8*\n\ +0 $1 to $16 corner X, Y*\n\ +0 $17 Rotation angle, in degrees counterclockwise*\n\ +0 create outline with 8 corners*\n\ +4,1,8,$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$1,$2,$17*%\n" diff --git a/common/plotters/gbr_plotter_apertures.h b/common/plotters/gbr_plotter_apertures.h index bda5717265..78cb671224 100644 --- a/common/plotters/gbr_plotter_apertures.h +++ b/common/plotters/gbr_plotter_apertures.h @@ -63,6 +63,10 @@ public: AM_ROUND_RECT, // Aperture macro for round rect pads AM_ROT_RECT, // Aperture macro for rotated rect pads APER_MACRO_OUTLINE4P, // Aperture macro for trapezoid pads (outline with 4 corners) + APER_MACRO_OUTLINE5P, // Aperture macro for pad polygons with 5 corners (chamfered pads) + APER_MACRO_OUTLINE6P, // Aperture macro for pad polygons with 6 corners (chamfered pads) + APER_MACRO_OUTLINE7P, // Aperture macro for pad polygons with 7 corners (chamfered pads) + APER_MACRO_OUTLINE8P, // Aperture macro for pad polygons with 8 corners (chamfered pads) AM_ROTATED_OVAL // Aperture macro for rotated oval pads // (not rotated uses a primitive) }; diff --git a/common/plotters/plotter_gerber.h b/common/plotters/plotter_gerber.h index a9586beb0e..6ccf2f9044 100644 --- a/common/plotters/plotter_gerber.h +++ b/common/plotters/plotter_gerber.h @@ -142,11 +142,32 @@ public: * TODO: always use flashed shapes (aperture macros) */ virtual void FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners, - double aPadOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) override; + double aPadOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) override; virtual void FlashRegularPolygon( const wxPoint& aShapePos, int aDiameter, int aCornerCount, double aOrient, EDA_DRAW_MODE_T aTraceMode, void* aData ) override; + /** + * flash a chamfered round rect pad. + * @param aShapePos = position of the pad shape + * @param aPadSize = size of the rectangle + * @param aCornerRadius = radius of rounded corners + * @param aChamferRatio = chamfer value (ratio < 0.5 between smallest size and chamfer) + * @param aChamferPositions = identifier of the corners to chamfer: + * 0 = no chamfer + * 1 = TOP_LEFT + * 2 = TOP_RIGHT + * 4 = BOTTOM_LEFT + * 8 = BOTTOM_RIGHT + * @param aPadOrient = rotation in 0.1 degrees of the shape + * @param aPlotMode = FILLED or SKETCH + * @param aData = a reference to Gerber attributes descr + */ + void FlashPadChamferRoundRect( const wxPoint& aShapePos, const wxSize& aPadSize, + int aCornerRadius, double aChamferRatio, + int aChamferPositions, + double aPadOrient, EDA_DRAW_MODE_T aPlotMode, void* aData ); + /** * Plot a Gerber region: similar to PlotPoly but plot only filled polygon, * and add the TA.AperFunction if aData contains this attribute, and clear it @@ -317,12 +338,15 @@ protected: */ void writeApertureList(); - std::vector m_apertures; // The list of available apertures - int m_currentApertureIdx; // The index of the current aperture in m_apertures - bool m_hasApertureRoundRect; // true is at least one round rect aperture is in use - bool m_hasApertureRotOval; // true is at least one oval rotated aperture is in use - bool m_hasApertureRotRect; // true is at least one rect. rotated aperture is in use - bool m_hasApertureOutline; // true is at least one outline (free polygon) aperture is in use + std::vector m_apertures; // The list of available apertures + int m_currentApertureIdx; // The index of the current aperture in m_apertures + bool m_hasApertureRoundRect; // true is at least one round rect aperture is in use + bool m_hasApertureRotOval; // true is at least one oval rotated aperture is in use + bool m_hasApertureRotRect; // true is at least one rect. rotated aperture is in use + bool m_hasApertureOutline4P; // true is at least one 4 corners outline (free polygon + // with 4 corners) aperture is in use + bool m_hasApertureChamferedRect; // true is at least one chamfered rect is in use + // (with no rounded corner) bool m_gerberUnitInch; // true if the gerber units are inches, false for mm int m_gerberUnitFmt; // number of digits in mantissa. diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index f392576907..a9f8a0c1a6 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -256,8 +256,20 @@ void BRDITEMS_PLOTTER::PlotPad( D_PAD* aPad, COLOR4D aColor, EDA_DRAW_MODE_T aPl } break; - default: case PAD_SHAPE_CHAMFERED_RECT: + if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER ) + { + static_cast( m_plotter )->FlashPadChamferRoundRect( + shape_pos, aPad->GetSize(), + aPad->GetRoundRectCornerRadius(), + aPad->GetChamferRectRatio(), + aPad->GetChamferPositions(), + aPad->GetOrientation(), aPlotMode, &gbr_metadata ); + break; + } + KI_FALLTHROUGH; + + default: case PAD_SHAPE_CUSTOM: { const std::shared_ptr& polygons = aPad->GetEffectivePolygon();