From 61f1f7d94852d8091488da48430549390c27690c Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Mon, 5 Oct 2020 20:34:33 +0200 Subject: [PATCH] Gerber, aperture macros: use different apertures primitives in macro defs. To avoid issues with broken Gerber readers use aperture macros with shapes without rotation when more than one primitive is required. In many gerber readers, rotation of a set of primitives is broken (do not follow Gerber requirements) --- common/plotters/GERBER_plotter.cpp | 64 +++++++++++++++---- common/plotters/gbr_plotter_aperture_macros.h | 61 +++++++++--------- gerbview/am_param.cpp | 2 +- gerbview/am_primitive.cpp | 7 +- 4 files changed, 87 insertions(+), 47 deletions(-) diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index 264b43ddfa..daeed7e114 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -40,7 +40,10 @@ // 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 +#define GBR_USE_MACROS_FOR_ROUNDRECT +#define GBR_USE_MACROS_FOR_TRAPEZOID +#define GBR_USE_MACROS_FOR_ROTATED_OVAL +#define GBR_USE_MACROS_FOR_ROTATED_RECT GERBER_PLOTTER::GERBER_PLOTTER() { @@ -293,7 +296,7 @@ bool GERBER_PLOTTER::EndPlot() fputs( APER_MACRO_ROUNDRECT_HEADER, outputFile ); if( m_hasApertureRotOval ) - fputs( APER_MACRO_HORIZ_OVAL_HEADER, outputFile ); + fputs( APER_MACRO_SHAPE_OVAL_HEADER, outputFile ); if( m_hasApertureRotRect ) fputs( APER_MACRO_ROT_RECT_HEADER, outputFile ); @@ -561,9 +564,34 @@ void GERBER_PLOTTER::writeApertureList() break; case APERTURE::AM_ROUND_RECT: // Aperture macro for round rect pads - sprintf( cbuf, "%s,%#fX%#fX%#fX%#f*%%\n", APER_MACRO_ROUNDRECT_NAME, - tool.m_Size.x * fscale, tool.m_Size.y * fscale, - tool.m_Radius * fscale, tool.m_Rotation ); + { + // The aperture macro needs coordinates of the centers of the 4 corners + std::vector corners; + wxSize half_size( tool.m_Size.x/2-tool.m_Radius, tool.m_Size.y/2-tool.m_Radius ); + + corners.emplace_back( -half_size.x, -half_size.y ); + corners.emplace_back( half_size.x, -half_size.y ); + corners.emplace_back( half_size.x, half_size.y ); + corners.emplace_back( -half_size.x, half_size.y ); + + // Rotate the corner coordinates: + for( int ii = 0; ii < 4; ii++ ) + RotatePoint( corners[ii], tool.m_Rotation*10.0 ); + + sprintf( cbuf, "%s,%#fX", APER_MACRO_ROUNDRECT_NAME, + tool.m_Radius * fscale ); + buffer += cbuf; + + // Add each corner + for( int ii = 0; ii < 4; ii++ ) + { + sprintf( cbuf, "%#fX%#fX", + corners[ii].x * fscale, corners[ii].y * fscale ); + buffer += cbuf; + } + + sprintf( cbuf, "0*%%\n" ); + } break; case APERTURE::AM_ROT_RECT: // Aperture macro for rotated rect pads @@ -594,10 +622,20 @@ void GERBER_PLOTTER::writeApertureList() case APERTURE::AM_ROTATED_OVAL: // Aperture macro for rotated oval pads // (not rotated is a primitive) - // m_Size.x = lenght; m_Size.y = width - sprintf( cbuf, "%s,%#fX%#fX%#f*%%\n", APER_MACRO_HORIZ_OVAL_NAME, - tool.m_Size.x * fscale, tool.m_Size.y * fscale, - tool.m_Rotation ); + // m_Size.x = lenght; m_Size.y = width, and the macro aperure expects + // the position of ends + { + VECTOR2I start( tool.m_Size.x/2, 0 ); + VECTOR2I end( -tool.m_Size.x/2, 0 ); + + RotatePoint( start, tool.m_Rotation*10.0 ); + RotatePoint( end, tool.m_Rotation*10.0 ); + + sprintf( cbuf, "%s,%#fX%#fX%#fX%#fX%#fX0*%%\n", APER_MACRO_SHAPE_OVAL_NAME, + tool.m_Size.y * fscale, // width + start.x * fscale, -start.y * fscale, + end.x * fscale, -end.y * fscale ); + } break; } @@ -949,7 +987,7 @@ void GERBER_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, doub { if( trace_mode == FILLED ) { - #ifdef GBR_USE_MACROS + #ifdef GBR_USE_MACROS_FOR_ROTATED_OVAL m_hasApertureRotOval = true; // We are using a aperture macro that expect size.y < size.x // i.e draw a horizontal line for rotation = 0.0 @@ -1041,7 +1079,7 @@ void GERBER_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& aSize, break; default: - #ifdef GBR_USE_MACROS + #ifdef GBR_USE_MACROS_FOR_ROTATED_RECT if( trace_mode != SKETCH ) { m_hasApertureRotRect = true; @@ -1112,7 +1150,7 @@ void GERBER_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aS } else { - #ifdef GBR_USE_MACROS + #ifdef GBR_USE_MACROS_FOR_ROUNDRECT m_hasApertureRoundRect = true; DPOINT pos_dev = userToDeviceCoordinates( aPadPos ); @@ -1353,7 +1391,7 @@ void GERBER_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCo if( aTrace_Mode == SKETCH ) PlotPoly( cornerList, NO_FILL, GetCurrentLineWidth(), &metadata ); else - #ifdef GBR_USE_MACROS + #ifdef GBR_USE_MACROS_FOR_TRAPEZOID { m_hasApertureOutline = true; DPOINT pos_dev = userToDeviceCoordinates( aPadPos ); diff --git a/common/plotters/gbr_plotter_aperture_macros.h b/common/plotters/gbr_plotter_aperture_macros.h index f6facc3e8d..da65a659be 100644 --- a/common/plotters/gbr_plotter_aperture_macros.h +++ b/common/plotters/gbr_plotter_aperture_macros.h @@ -26,27 +26,28 @@ #pragma once // A aperture macro to define a rounded rect pad shape +// In many gerber readers, the rotation of the full shape is broken +// so we are using primitives that does not need a rotation #define APER_MACRO_ROUNDRECT_NAME "RoundRect" #define APER_MACRO_ROUNDRECT_HEADER \ "%AMRoundRect*\n\ -0 Rectangle with rounded corners, with rotation*\n\ -0 The origin of the aperture is its center*\n\ -0 $1 X-size*\n\ -0 $2 Y-size*\n\ -0 $3 Rounding radius*\n\ -0 $4 Rotation angle, in degrees counterclockwise*\n\ -0 Add two overlapping rectangle primitives as box body*\n\ -21,1,$1,$2-$3-$3,0,0,$4*\n\ -21,1,$1-$3-$3,$2,0,0,$4*\n\ +0 Rectangle with rounded corners*\n\ +0 $1 Rounding radius*\n\ +0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*\n\ +0 Add a 4 corners polygon primitive as box body*\n\ +4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*\n\ 0 Add four circle primitives for the rounded corners*\n\ -$5=$1/2*\n\ -$6=$2/2*\n\ -$7=2x$3*\n\ -1,1,$7,$5-$3,$6-$3,$4*\n\ -1,1,$7,-$5+$3,$6-$3,$4*\n\ -1,1,$7,-$5+$3,-$6+$3,$4*\n\ -1,1,$7,$5-$3,-$6+$3,$4*%\n" +1,1,$1+$1,$2,$3,0*\n\ +1,1,$1+$1,$4,$5,0*\n\ +1,1,$1+$1,$6,$7,0*\n\ +1,1,$1+$1,$8,$9,0*\n\ +0 Add four rect primitives between the rounded corners*\n\ +20,1,$1+$1,$2,$3,$4,$5,0*\n\ +20,1,$1+$1,$4,$5,$6,$7,0*\n\ +20,1,$1+$1,$6,$7,$8,$9,0*\n\ +20,1,$1+$1,$8,$9,$2,$3,0*\ +%\n" // A aperture macro to define a rotated rect pad shape #define APER_MACRO_ROT_RECT_NAME "RotRect" @@ -63,23 +64,25 @@ $7=2x$3*\n\ // A aperture macro to define a oval pad shape -#define APER_MACRO_HORIZ_OVAL_NAME "HorizOval" +// In many gerber readers, the rotation of the full shape is broken +// so we are using a primitive that does not need a rotation to be +// plotted +#define APER_MACRO_SHAPE_OVAL_NAME "HorizOval" -#define APER_MACRO_HORIZ_OVAL_HEADER \ +#define APER_MACRO_SHAPE_OVAL_HEADER \ "%AMHorizOval*\n\ -0 Thick line with rounded ends, with rotation*\n\ -0 The origin of the aperture is its center*\n\ -0 $1 length (X dim)*\n\ -0 $2 width (Y dim)*\n\ -0 $3 Rotation angle, in degrees counterclockwise*\n\ -0 Add horizontal line*\n\ -21,1,$1-$2,$2,0,0,$3*\n\ -0 Add two circle primitives for the rounded ends*\n\ -$4=($1-$2)/2*\n\ -1,1,$2,$4,0,$3*\n\ -1,1,$2,-$4,0,$3*%\n" +0 Thick line with rounded ends*\n\ +0 $1 width*\n\ +0 $2 $3 position (X,Y) of the first rounded end (center of the circle)*\n\ +0 $4 $5 position (X,Y) of the second rounded end (center of the circle)*\n\ +0 Add line between two ends*\n\ +20,1,$1,$2,$3,$4,$5,0*\n\ +0 Add two circle primitives to create the rounded ends*\n\ +1,1,$1,$2,$3,0*\n\ +1,1,$1,$4,$5,0*%\n" // A aperture macro to define a trapezoid (polygon) by 4 corners +// and a rotation angle #define APER_MACRO_OUTLINE4P_NAME "Outline4P" #define APER_MACRO_OUTLINE4P_HEADER \ diff --git a/gerbview/am_param.cpp b/gerbview/am_param.cpp index 7e83b20512..0cd356e5d9 100644 --- a/gerbview/am_param.cpp +++ b/gerbview/am_param.cpp @@ -29,7 +29,7 @@ #include #include -#include +//#include #include diff --git a/gerbview/am_primitive.cpp b/gerbview/am_primitive.cpp index e8cdff3889..37145c49e9 100644 --- a/gerbview/am_primitive.cpp +++ b/gerbview/am_primitive.cpp @@ -35,8 +35,6 @@ #include #include -#include - /** * Function scaletoIU @@ -281,8 +279,8 @@ void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent, case AMP_MOIRE: { - /* Moir�, Primitive Code 6 - * The moir� primitive is a cross hair centered on concentric rings (annuli). + /* Moire, Primitive Code 6 + * The moire primitive is a cross hair centered on concentric rings (annuli). * Exposure is always on. */ curPos += mapPt( params[0].GetValue( tool ), params[1].GetValue( tool ), @@ -365,6 +363,7 @@ void AM_PRIMITIVE::DrawBasicShape( const GERBER_DRAW_ITEM* aParent, // * the polygon is always closed, // * therefore the last XY coordinate is the same as the first int prm_idx = 2; // params[2] is the first X coordinate + for( int i = 0; i <= numCorners; ++i ) { pos.x = scaletoIU( params[prm_idx].GetValue( tool ), m_GerbMetric );