Support DXF ellipses and elliptical arcs
Fixes https://gitlab.com/kicad/code/kicad/-/issues/12563
This commit is contained in:
parent
13b73ed6b4
commit
b85fab9ab6
|
@ -5,6 +5,7 @@ set( KIMATH_SRCS
|
||||||
src/trigo.cpp
|
src/trigo.cpp
|
||||||
|
|
||||||
src/geometry/eda_angle.cpp
|
src/geometry/eda_angle.cpp
|
||||||
|
src/geometry/ellipse.cpp
|
||||||
src/geometry/circle.cpp
|
src/geometry/circle.cpp
|
||||||
src/geometry/convex_hull.cpp
|
src/geometry/convex_hull.cpp
|
||||||
src/geometry/direction_45.cpp
|
src/geometry/direction_45.cpp
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <math/vector2d.h>
|
#include <math/vector2d.h>
|
||||||
|
|
||||||
|
template <typename T> class ELLIPSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bezier curves to polygon converter.
|
* Bezier curves to polygon converter.
|
||||||
*
|
*
|
||||||
|
@ -65,4 +67,36 @@ private:
|
||||||
std::vector<VECTOR2D> m_ctrlPts;
|
std::vector<VECTOR2D> m_ctrlPts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Refactor BEZIER_POLY to use BEZIER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic cubic Bezier representation
|
||||||
|
*/
|
||||||
|
template <typename NumericType>
|
||||||
|
class BEZIER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BEZIER() = default;
|
||||||
|
|
||||||
|
BEZIER( VECTOR2<NumericType> aStart, VECTOR2<NumericType> aC1, VECTOR2<NumericType> aC2,
|
||||||
|
VECTOR2<NumericType> aEnd ) :
|
||||||
|
Start( aStart ),
|
||||||
|
C1( aC1 ),
|
||||||
|
C2( aC2 ),
|
||||||
|
End( aEnd )
|
||||||
|
{}
|
||||||
|
|
||||||
|
VECTOR2<NumericType> Start;
|
||||||
|
VECTOR2<NumericType> C1;
|
||||||
|
VECTOR2<NumericType> C2;
|
||||||
|
VECTOR2<NumericType> End;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms an ellipse or elliptical arc into a set of quadratic Bezier curves that approximate it
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void TransformEllipseToBeziers( const ELLIPSE<T>& aEllipse, std::vector<BEZIER<T>>& aBeziers );
|
||||||
|
|
||||||
#endif // BEZIER_CURVES_H
|
#endif // BEZIER_CURVES_H
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KICAD_ELLIPSE_H
|
||||||
|
#define KICAD_ELLIPSE_H
|
||||||
|
|
||||||
|
#include <math/vector2d.h>
|
||||||
|
#include <geometry/eda_angle.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class was created to handle importing ellipses from other file formats that support them
|
||||||
|
* natively. The storage format and API may need to be refactored before this is used as part of a
|
||||||
|
* potential future native KiCad ellipse.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename NumericType>
|
||||||
|
class ELLIPSE
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ELLIPSE() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an ellipse or elliptical arc. The ellipse sweeps from aStartAngle to aEndAngle
|
||||||
|
* in a counter-clockwise direction.
|
||||||
|
*
|
||||||
|
* @param aCenter is the center point of the ellipse.
|
||||||
|
* @param aMajorRadius is the radius of the "x-axis" dimension of the ellipse.
|
||||||
|
* @param aMinorRadius is the radius of the "y-axis" dimension of the ellipse.
|
||||||
|
* @param aRotation is the angle of the ellipse "x-axis" relative to world x-axis.
|
||||||
|
* @param aStartAngle is the starting angle of the elliptical arc.
|
||||||
|
* @param aEndAngle is the ending angle of the elliptical arc.
|
||||||
|
*/
|
||||||
|
ELLIPSE( const VECTOR2<NumericType>& aCenter, NumericType aMajorRadius,
|
||||||
|
NumericType aMinorRadius, EDA_ANGLE aRotation, EDA_ANGLE aStartAngle = ANGLE_0,
|
||||||
|
EDA_ANGLE aEndAngle = FULL_CIRCLE );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a DXF-style ellipse or elliptical arc, where the major axis is given by a point
|
||||||
|
* rather than a radius, and therefore defines not only the major radius but also the rotation
|
||||||
|
* of the ellipse.
|
||||||
|
*
|
||||||
|
* @param aCenter is the center point of the ellipse.
|
||||||
|
* @param aMajor is the endpoint of the major axis, relative to the center.
|
||||||
|
* @param aRatio is the ratio of the minor axis length to the major axis length.
|
||||||
|
* @param aStartAngle is the starting angle of the elliptical arc.
|
||||||
|
* @param aEndAngle is the ending angle of the elliptical arc.
|
||||||
|
*/
|
||||||
|
ELLIPSE( const VECTOR2<NumericType>& aCenter, const VECTOR2<NumericType>& aMajor, double aRatio,
|
||||||
|
EDA_ANGLE aStartAngle = ANGLE_0, EDA_ANGLE aEndAngle = FULL_CIRCLE );
|
||||||
|
|
||||||
|
VECTOR2<NumericType> Center;
|
||||||
|
NumericType MajorRadius;
|
||||||
|
NumericType MinorRadius;
|
||||||
|
EDA_ANGLE Rotation;
|
||||||
|
EDA_ANGLE StartAngle;
|
||||||
|
EDA_ANGLE EndAngle;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //KICAD_ELLIPSE_H
|
|
@ -26,6 +26,8 @@
|
||||||
/************************************/
|
/************************************/
|
||||||
|
|
||||||
#include <bezier_curves.h>
|
#include <bezier_curves.h>
|
||||||
|
#include <geometry/ellipse.h>
|
||||||
|
#include <trigo.h>
|
||||||
#include <math/vector2d.h> // for VECTOR2D, operator*, VECTOR2
|
#include <math/vector2d.h> // for VECTOR2D, operator*, VECTOR2
|
||||||
#include <wx/debug.h> // for wxASSERT
|
#include <wx/debug.h> // for wxASSERT
|
||||||
|
|
||||||
|
@ -105,3 +107,99 @@ void BEZIER_POLY::GetPoly( std::vector<VECTOR2D>& aOutput, double aMinSegLen, in
|
||||||
if( aOutput.back() != m_ctrlPts[3] )
|
if( aOutput.back() != m_ctrlPts[3] )
|
||||||
aOutput.push_back( m_ctrlPts[3] );
|
aOutput.push_back( m_ctrlPts[3] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void TransformEllipseToBeziers( const ELLIPSE<T>& aEllipse, std::vector<BEZIER<T>>& aBeziers )
|
||||||
|
{
|
||||||
|
EDA_ANGLE arcAngle = -( aEllipse.EndAngle - aEllipse.StartAngle );
|
||||||
|
|
||||||
|
if( arcAngle >= ANGLE_0 )
|
||||||
|
arcAngle -= ANGLE_360;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* KiCad does not natively support ellipses or elliptical arcs. So, we convert them to Bezier
|
||||||
|
* splines as these are the nearest thing we have that represents them in a way that is both
|
||||||
|
* editable and preserves their curvature accurately (enough).
|
||||||
|
*
|
||||||
|
* Credit to Kliment for developing and documenting this method.
|
||||||
|
*/
|
||||||
|
/// Minimum number of Beziers to use for a full circle to keep error manageable.
|
||||||
|
const int minBeziersPerCircle = 4;
|
||||||
|
|
||||||
|
/// The number of Beziers needed for the given arc
|
||||||
|
const int numBeziers = std::ceil( std::abs( arcAngle.AsRadians() /
|
||||||
|
( 2 * M_PI / minBeziersPerCircle ) ) );
|
||||||
|
|
||||||
|
/// Angle occupied by each Bezier
|
||||||
|
const double angleIncrement = arcAngle.AsRadians() / numBeziers;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, let's assume a circle of radius 1, centered on origin, with angle startangle
|
||||||
|
* x-axis-aligned. We'll move, scale, and rotate it later. We're creating Bezier curves that hug
|
||||||
|
* this circle as closely as possible, with the angles that will be used on the final ellipse
|
||||||
|
* too.
|
||||||
|
*
|
||||||
|
* Thanks to the beautiful and excellent https://pomax.github.io/bezierinfo we know how to
|
||||||
|
* define a curve that hugs a circle as closely as possible.
|
||||||
|
*
|
||||||
|
* We need the value k, which is the optimal distance from the endpoint to the control point to
|
||||||
|
* make the curve match the circle for a given circle arc angle.
|
||||||
|
*
|
||||||
|
* k = 4/3 * tan(θ/4), where θ is the angle of the arc. In our case, θ=angleIncrement
|
||||||
|
*/
|
||||||
|
double theta = angleIncrement;
|
||||||
|
double k = ( 4. / 3. ) * std::tan( theta / 4 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define our Bezier:
|
||||||
|
* - Start point is on the circle at the x-axis
|
||||||
|
* - First control point just uses k as the y-value
|
||||||
|
* - Second control point is offset from the end point
|
||||||
|
* - End point is defined by the angle of the arc segment
|
||||||
|
* Note that we use double here no matter what the template param is; round at the end only.
|
||||||
|
*/
|
||||||
|
BEZIER<double> first = { { 1, 0 },
|
||||||
|
{ 1, k },
|
||||||
|
{ std::cos( theta ) + k * std::sin( theta ),
|
||||||
|
std::sin( theta ) - k * std::cos( theta ) },
|
||||||
|
{ std::cos( theta ), std::sin( theta ) } };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now construct the actual segments by transforming/rotating the first one
|
||||||
|
*/
|
||||||
|
auto transformPoint =
|
||||||
|
[&]( VECTOR2D aPoint, const double aAngle ) -> VECTOR2D
|
||||||
|
{
|
||||||
|
// Bring to the actual starting angle
|
||||||
|
RotatePoint( aPoint,
|
||||||
|
-EDA_ANGLE( aAngle - aEllipse.StartAngle.AsRadians(), RADIANS_T ) );
|
||||||
|
|
||||||
|
// Then scale to the major and minor radiuses of the ellipse
|
||||||
|
aPoint *= VECTOR2D( aEllipse.MajorRadius, aEllipse.MinorRadius );
|
||||||
|
|
||||||
|
// Now rotate to the ellipse coordinate system
|
||||||
|
RotatePoint( aPoint, -aEllipse.Rotation );
|
||||||
|
|
||||||
|
// And finally offset to the center location of the ellipse
|
||||||
|
aPoint += aEllipse.Center;
|
||||||
|
|
||||||
|
return aPoint;
|
||||||
|
};
|
||||||
|
|
||||||
|
for( int i = 0; i < numBeziers; i++ )
|
||||||
|
{
|
||||||
|
aBeziers.emplace_back( BEZIER<T>( {
|
||||||
|
transformPoint( first.Start, i * angleIncrement ),
|
||||||
|
transformPoint( first.C1, i * angleIncrement ),
|
||||||
|
transformPoint( first.C2, i * angleIncrement ),
|
||||||
|
transformPoint( first.End, i * angleIncrement )
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template void TransformEllipseToBeziers( const ELLIPSE<double>& aEllipse,
|
||||||
|
std::vector<BEZIER<double>>& aBeziers );
|
||||||
|
template void TransformEllipseToBeziers( const ELLIPSE<int>& aEllipse,
|
||||||
|
std::vector<BEZIER<int>>& aBeziers );
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <geometry/ellipse.h>
|
||||||
|
|
||||||
|
|
||||||
|
template <typename NumericType>
|
||||||
|
ELLIPSE<NumericType>::ELLIPSE( const VECTOR2<NumericType>& aCenter, NumericType aMajorRadius,
|
||||||
|
NumericType aMinorRadius, EDA_ANGLE aRotation, EDA_ANGLE aStartAngle,
|
||||||
|
EDA_ANGLE aEndAngle ) :
|
||||||
|
Center( aCenter ),
|
||||||
|
MajorRadius( aMajorRadius ),
|
||||||
|
MinorRadius( aMinorRadius ),
|
||||||
|
Rotation( aRotation ),
|
||||||
|
StartAngle( aStartAngle ),
|
||||||
|
EndAngle( aEndAngle )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename NumericType>
|
||||||
|
ELLIPSE<NumericType>::ELLIPSE( const VECTOR2<NumericType>& aCenter,
|
||||||
|
const VECTOR2<NumericType>& aMajor, double aRatio,
|
||||||
|
EDA_ANGLE aStartAngle, EDA_ANGLE aEndAngle ) :
|
||||||
|
Center( aCenter ),
|
||||||
|
StartAngle( aStartAngle ),
|
||||||
|
EndAngle( aEndAngle )
|
||||||
|
{
|
||||||
|
MajorRadius = aMajor.EuclideanNorm();
|
||||||
|
MinorRadius = MajorRadius * aRatio;
|
||||||
|
Rotation = EDA_ANGLE( std::atan2( aMajor.y, aMajor.x ), RADIANS_T );
|
||||||
|
}
|
||||||
|
|
||||||
|
template class ELLIPSE<double>;
|
||||||
|
template class ELLIPSE<int>;
|
|
@ -32,6 +32,8 @@
|
||||||
#include "dxf_import_plugin.h"
|
#include "dxf_import_plugin.h"
|
||||||
#include <wx/arrstr.h>
|
#include <wx/arrstr.h>
|
||||||
#include <wx/regex.h>
|
#include <wx/regex.h>
|
||||||
|
#include <geometry/ellipse.h>
|
||||||
|
#include <bezier_curves.h>
|
||||||
|
|
||||||
#include <trigo.h>
|
#include <trigo.h>
|
||||||
#include <macros.h>
|
#include <macros.h>
|
||||||
|
@ -561,6 +563,72 @@ void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DXF_IMPORT_PLUGIN::addEllipse( const DL_EllipseData& aData )
|
||||||
|
{
|
||||||
|
DXF_ARBITRARY_AXIS arbAxis = getArbitraryAxis( getExtrusion() );
|
||||||
|
VECTOR3D centerCoords = ocsToWcs( arbAxis, VECTOR3D( aData.cx, aData.cy, aData.cz ) );
|
||||||
|
VECTOR3D majorCoords = ocsToWcs( arbAxis, VECTOR3D( aData.mx, aData.my, aData.mz ) );
|
||||||
|
|
||||||
|
// DXF ellipses store the minor axis length as a ratio to the major axis.
|
||||||
|
// The major coords are relative to the center point.
|
||||||
|
// For now, we assume ellipses in the XY plane.
|
||||||
|
|
||||||
|
VECTOR2D center( mapX( centerCoords.x ), mapY( centerCoords.y ) );
|
||||||
|
VECTOR2D major( mapX( majorCoords.x ), mapY( majorCoords.y ) );
|
||||||
|
|
||||||
|
// DXF elliptical arcs store their angles in radians (unlike circular arcs which use degrees)
|
||||||
|
// The arcs wind CCW as in KiCad. The end angle must be greater than the start angle, and if
|
||||||
|
// the extrusion direction is negative, the arc winding is CW instead! Note that this is a
|
||||||
|
// simplification that assumes the DXF is representing a 2D drawing, and would need to be
|
||||||
|
// revisited if we want to import true 3D drawings and "flatten" them to the 2D KiCad plane
|
||||||
|
// internally.
|
||||||
|
EDA_ANGLE startAngle( aData.angle1, RADIANS_T );
|
||||||
|
EDA_ANGLE endAngle( aData.angle2, RADIANS_T );
|
||||||
|
|
||||||
|
if( startAngle > endAngle )
|
||||||
|
endAngle += ANGLE_360;
|
||||||
|
|
||||||
|
// TODO: testcases for negative extrusion vector; handle it here
|
||||||
|
|
||||||
|
if( aData.ratio == 1.0 )
|
||||||
|
{
|
||||||
|
double radius = major.EuclideanNorm();
|
||||||
|
|
||||||
|
if( startAngle == endAngle )
|
||||||
|
{
|
||||||
|
DL_CircleData circle( aData.cx, aData.cy, aData.cz, radius );
|
||||||
|
addCircle( circle );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DL_ArcData arc( aData.cx, aData.cy, aData.cz, radius,
|
||||||
|
startAngle.AsDegrees(), endAngle.AsDegrees() );
|
||||||
|
addArc( arc );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BEZIER<double>> splines;
|
||||||
|
ELLIPSE<double> ellipse( center, major, aData.ratio, startAngle, endAngle );
|
||||||
|
|
||||||
|
TransformEllipseToBeziers( ellipse, splines );
|
||||||
|
|
||||||
|
DXF_IMPORT_LAYER* layer = getImportLayer( attributes.getLayer() );
|
||||||
|
double lineWidth = lineWeightToWidth( attributes.getWidth(), layer );
|
||||||
|
|
||||||
|
GRAPHICS_IMPORTER_BUFFER* bufferToUse = m_currentBlock ? &m_currentBlock->m_buffer
|
||||||
|
: &m_internalImporter;
|
||||||
|
|
||||||
|
for( const BEZIER<double>& b : splines )
|
||||||
|
bufferToUse->AddSpline( b.Start, b.C1, b.C2, b.End, lineWidth );
|
||||||
|
|
||||||
|
// Naive bounding
|
||||||
|
updateImageLimits( center + major );
|
||||||
|
updateImageLimits( center - major );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
|
void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData )
|
||||||
{
|
{
|
||||||
DXF_ARBITRARY_AXIS arbAxis = getArbitraryAxis( getExtrusion() );
|
DXF_ARBITRARY_AXIS arbAxis = getArbitraryAxis( getExtrusion() );
|
||||||
|
|
|
@ -418,11 +418,12 @@ private:
|
||||||
|
|
||||||
virtual void addCircle( const DL_CircleData& aData ) override;
|
virtual void addCircle( const DL_CircleData& aData ) override;
|
||||||
virtual void addArc( const DL_ArcData& aData ) override;
|
virtual void addArc( const DL_ArcData& aData ) override;
|
||||||
|
void addEllipse( const DL_EllipseData& aData ) override;
|
||||||
//virtual void addLWPolyline( const DRW_LWPolyline& aData ) override;
|
//virtual void addLWPolyline( const DRW_LWPolyline& aData ) override;
|
||||||
virtual void addText( const DL_TextData& aData ) override;
|
virtual void addText( const DL_TextData& aData ) override;
|
||||||
virtual void addPolyline( const DL_PolylineData& aData ) override;
|
virtual void addPolyline( const DL_PolylineData& aData ) override;
|
||||||
|
|
||||||
/* Inserts blocks where specified by insert data */
|
/* Inserts blocks where specified by insert data */
|
||||||
virtual void addInsert( const DL_InsertData& aData ) override;
|
virtual void addInsert( const DL_InsertData& aData ) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -27,6 +27,7 @@ set( QA_KIMATH_SRCS
|
||||||
|
|
||||||
test_kimath.cpp
|
test_kimath.cpp
|
||||||
|
|
||||||
|
geometry/test_ellipse_to_bezier.cpp
|
||||||
geometry/test_fillet.cpp
|
geometry/test_fillet.cpp
|
||||||
geometry/test_circle.cpp
|
geometry/test_circle.cpp
|
||||||
geometry/test_segment.cpp
|
geometry/test_segment.cpp
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||||
|
#include <geometry/ellipse.h>
|
||||||
|
#include <bezier_curves.h>
|
||||||
|
|
||||||
|
#include <fmt.h>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE( EllipseToBezier )
|
||||||
|
|
||||||
|
|
||||||
|
/// Allows for rounding in the testcases
|
||||||
|
const double MAX_ERROR = 0.01;
|
||||||
|
|
||||||
|
|
||||||
|
struct ELLIPSE_TO_BEZIER_CASE
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
ELLIPSE<double> input;
|
||||||
|
std::vector<BEZIER<double>> expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static const std::vector<ELLIPSE_TO_BEZIER_CASE> cases = {
|
||||||
|
{
|
||||||
|
"full circle",
|
||||||
|
{ { 0, 0 }, { 100, 0 }, 1.0, ANGLE_0, FULL_CIRCLE },
|
||||||
|
{ { { 100, 0 }, { 100, -55.22847498307934 }, { 55.228474983079344, -100 }, { 0, -100 } },
|
||||||
|
{ { 0, -100 }, { -55.22847498307934, -100 }, { -100, -55.228474983079344 }, { -100, 0 } },
|
||||||
|
{ { -100, 0 }, { -100, 55.22847498307934 }, { -55.228474983079344, 100 }, { 0, 100 } },
|
||||||
|
{ { 0, 100 }, { 55.22847498307934, 100 }, { 100, 55.228474983079344 }, { 100, 0 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ellipse",
|
||||||
|
{ { 0, 0 }, { -100, 0 }, 0.5, ANGLE_0, FULL_CIRCLE },
|
||||||
|
{ { { -100, 0 }, { -100, 27.61423749153967 }, { -55.228474983079344, 50 }, { 0, 50 } },
|
||||||
|
{ { 0, 50 }, { 55.22847498307934, 50 }, { 100, 27.614237491539672 }, { 100, 0 } },
|
||||||
|
{ { 100, 0 }, { 100, -27.61423749153967 }, { 55.228474983079344, -50 }, { 0, -50 } },
|
||||||
|
{ { 0, -50 }, { -55.22847498307934, -50 }, { -100, -27.614237491539672 }, { -100, 0 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arc1",
|
||||||
|
{ { 0, 0 }, { 100, 0 }, 0.5, ANGLE_180, FULL_CIRCLE },
|
||||||
|
{ { { 100, 0 }, { 100, -27.61423749153967 }, { 55.228474983079344, -50 }, { 0, -50 } },
|
||||||
|
{ { 0, -50 }, { -55.22847498307934, -50 }, { -100, -27.614237491539672 }, { -100, 0 } },
|
||||||
|
{ { -100, 0 }, { -100, 27.61423749153967 }, { -55.228474983079344, 50 }, { 0, 50 } },
|
||||||
|
{ { 0, 50 }, { 55.22847498307934, 50 }, { 100, 27.614237491539672 }, { 100, 0 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arc2",
|
||||||
|
{ { 223, 165 }, { 372, 634 }, 0.96, EDA_ANGLE( 4.437, RADIANS_T ), EDA_ANGLE( 0.401, RADIANS_T ) },
|
||||||
|
{ { { -463.86, 336.27 }, { -389.81, 608.33 }, { -170.75, 818.49 }, { 99.52, 876.73 } },
|
||||||
|
{ { 99.52, 876.73 }, { 369.80, 934.98 }, { 643.36, 831.0 }, { 803.07, 609.31 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"arc3",
|
||||||
|
{ { 112.75, 490.24 }, { 304.54, 129.16 }, 7.14, EDA_ANGLE( 1.90, RADIANS_T ), EDA_ANGLE( 1.09, RADIANS_T ) },
|
||||||
|
{ { { 886.98, -1609.17 }, { 608.61, -1333.45 }, { 110.16, -394.92 }, { -305.88, 636.89 } },
|
||||||
|
{ { -305.88, 636.89 }, { -721.93, 1668.69 }, { -940.88, 2509.32 }, { -829.87, 2648.63 } },
|
||||||
|
{ { -829.87, 2648.63 }, { -718.85, 2787.95 }, { -308.47, 2187.55 }, { 152.23, 1211.78 } },
|
||||||
|
{ { 152.23, 1211.78 }, { 612.93, 236.01 }, { 996.95, -846.11 }, { 1071.24, -1377.92 } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( EllipseToBezier )
|
||||||
|
{
|
||||||
|
for( const ELLIPSE_TO_BEZIER_CASE& c : cases )
|
||||||
|
{
|
||||||
|
BOOST_TEST_CONTEXT( c.name )
|
||||||
|
{
|
||||||
|
std::vector<BEZIER<double>> out;
|
||||||
|
TransformEllipseToBeziers<double>( c.input, out );
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL( c.expected.size(), out.size() );
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for( BEZIER<double>& b : out )
|
||||||
|
{
|
||||||
|
BOOST_TEST_MESSAGE( fmt::format( "{{ {{ {}, {} }}, {{ {}, {} }}, {{ {}, {} }}, {{ {}, {} }} }}",
|
||||||
|
b.Start.x, b.Start.y, b.C1.x, b.C1.y,
|
||||||
|
b.C2.x, b.C2.y, b.End.x, b.End.y ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for( size_t i = 0; i < out.size(); i++ )
|
||||||
|
{
|
||||||
|
BOOST_CHECK_LE( ( c.expected[i].Start - out[i].Start ).EuclideanNorm(),
|
||||||
|
MAX_ERROR );
|
||||||
|
BOOST_CHECK_LE( ( c.expected[i].C1 - out[i].C1 ).EuclideanNorm(),
|
||||||
|
MAX_ERROR );
|
||||||
|
BOOST_CHECK_LE( ( c.expected[i].C2 - out[i].C2 ).EuclideanNorm(),
|
||||||
|
MAX_ERROR );
|
||||||
|
BOOST_CHECK_LE( ( c.expected[i].End - out[i].End ).EuclideanNorm(),
|
||||||
|
MAX_ERROR );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue