diff --git a/common/plotters/DXF_plotter.cpp b/common/plotters/DXF_plotter.cpp index 96f5da8b07..cf2072417c 100644 --- a/common/plotters/DXF_plotter.cpp +++ b/common/plotters/DXF_plotter.cpp @@ -30,6 +30,7 @@ #include #include #include +#include /** * Oblique angle for DXF native text @@ -146,6 +147,26 @@ void DXF_PLOTTER::SetUnits( DXF_UNITS aUnit ) } +// convert aValue to a string, and remove trailing zeros +// In DXF files coordinates need a high precision: at least 9 digits when given +// in inches and 7 digits when in mm. +// So we use 16 digits and remove trailing 0 (if any) +static std::string formatCoord( double aValue ) +{ + std::string buf; + + buf = fmt::format( "{:.16f}", aValue ); + + // remove trailing zeros + while( !buf.empty() && buf[buf.size() - 1] == '0' ) + { + buf.pop_back(); + } + + return buf; +} + + void DXF_PLOTTER::SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil, double aScale, bool aMirror ) { @@ -437,22 +458,28 @@ void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int if( fill == FILL_T::NO_FILL ) { - fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n", + fprintf( m_outputFile, "0\nCIRCLE\n8\n%s\n10\n%s\n20\n%s\n40\n%s\n", TO_UTF8( cname ), - centre_dev.x, centre_dev.y, radius ); + formatCoord( centre_dev.x ).c_str(), + formatCoord( centre_dev.y ).c_str(), + formatCoord( radius ).c_str() ); } else if( fill == FILL_T::FILLED_SHAPE ) { double r = radius*0.5; fprintf( m_outputFile, "0\nPOLYLINE\n" ); fprintf( m_outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname ) ); - fprintf( m_outputFile, "40\n%g\n41\n%g\n", radius, radius); + fprintf( m_outputFile, "40\n%s\n41\n%s\n", + formatCoord( radius ).c_str(), + formatCoord( radius ).c_str() ); fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) ); - fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", - centre_dev.x-r, centre_dev.y ); + fprintf( m_outputFile, "10\n%s\n 20\n%s\n42\n1.0\n", + formatCoord( centre_dev.x-r ).c_str(), + formatCoord( centre_dev.y ).c_str() ); fprintf( m_outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ) ); - fprintf( m_outputFile, "10\n%g\n 20\n%g\n42\n1.0\n", - centre_dev.x+r, centre_dev.y ); + fprintf( m_outputFile, "10\n%s\n 20\n%s\n42\n1.0\n", + formatCoord( centre_dev.x+r ).c_str(), + formatCoord( centre_dev.y ).c_str() ); fprintf( m_outputFile, "0\nSEQEND\n"); } } @@ -578,9 +605,12 @@ void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume ) // DXF LINE wxString cname = getDXFColorName( m_currentColor ); const char* lname = getDXFLineType( static_cast( m_currentLineType ) ); - fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n", + fprintf( m_outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%s\n20\n%s\n11\n%s\n21\n%s\n", TO_UTF8( cname ), lname, - pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y ); + formatCoord( pen_lastpos_dev.x ).c_str(), + formatCoord( pen_lastpos_dev.y ).c_str(), + formatCoord( pos_dev.x ).c_str(), + formatCoord( pos_dev.y ).c_str() ); } m_penLastpos = pos; @@ -626,8 +656,8 @@ void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, in } -void DXF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { wxASSERT( m_outputFile ); @@ -648,9 +678,11 @@ void DXF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, // Emit a DXF ARC entity wxString cname = getDXFColorName( m_currentColor ); fprintf( m_outputFile, - "0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n", + "0\nARC\n8\n%s\n10\n%s\n20\n%s\n40\n%s\n50\n%.8f\n51\n%.8f\n", TO_UTF8( cname ), - centre_device.x, centre_device.y, radius_device, + formatCoord( centre_device.x ).c_str(), + formatCoord( centre_device.y ).c_str(), + formatCoord( radius_device ).c_str(), startAngle.AsDegrees(), endAngle.AsDegrees() ); } @@ -936,21 +968,21 @@ void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor " 8\n" "%s\n" // Layer name " 10\n" - "%g\n" // First point X + "%s\n" // First point X " 11\n" - "%g\n" // Second point X + "%s\n" // Second point X " 20\n" - "%g\n" // First point Y + "%s\n" // First point Y " 21\n" - "%g\n" // Second point Y + "%s\n" // Second point Y " 40\n" - "%g\n" // Text height + "%s\n" // Text height " 41\n" - "%g\n" // Width factor + "%s\n" // Width factor " 50\n" - "%g\n" // Rotation + "%.8f\n" // Rotation " 51\n" - "%g\n" // Oblique angle + "%.8f\n" // Oblique angle " 71\n" "%d\n" // Mirror flags " 72\n" @@ -960,9 +992,9 @@ void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor aAttributes.m_Bold ? (aAttributes.m_Italic ? "KICADBI" : "KICADB") : (aAttributes.m_Italic ? "KICADI" : "KICAD"), TO_UTF8( cname ), - origin_dev.x, origin_dev.x, - origin_dev.y, origin_dev.y, - size_dev.y, fabs( size_dev.x / size_dev.y ), + formatCoord( origin_dev.x ).c_str(), formatCoord( origin_dev.x ).c_str(), + formatCoord( origin_dev.y ).c_str(), formatCoord( origin_dev.y ).c_str(), + formatCoord( size_dev.y ).c_str(), formatCoord( fabs( size_dev.x / size_dev.y ) ).c_str(), aAttributes.m_Angle.AsDegrees(), aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0, aAttributes.m_Mirrored ? 2 : 0, // X mirror flag diff --git a/common/plotters/GERBER_plotter.cpp b/common/plotters/GERBER_plotter.cpp index 595345119e..d82f5480f5 100644 --- a/common/plotters/GERBER_plotter.cpp +++ b/common/plotters/GERBER_plotter.cpp @@ -817,8 +817,8 @@ void GERBER_PLOTTER::Circle( const VECTOR2I& aCenter, int aDiameter, FILL_T aFil -void GERBER_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void GERBER_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { SetCurrentLineWidth( aWidth ); @@ -1140,8 +1140,8 @@ void GERBER_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, i } } -void GERBER_PLOTTER::ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, int aWidth, +void GERBER_PLOTTER::ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, int aWidth, OUTLINE_MODE aTraceMode, void* aData ) { GBR_METADATA *gbr_metadata = static_cast( aData ); diff --git a/common/plotters/HPGL_plotter.cpp b/common/plotters/HPGL_plotter.cpp index 24ce9866a5..81633698a4 100644 --- a/common/plotters/HPGL_plotter.cpp +++ b/common/plotters/HPGL_plotter.cpp @@ -567,8 +567,8 @@ void HPGL_PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, } -void HPGL_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void HPGL_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { if( aRadius <= 0 ) return; diff --git a/common/plotters/PDF_plotter.cpp b/common/plotters/PDF_plotter.cpp index 79a0854ad2..23dcc1e7ab 100644 --- a/common/plotters/PDF_plotter.cpp +++ b/common/plotters/PDF_plotter.cpp @@ -338,8 +338,8 @@ void PDF_PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VE } -void PDF_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void PDF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { wxASSERT( m_workFile ); diff --git a/common/plotters/SVG_plotter.cpp b/common/plotters/SVG_plotter.cpp index b1b67e86ca..311ff404af 100644 --- a/common/plotters/SVG_plotter.cpp +++ b/common/plotters/SVG_plotter.cpp @@ -443,8 +443,8 @@ void SVG_PLOTTER::Circle( const VECTOR2I& pos, int diametre, FILL_T fill, int wi } -void SVG_PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void SVG_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { /* Draws an arc of a circle, centered on (xc,yc), with starting point (x1, y1) and ending * at (x2, y2). The current pen is used for the outline and the current brush for filling diff --git a/common/plotters/plotter.cpp b/common/plotters/plotter.cpp index 52e577a177..43c5d50dbe 100644 --- a/common/plotters/plotter.cpp +++ b/common/plotters/plotter.cpp @@ -145,13 +145,31 @@ double PLOTTER::GetDashGapLenIU( int aLineWidth ) const return userToDeviceSize( m_renderSettings->GetGapLength( aLineWidth ) ); } - +#include void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR2I& aEnd, FILL_T aFill, int aWidth, int aMaxError ) { - EDA_ANGLE startAngle( aStart - aCenter ); - EDA_ANGLE endAngle( aEnd - aCenter ); - int radius = ( aStart - aCenter ).EuclideanNorm(); + // Recalculate aCenter using double to be sure we will use a exact value, from aStart and aEnd + // it must be on the line passing by the middle of segment {aStart, aEnd} + // To simplify calculations, use aStart as origin in intermediate calculations + VECTOR2D center = aCenter - aStart; + VECTOR2D end = aEnd - aStart; + EDA_ANGLE segAngle( end ); + + // Rotate end and center, to make segment {aStart, aEnd} horizontal + RotatePoint( end, segAngle ); + RotatePoint( center, segAngle ); + + // center.x must be at end.x/2 coordinate + center.x = end.x / 2; + + // Now calculate the right center position + RotatePoint( center, -segAngle ); + center += aStart; + + EDA_ANGLE startAngle( VECTOR2D( aStart ) - center ); + EDA_ANGLE endAngle( VECTOR2D( aEnd ) - center ); + double radius = ( VECTOR2D( aStart ) - center ).EuclideanNorm(); if( startAngle > endAngle ) { @@ -168,12 +186,12 @@ void PLOTTER::Arc( const VECTOR2I& aCenter, const VECTOR2I& aStart, const VECTOR startAngle = -startAngle; endAngle = -endAngle; - Arc( aCenter, startAngle, endAngle, radius, aFill, aWidth ); + Arc( center, startAngle, endAngle, radius, aFill, aWidth ); } -void PLOTTER::Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, int aWidth ) +void PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth ) { EDA_ANGLE startAngle( aStartAngle ); EDA_ANGLE endAngle( aEndAngle ); @@ -570,8 +588,8 @@ void PLOTTER::ThickSegment( const VECTOR2I& start, const VECTOR2I& end, int widt } -void PLOTTER::ThickArc( const VECTOR2I& centre, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, int aWidth, +void PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, int aWidth, OUTLINE_MODE aTraceMode, void* aData ) { if( aTraceMode == FILLED ) diff --git a/include/plotters/plotter.h b/include/plotters/plotter.h index 22584b01fe..c64b26e255 100644 --- a/include/plotters/plotter.h +++ b/include/plotters/plotter.h @@ -550,13 +550,19 @@ public: protected: /** * Generic fallback: arc rendered as a polyline. + * Note also aCentre and aRadius are double to avoid creating rounding issues due + * to the fact a arc is defined in Kicad by a start point, a end point and third point + * not angles and radius. + * In some plotters (i.e. dxf) whe need a good precision when calculating an arc + * without error introduced by rounding, to avoid moving the end points, + * usually important in outlines when plotting an arc given by center, radius and angles */ - virtual void Arc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, + virtual void Arc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ); - virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& StAngle, - const EDA_ANGLE& EndAngle, int aRadius, int aWidth, + virtual void ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& StAngle, + const EDA_ANGLE& EndAngle, double aRadius, int aWidth, OUTLINE_MODE aTraceMode, void* aData ); // These are marker subcomponents diff --git a/include/plotters/plotter_dxf.h b/include/plotters/plotter_dxf.h index fe75764889..f557cc625d 100644 --- a/include/plotters/plotter_dxf.h +++ b/include/plotters/plotter_dxf.h @@ -211,8 +211,8 @@ public: } protected: - virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, + virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; void plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor, diff --git a/include/plotters/plotter_gerber.h b/include/plotters/plotter_gerber.h index d60c78c7ca..f4d768a45c 100644 --- a/include/plotters/plotter_gerber.h +++ b/include/plotters/plotter_gerber.h @@ -272,12 +272,12 @@ public: APERTURE::APERTURE_TYPE aType, int aApertureAttribute ); protected: - virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, + virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; - virtual void ThickArc( const VECTOR2I& aCentre, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, int aWidth, + virtual void ThickArc( const VECTOR2D& aCentre, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, int aWidth, OUTLINE_MODE aTraceMode, void* aData ) override; /** diff --git a/include/plotters/plotter_hpgl.h b/include/plotters/plotter_hpgl.h index 4e53f6f7a4..8df1a8e9b7 100644 --- a/include/plotters/plotter_hpgl.h +++ b/include/plotters/plotter_hpgl.h @@ -145,8 +145,8 @@ protected: * aEndAngle is end angle the arc. * Radius is the radius of the arc. */ - virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, FILL_T aFill, + virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; /** diff --git a/include/plotters/plotters_pslike.h b/include/plotters/plotters_pslike.h index 89fadab94a..8a522a68d3 100644 --- a/include/plotters/plotters_pslike.h +++ b/include/plotters/plotters_pslike.h @@ -423,8 +423,8 @@ protected: OUTLINE_NODE* addOutlineNode( OUTLINE_NODE* aParent, int aActionHandle, const wxString& aTitle ); - virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, + virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; /// convert a wxString unicode string to a char string compatible with the accepted @@ -625,8 +625,8 @@ public: void* aData = nullptr ) override; protected: - virtual void Arc( const VECTOR2I& aCenter, const EDA_ANGLE& aStartAngle, - const EDA_ANGLE& aEndAngle, int aRadius, + virtual void Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle, + const EDA_ANGLE& aEndAngle, double aRadius, FILL_T aFill, int aWidth = USE_DEFAULT_LINE_WIDTH ) override; /**