Plotters (especially DXF) add more precision when plotting arcs (and others)
- in DXF coordinates were using 6 digits for coordinate mantissa: this is not enough for coord in inches. Now use 16 digits - Arc( VECTOR2I& aCentre, EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle, ...) was using integers for coord. This creates significant errors for start point and end points of the arc. Now the center is given in double, and its position is calculated from angle end points (and radius) to do not generate a position error for these end points (previously the error could be 20 ... 50 nm) Fixes #15056 https://gitlab.com/kicad/code/kicad/-/issues/15056
This commit is contained in:
parent
efb452df37
commit
88ffcec4b5
|
@ -30,6 +30,7 @@
|
|||
#include <string_utils.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <trigo.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
/**
|
||||
* 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<PLOT_DASH_TYPE>( 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
|
||||
|
|
|
@ -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<GBR_METADATA*>( aData );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -145,13 +145,31 @@ double PLOTTER::GetDashGapLenIU( int aLineWidth ) const
|
|||
return userToDeviceSize( m_renderSettings->GetGapLength( aLineWidth ) );
|
||||
}
|
||||
|
||||
|
||||
#include <wx/log.h>
|
||||
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 )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue