diff --git a/common/bezier_curves.cpp b/common/bezier_curves.cpp index 9dc67b6a93..bbf94acb0b 100644 --- a/common/bezier_curves.cpp +++ b/common/bezier_curves.cpp @@ -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 -#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 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 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 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 Bezier2Poly( int x1, int y1, int x2, int y2, int x3, int y3 ) +void BEZIER_POLY::GetPoly( std::vector& 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 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 ); } diff --git a/common/gr_basic.cpp b/common/gr_basic.cpp index 9c1e0ab6c2..7558ca70e7 100644 --- a/common/gr_basic.cpp +++ b/common/gr_basic.cpp @@ -1233,8 +1233,12 @@ void GRBezier( EDA_RECT* ClipBox, int width, COLOR4D Color ) { - std::vector Points = Bezier2Poly( x1, y1, x2, y2, x3, y3 ); - GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color ); + std::vector 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 Points = Bezier2Poly( x1, y1, x2, y2, x3, y3, x4, y4 ); - GRPoly( ClipBox, DC, Points.size(), &Points[0], false, width, Color, Color ); + std::vector 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 ); } diff --git a/eeschema/lib_bezier.cpp b/eeschema/lib_bezier.cpp index a1b81d8da2..8a7aa95768 100644 --- a/eeschema/lib_bezier.cpp +++ b/eeschema/lib_bezier.cpp @@ -291,11 +291,8 @@ void LIB_BEZIER::drawGraphic( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& std::vector 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 diff --git a/include/bezier_curves.h b/include/bezier_curves.h index 1e5c8a767e..c63871e706 100644 --- a/include/bezier_curves.h +++ b/include/bezier_curves.h @@ -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 - #include - /** - * 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 containing the points of the polyline + * Bezier curves to polygon converter. */ -std::vector 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 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 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 containing the points of the polyline - */ -std::vector Bezier2Poly( wxPoint c1, wxPoint c2, wxPoint c3,wxPoint c4 ); + BEZIER_POLY( const std::vector& aControlPoints ) + : m_ctrlPts( aControlPoints ) + { + } -std::vector 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& aOutput ); +private: + ///> Control points + std::vector m_ctrlPts; + + ///> Pointer to the output vector + std::vector* 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 diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index b454c3aaab..68dbd15866 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -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 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++ ) { diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index 50946ac184..9d61a59ff3 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -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 bezier_points; + std::vector 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 bezier_points; + std::vector 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 ); }