86#define BEZIER_DBG "bezier"
102 m_ctrlPts.reserve( aControlPoints.size() );
104 for(
const VECTOR2I& pt : aControlPoints )
115 if( std::isnan( pt.x ) || std::isnan( pt.y ) )
131 double u = std::min( std::max( t, 0.0 ), 1.0 );
135 return 0.5 *
delta.EuclideanNorm() <= aMaxError;
144 double cross1 =
delta.Cross( D21 );
145 double cross2 =
delta.Cross( D31 );
147 double inv_delta_sq = 1.0 /
delta.SquaredEuclideanNorm();
148 double d1 = ( cross1 * cross1 ) * inv_delta_sq;
149 double d2 = ( cross2 * cross2 ) * inv_delta_sq;
151 double factor = ( cross1 * cross2 > 0.0 ) ? 3.0 / 4.0 : 4.0 / 9.0;
152 double f2 = factor * factor;
153 double tol = aMaxError * aMaxError;
155 return d1 * f2 <= tol && d2 * f2 <= tol;
167 std::vector<VECTOR2D> buffer;
170 aOutput.reserve( buffer.size() );
178 const double d = 0.6744897501960817;
179 const double d4 = d * d * d * d;
180 return x / ( 1.0 - d + std::pow( d4 + x * x * 0.25, 0.25 ) );
185 const double p = 0.39538816;
186 return x * ( 1.0 - p + std::sqrt( p * p + 0.25 * x * x ) );
192 double omt = 1.0 - t;
193 double omt2 = omt * omt;
201 double omt3 = omt * omt2;
224 double x0 = u0 / cross;
225 double x2 = u2 / cross;
231 int n = std::ceil( 0.5 *
std::abs( a2 - a0 ) * std::sqrt(
scale / aMaxError ) );
238 for(
int ii = 0; ii < n; ++ii )
241 double t = ( u - v0 ) / (
v2 - v0 );
242 aOutput.emplace_back(
eval( t ) );
255 double cross1 = D21.
Cross( D32 ) * D32.
Cross( D43 );
256 double cross2 = D21.
Cross( D32 ) * D21.
Cross( D43 );
260 else if( cross2 > 0.0 )
263 bool b1 = D21.
Dot( D32 ) > 0.0;
264 bool b2 = D32.
Dot( D43 ) > 0.0;
269 wxLogTrace(
BEZIER_DBG,
"numberOfInflectionPoints: rare case" );
278 double len_sq =
delta.SquaredEuclideanNorm();
283 double len = std::sqrt( len_sq );
308 VECTOR2D left_ctrl2 = left_ctrl1 + aT * ( tmp - left_ctrl1 );
310 VECTOR2D right_ctrl1 = tmp + aT * ( right_ctrl2 - tmp );
311 VECTOR2D shared = left_ctrl2 + aT * ( right_ctrl1 - left_ctrl2 );
332 wxLogTrace(
BEZIER_DBG,
"recursiveSegmentation with threshold %f", aThreshhold );
333 std::vector<BEZIER_POLY> stack;
339 stack.push_back( *
this );
341 while( !stack.empty() )
343 bezier = &stack.back();
347 wxLogTrace(
BEZIER_DBG,
"recursiveSegmentation dropping zero length segment" );
350 else if( bezier->
isFlat( aThreshhold ) )
352 aOutput.push_back( bezier->
m_ctrlPts[3] );
359 stack.push_back(
left );
363 wxLogTrace(
BEZIER_DBG,
"recursiveSegmentation generated %zu points", aOutput.size() );
376 double a = 3 *
A.Cross(
B );
377 double b = 3 *
A.Cross(
C );
378 double c =
B.Cross(
C );
381 double r2 = ( b * b - 4 * a * c );
386 if( r2 >= 0.0 && a != 0.0 )
388 double r = std::sqrt( r2 );
390 double t1 = ( ( -b + r ) / ( 2 * a ) );
391 double t2 = ( ( -b - r ) / ( 2 * a ) );
393 if( ( t1 > 0.0 && t1 < 1.0 ) && ( t2 > 0.0 && t2 < 1.0 ) )
403 if( t2 - t1 > 0.00001 )
405 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found 2 inflection points at t1 = %f, t2 = %f", t1, t2 );
410 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found 1 inflection point at t = %f", t1 );
414 else if( t1 > 0.0 && t1 < 1.0 )
417 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found 1 inflection point at t = %f", t1 );
420 else if( t2 > 0.0 && t2 < 1.0 )
423 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found 1 inflection point at t = %f", t2 );
427 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found no inflection points" );
431 wxLogTrace(
BEZIER_DBG,
"BEZIER_POLY Found no inflection points" );
438 std::vector<BEZIER_POLY> stack;
439 stack.push_back( std::vector<VECTOR2D>(4) );
440 stack.push_back( std::vector<VECTOR2D>(4) );
441 stack.push_back( std::vector<VECTOR2D>(4) );
442 stack.push_back( std::vector<VECTOR2D>(4) );
452 wxLogDebug(
"cubicParabolicApprox: NaN detected" );
456 if( c->
isFlat( aMaxError ) )
466 double t = 2 * std::sqrt( aMaxError / ( 3.0 * d ) );
468 wxLogTrace(
BEZIER_DBG,
"cubicParabolicApprox: split point t = %f", t );
472 wxLogTrace(
BEZIER_DBG,
"cubicParabolicApprox: Invalid t value detected" );
482 if( b1->
isFlat( aMaxError ) )
496 if( b1 == &stack.front() )
516 wxLogTrace(
BEZIER_DBG,
"getCubicPoly Short circuit to PA" );
528 wxLogTrace(
BEZIER_DBG,
"getCubicPoly: 2 inflection points" );
541 else if( numOfIfP == 1 )
545 wxLogTrace(
BEZIER_DBG,
"getCubicPoly: 2nd inflection point not found" );
558 else if( numOfIfP == 1 )
560 wxLogTrace(
BEZIER_DBG,
"getCubicPoly: 1 inflection point" );
570 wxLogTrace(
BEZIER_DBG,
"getCubicPoly: Unknown inflection points" );
579 if( aMaxError <= 0.0 )
595 wxLogTrace(
BEZIER_DBG,
"GetPoly generated %zu points", aOutput.size() );
615 const int minBeziersPerCircle = 4;
618 const int numBeziers =
static_cast<int>(
619 std::ceil(
std::abs( arcAngle.
AsRadians() / ( 2 * M_PI / minBeziersPerCircle ) ) ) );
622 const double angleIncrement = arcAngle.
AsRadians() / numBeziers;
638 double theta = angleIncrement;
639 double k = ( 4. / 3. ) * std::tan( theta / 4 );
651 { std::cos( theta ) + k * std::sin( theta ),
652 std::sin( theta ) - k * std::cos( theta ) },
653 { std::cos( theta ), std::sin( theta ) } };
658 auto transformPoint =
672 aPoint += aEllipse.
Center;
677 for(
int i = 0; i < numBeziers; i++ )
680 transformPoint( first.
Start, i * angleIncrement ),
681 transformPoint( first.
C1, i * angleIncrement ),
682 transformPoint( first.
C2, i * angleIncrement ),
683 transformPoint( first.
End, i * angleIncrement )
static double approx_int(double x)
static double approx_inv_int(double x)
void TransformEllipseToBeziers(const ELLIPSE< T > &aEllipse, std::vector< BEZIER< T > > &aBeziers)
Transforms an ellipse or elliptical arc into a set of quadratic Bezier curves that approximate it.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Bezier curves to polygon converter.
void recursiveSegmentation(std::vector< VECTOR2D > &aOutput, double aMaxError)
void getQuadPoly(std::vector< VECTOR2D > &aOutput, double aMaxError)
int numberOfInflectionPoints()
double thirdControlPointDeviation()
void cubicParabolicApprox(std::vector< VECTOR2D > &aOutput, double aMaxError)
double m_minSegLen
Control points.
void getCubicPoly(std::vector< VECTOR2D > &aOutput, double aMaxError)
BEZIER_POLY(const VECTOR2I &aStart, const VECTOR2I &aCtrl1, const VECTOR2I &aCtrl2, const VECTOR2I &aEnd)
void subdivide(double aT, BEZIER_POLY &aLeft, BEZIER_POLY &aRight)
bool isFlat(double aMaxError) const
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
int findInflectionPoints(double &aT1, double &aT2)
std::vector< VECTOR2D > m_ctrlPts
Generic cubic Bezier representation.
VECTOR2< NumericType > Start
VECTOR2< NumericType > C1
VECTOR2< NumericType > C2
VECTOR2< NumericType > End
This class was created to handle importing ellipses from other file formats that support them nativel...
VECTOR2< NumericType > Center
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
static constexpr EDA_ANGLE ANGLE_0
static constexpr EDA_ANGLE ANGLE_360
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
VECTOR2< double > VECTOR2D