Bezier2Poly refactor

Before the refactor library browser display Bezier curves correctly only
once, at other times they were just straight lines or crashed the
application.
This commit is contained in:
Maciej Suminski 2017-05-16 17:38:19 +02:00
parent f77db7b4b9
commit e79f97860c
6 changed files with 158 additions and 167 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors.
* Copyright (C) 2014-2017 KiCad Developers, see CHANGELOG.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
@ -30,49 +30,7 @@
#include <bezier_curves.h>
#define add_segment(segment) if(s_bezier_Points_Buffer[s_bezier_Points_Buffer.size()-1] != segment) s_bezier_Points_Buffer.push_back(segment);
// Local variables:
static std::vector<wxPoint> s_bezier_Points_Buffer;
static int bezier_recursion_limit = 12;
static double bezier_approximation_scale = 0.5; // 1
static double bezier_curve_collinearity_epsilon = 1e-30;
static double bezier_curve_angle_tolerance_epsilon = 0.0001;
static double bezier_distance_tolerance_square; // derived by approximation_scale
static double bezier_angle_tolerance = 0.0;
static double bezier_cusp_limit = 0.0;
// Local functions:
static void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level );
static void recursive_bezier( int x1,
int y1,
int x2,
int y2,
int x3,
int y3,
int x4,
int y4,
int level );
/***********************************************************************************/
std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3, wxPoint c4 )
{
return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y, c4.x, c4.y );
}
std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3 )
{
return Bezier2Poly( c1.x, c1.y, c2.x, c2.y, c3.x, c3.y );
}
inline double calc_sq_distance( int x1, int y1, int x2, int y2 )
static inline double calc_sq_distance( int x1, int y1, int x2, int y2 )
{
int dx = x2 - x1;
int dy = y2 - y1;
@ -80,47 +38,39 @@ inline double calc_sq_distance( int x1, int y1, int x2, int y2 )
return (double)dx * dx + (double)dy * dy;
}
inline double sqrt_len( int dx, int dy )
static inline double sqrt_len( int dx, int dy )
{
return ((double)dx * dx) + ((double)dy * dy);
}
std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3 )
void BEZIER_POLY::GetPoly( std::vector<wxPoint>& aOutput )
{
s_bezier_Points_Buffer.clear();
m_output = &aOutput;
m_output->clear();
m_output->push_back( wxPoint( m_ctrlPts.front() ) );
bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale;
bezier_distance_tolerance_square *= bezier_distance_tolerance_square;
s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) );
recursive_bezier( x1, y1, x2, y2, x3, y3, 0 );
s_bezier_Points_Buffer.push_back( wxPoint( x3, y3 ) );
// Only quadratic and cubic Bezier curves are handled
if( m_ctrlPts.size() == 3 )
recursiveBezier( m_ctrlPts[0].x, m_ctrlPts[0].y,
m_ctrlPts[1].x, m_ctrlPts[1].y,
m_ctrlPts[2].x, m_ctrlPts[2].y, 0 );
wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() );
return s_bezier_Points_Buffer;
else if( m_ctrlPts.size() == 4 )
recursiveBezier( m_ctrlPts[0].x, m_ctrlPts[0].y,
m_ctrlPts[1].x, m_ctrlPts[1].y,
m_ctrlPts[2].x, m_ctrlPts[2].y,
m_ctrlPts[3].x, m_ctrlPts[3].y, 0 );
m_output->push_back( wxPoint( m_ctrlPts.back() ) );
}
std::vector<wxPoint> Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
void BEZIER_POLY::recursiveBezier( int x1, int y1, int x2, int y2, int x3, int y3, unsigned int level )
{
s_bezier_Points_Buffer.clear();
bezier_distance_tolerance_square = 0.5 / bezier_approximation_scale;
bezier_distance_tolerance_square *= bezier_distance_tolerance_square;
s_bezier_Points_Buffer.push_back( wxPoint( x1, y1 ) );
recursive_bezier( x1, y1, x2, y2, x3, y3, x4, y4, 0 );
s_bezier_Points_Buffer.push_back( wxPoint( x4, y4 ) );
wxLogDebug( wxT( "Bezier Conversion - End (%d vertex)" ), s_bezier_Points_Buffer.size() );
return s_bezier_Points_Buffer;
}
void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level )
{
if( abs( level ) > bezier_recursion_limit )
{
if( level > recursion_limit )
return;
}
// Calculate all the mid-points of the line segments
//----------------------
@ -136,18 +86,18 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level
double d = fabs( ((double) (x2 - x3) * dy) - ((double) (y2 - y3) * dx ) );
double da;
if( d > bezier_curve_collinearity_epsilon )
if( d > curve_collinearity_epsilon )
{
// Regular case
//-----------------
if( d * d <= bezier_distance_tolerance_square * (dx * dx + dy * dy) )
if( d * d <= distance_tolerance_square * (dx * dx + dy * dy) )
{
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
if( angle_tolerance < curve_angle_tolerance_epsilon )
{
add_segment( wxPoint( x123, y123 ) );
addSegment( wxPoint( x123, y123 ) );
return;
}
@ -158,11 +108,11 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level
if( da >=M_PI )
da = 2 * M_PI - da;
if( da < bezier_angle_tolerance )
if( da < angle_tolerance )
{
// Finally we can stop the recursion
//----------------------
add_segment( wxPoint( x123, y123 ) );
addSegment( wxPoint( x123, y123 ) );
return;
}
}
@ -193,26 +143,24 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int level
d = calc_sq_distance( x2, y2, x1 + (int) d * dx,
y1 + (int) d * dy );
}
if( d < bezier_distance_tolerance_square )
if( d < distance_tolerance_square )
{
add_segment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x2, y2 ) );
return;
}
}
// Continue subdivision
//----------------------
recursive_bezier( x1, y1, x12, y12, x123, y123, level + 1 );
recursive_bezier( x123, y123, x23, y23, x3, y3, -(level + 1) );
recursiveBezier( x1, y1, x12, y12, x123, y123, level + 1 );
recursiveBezier( x123, y123, x23, y23, x3, y3, -(level + 1) );
}
void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int level )
void BEZIER_POLY::recursiveBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, unsigned int level )
{
if( abs( level ) > bezier_recursion_limit )
{
if( level > recursion_limit )
return;
}
// Calculate all the mid-points of the line segments
//----------------------
@ -239,8 +187,8 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
double d3 = fabs( (double) ( (x3 - x4) * dy - (y3 - y4) * dx ) );
double da1, da2, k;
switch( (int(d2 > bezier_curve_collinearity_epsilon) << 1) +
int(d3 > bezier_curve_collinearity_epsilon) )
switch( (int(d2 > curve_collinearity_epsilon) << 1) +
int(d3 > curve_collinearity_epsilon) )
{
case 0:
@ -285,17 +233,17 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
}
if( d2 > d3 )
{
if( d2 < bezier_distance_tolerance_square )
if( d2 < distance_tolerance_square )
{
add_segment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x2, y2 ) );
return;
}
}
else
{
if( d3 < bezier_distance_tolerance_square )
if( d3 < distance_tolerance_square )
{
add_segment( wxPoint( x3, y3 ) );
addSegment( wxPoint( x3, y3 ) );
return;
}
}
@ -305,11 +253,11 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
// p1,p2,p4 are collinear, p3 is significant
//----------------------
if( d3 * d3 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
if( d3 * d3 <= distance_tolerance_square * sqrt_len(dx, dy) )
{
if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
if( angle_tolerance < curve_angle_tolerance_epsilon )
{
add_segment( wxPoint( x23, y23 ) );
addSegment( wxPoint( x23, y23 ) );
return;
}
@ -320,18 +268,18 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
if( da1 >= M_PI )
da1 = 2 * M_PI - da1;
if( da1 < bezier_angle_tolerance )
if( da1 < angle_tolerance )
{
add_segment( wxPoint( x2, y2 ) );
add_segment( wxPoint( x3, y3 ) );
addSegment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x3, y3 ) );
return;
}
if( bezier_cusp_limit != 0.0 )
if( cusp_limit != 0.0 )
{
if( da1 > bezier_cusp_limit )
if( da1 > cusp_limit )
{
add_segment( wxPoint( x3, y3 ) );
addSegment( wxPoint( x3, y3 ) );
return;
}
}
@ -342,11 +290,11 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
// p1,p3,p4 are collinear, p2 is significant
//----------------------
if( d2 * d2 <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
if( d2 * d2 <= distance_tolerance_square * sqrt_len(dx, dy) )
{
if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
if( angle_tolerance < curve_angle_tolerance_epsilon )
{
add_segment( wxPoint( x23, y23 ) );
addSegment( wxPoint( x23, y23 ) );
return;
}
@ -357,18 +305,18 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
if( da1 >= M_PI )
da1 = 2 * M_PI - da1;
if( da1 < bezier_angle_tolerance )
if( da1 < angle_tolerance )
{
add_segment( wxPoint( x2, y2 ) );
add_segment( wxPoint( x3, y3 ) );
addSegment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x3, y3 ) );
return;
}
if( bezier_cusp_limit != 0.0 )
if( cusp_limit != 0.0 )
{
if( da1 > bezier_cusp_limit )
if( da1 > cusp_limit )
{
add_segment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x2, y2 ) );
return;
}
}
@ -379,14 +327,14 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
// Regular case
//-----------------
if( (d2 + d3) * (d2 + d3) <= bezier_distance_tolerance_square * sqrt_len(dx, dy) )
if( (d2 + d3) * (d2 + d3) <= distance_tolerance_square * sqrt_len(dx, dy) )
{
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if( bezier_angle_tolerance < bezier_curve_angle_tolerance_epsilon )
if( angle_tolerance < curve_angle_tolerance_epsilon )
{
add_segment( wxPoint( x23, y23 ) );
addSegment( wxPoint( x23, y23 ) );
return;
}
@ -402,25 +350,25 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
if( da2 >= M_PI )
da2 = 2 * M_PI - da2;
if( da1 + da2 < bezier_angle_tolerance )
if( da1 + da2 < angle_tolerance )
{
// Finally we can stop the recursion
//----------------------
add_segment( wxPoint( x23, y23 ) );
addSegment( wxPoint( x23, y23 ) );
return;
}
if( bezier_cusp_limit != 0.0 )
if( cusp_limit != 0.0 )
{
if( da1 > bezier_cusp_limit )
if( da1 > cusp_limit )
{
add_segment( wxPoint( x2, y2 ) );
addSegment( wxPoint( x2, y2 ) );
return;
}
if( da2 > bezier_cusp_limit )
if( da2 > cusp_limit )
{
add_segment( wxPoint( x3, y3 ) );
addSegment( wxPoint( x3, y3 ) );
return;
}
}
@ -430,6 +378,6 @@ void recursive_bezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, i
// Continue subdivision
//----------------------
recursive_bezier( x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1 );
recursive_bezier( x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1 );
recursiveBezier( x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1 );
recursiveBezier( x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1 );
}

View File

@ -1233,8 +1233,12 @@ void GRBezier( EDA_RECT* ClipBox,
int width,
COLOR4D Color )
{
std::vector<wxPoint> Points = Bezier2Poly( x1, y1, x2, y2, x3, y3 );
GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color );
std::vector<wxPoint> points;
BEZIER_POLY converter( x1, y1, x2, y2, x3, y3 );
converter.GetPoly( points );
GRPoly( ClipBox, DC, points.size(), &points[0], false, width, Color, Color );
}
@ -1251,8 +1255,12 @@ void GRBezier( EDA_RECT* ClipBox,
int width,
COLOR4D Color )
{
std::vector<wxPoint> Points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 );
GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color );
std::vector<wxPoint> points;
BEZIER_POLY converter( x1, y1, x2, y2, x3, y3, x4, y4 );
converter.GetPoly( points );
GRPoly( ClipBox, DC, points.size(), &points[0], false, width, Color, Color );
}

View File

@ -291,11 +291,8 @@ void LIB_BEZIER::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint&
std::vector<wxPoint> PolyPointsTraslated;
COLOR4D color = GetLayerColor( LAYER_DEVICE );
m_PolyPoints = Bezier2Poly( m_BezierPoints[0],
m_BezierPoints[1],
m_BezierPoints[2],
m_BezierPoints[3] );
BEZIER_POLY converter( m_BezierPoints );
converter.GetPoly( m_PolyPoints );
PolyPointsTraslated.clear();
@ -319,17 +316,18 @@ void LIB_BEZIER::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint&
fill = NO_FILL;
GRSetDrawMode( aDC, aDrawMode );
EDA_RECT* const clipbox = aPanel? aPanel->GetClipBox() : NULL;
if( fill == FILLED_WITH_BG_BODYCOLOR )
GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
GRPoly( clipbox, aDC, m_PolyPoints.size(),
&PolyPointsTraslated[0], 1, GetPenSize(),
(m_Flags & IS_MOVED) ? color : GetLayerColor( LAYER_DEVICE_BACKGROUND ),
GetLayerColor( LAYER_DEVICE_BACKGROUND ) );
else if( fill == FILLED_SHAPE )
GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
GRPoly( clipbox, aDC, m_PolyPoints.size(),
&PolyPointsTraslated[0], 1, GetPenSize(), color, color );
else
GRPoly( aPanel->GetClipBox(), aDC, m_PolyPoints.size(),
GRPoly( clipbox, aDC, m_PolyPoints.size(),
&PolyPointsTraslated[0], 0, GetPenSize(), color, color );
/* Set to one (1) to draw bounding box around bezier curve to validate

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
* Copyright (C) 1992-2017 KiCad Developers, see CHANGELOG.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
@ -26,35 +26,67 @@
#define BEZIER_CURVES_H
#include <vector>
#include <wx/gdicmn.h>
/**
* Function Bezier2Poly
* convert a Bezier curve to a polyline
* @param c1 - First point to convert.
* @param c2 - Second point to convert.
* @param c3 - Third point to convert.
* @return a std::vector<wxPoint> containing the points of the polyline
* Bezier curves to polygon converter.
*/
std::vector<wxPoint> Bezier2Poly(wxPoint c1, wxPoint c2, wxPoint c3);
class BEZIER_POLY
{
public:
BEZIER_POLY( int x1, int y1, int x2, int y2, int x3, int y3 )
{
m_ctrlPts.emplace_back( x1, y1 );
m_ctrlPts.emplace_back( x2, y2 );
m_ctrlPts.emplace_back( x3, y3 );
}
std::vector<wxPoint> Bezier2Poly(int x1, int y1, int x2, int y2, int x3, int y3);
BEZIER_POLY( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
{
m_ctrlPts.emplace_back( x1, y1 );
m_ctrlPts.emplace_back( x2, y2 );
m_ctrlPts.emplace_back( x3, y3 );
m_ctrlPts.emplace_back( x4, y4 );
}
/**
* Function Bezier2Poly
* convert a Bezier curve to a polyline
* @return a std::vector<wxPoint> containing the points of the polyline
* @param c1 - First point to convert.
* @param c2 - Second point to convert.
* @param c3 - Third point to convert.
* @param c4 - Fourth point to convert.
* @return a std::vector<wxPoint> containing the points of the polyline
*/
std::vector<wxPoint> Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3,wxPoint c4 );
BEZIER_POLY( const std::vector<wxPoint>& aControlPoints )
: m_ctrlPts( aControlPoints )
{
}
std::vector<wxPoint> Bezier2Poly(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
/**
* Converts Bezier curve to a polygon.
* @param aOutput will be used as an output vector storing polygon points.
*/
void GetPoly( std::vector<wxPoint>& aOutput );
private:
///> Control points
std::vector<wxPoint> m_ctrlPts;
///> Pointer to the output vector
std::vector<wxPoint>* m_output;
void addSegment( const wxPoint& aSegment )
{
if( m_output->back() != aSegment )
m_output->push_back( aSegment );
}
void recursiveBezier( int x1, int y1, int x2, int y2, int x3, int y3, unsigned int level );
void recursiveBezier( int x1, int y1, int x2, int y2,
int x3, int y3, int x4, int y4, unsigned int level );
// Conversion parameters
constexpr static double angle_tolerance = 0.0;
constexpr static double cusp_limit = 0.0;
constexpr static int recursion_limit = 12;
constexpr static double approximation_scale = 0.5; // 1
constexpr static double distance_tolerance_square = ( 0.5 / approximation_scale ) * ( 0.5 / approximation_scale );
constexpr static double curve_collinearity_epsilon = 1e-30;
constexpr static double curve_angle_tolerance_epsilon = 0.0001;
};
#endif // BEZIER_CURVES_H

View File

@ -289,7 +289,11 @@ void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode,
break;
case S_CURVE:
m_BezierPoints = Bezier2Poly( m_Start, m_BezierC1, m_BezierC2, m_End );
{
std::vector<wxPoint> ctrlPoints = { m_Start, m_BezierC1, m_BezierC2, m_End };
BEZIER_POLY converter( ctrlPoints );
converter.GetPoly( m_BezierPoints );
}
for( unsigned int i=1; i < m_BezierPoints.size(); i++ )
{

View File

@ -1008,26 +1008,27 @@ void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int n
}
// Bezier Support
void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3 )
{
std::vector<wxPoint> bezier_points;
std::vector<wxPoint> polyPoints;
bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3 );
BEZIER_POLY converter( x1, y1, x2, y2, x3, y3 );
converter.GetPoly( polyPoints );
for( unsigned int i = 0; i < bezier_points.size(); i++ )
AppendCorner( bezier_points[i].x, bezier_points[i].y );
for( const auto& pt : polyPoints )
AppendCorner( pt.x, pt.y );
}
void CPolyLine::AppendBezier( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4 )
{
std::vector<wxPoint> bezier_points;
std::vector<wxPoint> polyPoints;
bezier_points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 );
BEZIER_POLY converter( x1, y1, x2, y2, x3, y3, x4, y4 );
converter.GetPoly( polyPoints );
for( unsigned int i = 0; i < bezier_points.size(); i++ )
AppendCorner( bezier_points[i].x, bezier_points[i].y );
for( const auto& pt : polyPoints )
AppendCorner( pt.x, pt.y );
}