diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 7d6cc74f1a..194d307ba7 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -38,6 +38,7 @@ #include <md5_hash.h> #include <map> +#include <geometry/geometry_utils.h> #include <geometry/shape.h> #include <geometry/shape_line_chain.h> #include <geometry/shape_poly_set.h> @@ -1575,10 +1576,10 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, - unsigned int aSegments, + int aErrorMax, int aIndex ) { - return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aSegments ); + return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aErrorMax ); } @@ -1703,12 +1704,12 @@ SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance ) } -SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments ) +SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aErrorMax ) { SHAPE_POLY_SET filleted; for( size_t polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ ) - filleted.m_polys.push_back( FilletPolygon( aRadius, aSegments, polygonIdx ) ); + filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, polygonIdx ) ); return filleted; } @@ -1717,7 +1718,7 @@ SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aSegments ) SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance, int aIndex, - int aSegments ) + int aErrorMax ) { // Null segments create serious issues in calculations. Remove them: RemoveNullSegments(); @@ -1833,9 +1834,8 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, argument = 1; double arcAngle = acos( argument ); - - // Calculate the number of segments - unsigned int segments = ceil( (double) aSegments * ( arcAngle / ( 2 * M_PI ) ) ); + double arcAngleDegrees = arcAngle * 180.0 / M_PI; + int segments = GetArcToSegmentCount( radius, aErrorMax, arcAngleDegrees ); double deltaAngle = arcAngle / segments; double startAngle = atan2( -ys, xs ); diff --git a/include/convert_to_biu.h b/include/convert_to_biu.h index 795f79d48a..b86f22ebd6 100644 --- a/include/convert_to_biu.h +++ b/include/convert_to_biu.h @@ -98,6 +98,15 @@ constexpr inline double Iu2Mils( int iu ) { return iu / IU_PER_MILS; } + +// The max error is the distance between the middle of a segment, and the circle +// for circle/arc to segment approximation. +// Warning: too small values can create very long calculation time in zone filling +// 0.05 to 0.005 mm are reasonable values + +constexpr int ARC_LOW_DEF = Millimeter2iu( 0.02 ); +constexpr int ARC_HIGH_DEF = Millimeter2iu( 0.005 ); + #endif #endif // CONVERT_TO_BIU_H_ diff --git a/include/geometry/geometry_utils.h b/include/geometry/geometry_utils.h index 0c5cc3915b..a2d4f1d3c4 100644 --- a/include/geometry/geometry_utils.h +++ b/include/geometry/geometry_utils.h @@ -41,7 +41,7 @@ int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree ); /** - * @return the correction factor to approximate a circle by seg�ments + * @return the correction factor to approximate a circle by segments * @param aSegCountforCircle is the number of segments to approximate the circle * * When creating a polygon from a circle, the polygon is inside the circle. diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index cec5870228..4d0c4ed6c8 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -1002,11 +1002,11 @@ class SHAPE_POLY_SET : public SHAPE * Function Fillet * returns a filleted version of the aIndex-th polygon. * @param aRadius is the fillet radius. - * @param aSegments is the number of segments / fillet. + * @param aErrorMax is the maximum allowable deviation of the polygon from the circle * @param aIndex is the index of the polygon to be filleted * @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon. */ - POLYGON FilletPolygon( unsigned int aRadius, unsigned int aSegments, int aIndex = 0 ); + POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex = 0 ); /** * Function Chamfer @@ -1020,10 +1020,10 @@ class SHAPE_POLY_SET : public SHAPE * Function Fillet * returns a filleted version of the polygon set. * @param aRadius is the fillet radius. - * @param aSegments is the number of segments / fillet. + * @param aErrorMax is the maximum allowable deviation of the polygon from the circle * @return SHAPE_POLY_SET - A set containing the filleted version of this set. */ - SHAPE_POLY_SET Fillet( int aRadius, int aSegments ); + SHAPE_POLY_SET Fillet( int aRadius, int aErrorMax ); /** * Function DistanceToPolygon @@ -1147,12 +1147,12 @@ class SHAPE_POLY_SET : public SHAPE * @param aDistance is the chamfering distance if aMode = CHAMFERED; if aMode = FILLETED, * is the filleting radius. * @param aIndex is the index of the polygon that will be chamfered/filleted. - * @param aSegments is the number of filleting segments if aMode = FILLETED. If aMode = - * CHAMFERED, it is unused. + * @param aErrorMax is the maximum allowable deviation of the polygon from the circle + * if aMode = FILLETED. If aMode = CHAMFERED, it is unused. * @return POLYGON - the chamfered/filleted version of the polygon. */ POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance, - int aIndex, int aSegments = -1 ); + int aIndex, int aErrorMax = -1 ); ///> Returns true if the polygon set has any holes that touch share a vertex. bool hasTouchingHoles( const POLYGON& aPoly ) const; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 694ede18f6..1fd86ce2dd 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -1328,7 +1328,12 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const break; case ZONE_SETTINGS::SMOOTHING_FILLET: - aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, m_ArcToSegmentsCount ); + // Note: we're now using m_ArcToSegmentsCount only as a hint to determine accuracy + // vs. speed. + if( m_ArcToSegmentsCount > SEGMENT_COUNT_CROSSOVER ) + aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, ARC_HIGH_DEF ); + else + aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, ARC_LOW_DEF ); break; default: diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp index 32e8bb9e08..ba82de7cf9 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.cpp +++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp @@ -38,12 +38,6 @@ #include <geometry/geometry_utils.h> -// Error max when converting a circle or arc to segments. -// Avoid too small values that create a very long calculation time -// in zone fillings -#define ARC_ACCURACY ( 0.05 * IU_PER_MM ) - - /** * Function close_ness * is a non-exact distance (also called Manhattan distance) used to approximate @@ -234,8 +228,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, wxPoint pstart = graphic->GetArcStart(); wxPoint center = graphic->GetCenter(); double angle = -graphic->GetAngle(); - int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, - (double)angle / 10.0 ); + double radius = graphic->GetRadius(); + int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); wxPoint pt; for( int step = 1; step<=steps; ++step ) @@ -293,7 +287,7 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, // Output the Edge.Cuts perimeter as circle or polygon. if( graphic->GetShape() == S_CIRCLE ) { - int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 ); + int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_LOW_DEF, 360.0 ); TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps ); } else @@ -340,8 +334,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, wxPoint pend = graphic->GetArcEnd(); wxPoint pcenter = graphic->GetCenter(); double angle = -graphic->GetAngle(); - int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, - (double)angle / 10.0 ); + double radius = graphic->GetRadius(); + int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); if( !close_enough( prevPt, pstart, prox ) ) { @@ -431,9 +425,9 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, // make a circle by segments; wxPoint center = graphic->GetCenter(); double angle = 3600.0; - wxPoint start = center; + wxPoint start = center; int radius = graphic->GetRadius(); - int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 ); + int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 ); wxPoint nextPt; start.x += radius; @@ -491,8 +485,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, wxPoint pend = graphic->GetArcEnd(); wxPoint pcenter = graphic->GetCenter(); double angle = -graphic->GetAngle(); - int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, - (double)angle / 10.0 ); + int radius = graphic->GetRadius(); + int steps = GetArcToSegmentCount( radius, ARC_LOW_DEF, angle / 10.0 ); if( !close_enough( prevPt, pstart, prox ) ) { diff --git a/pcbnew/pcbnew.h b/pcbnew/pcbnew.h index 228a5fab03..8209549772 100644 --- a/pcbnew/pcbnew.h +++ b/pcbnew/pcbnew.h @@ -41,6 +41,10 @@ #define ARC_APPROX_SEGMENTS_COUNT_LOW_DEF 16 #define ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF 32 +// The new absolute-error-based algorithm uses the stored segment count as a hint on whether +// to use ARC_HIGH_DEF or ARC_LOW_DEF. This defines the crossover point. +#define SEGMENT_COUNT_CROSSOVER 24 + /* Flag used in locate functions. The locate ref point is the on grid cursor or the off * grid mouse cursor. */ #define CURSEUR_ON_GRILLE (0 << 0) diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index dccf7f07f1..4e98f9c9c8 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -767,8 +767,7 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule ) double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() ); // seg count to approximate circle by line segments - int err_max = Millimeter2iu( 0.05 ); - int seg_per_circle = GetArcToSegmentCount( radius, err_max, 360.0 ); + int seg_per_circle = GetArcToSegmentCount( radius, ARC_LOW_DEF, 360.0 ); for( int ii = 0; ii < seg_per_circle; ++ii ) { diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 32a98fdb5f..87bcb9bc91 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -293,7 +293,7 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone, double correctionFactor; // Set the number of segments in arc approximations - if( aZone->GetArcSegmentCount() == ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF ) + if( aZone->GetArcSegmentCount() > SEGMENT_COUNT_CROSSOVER ) segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_HIGHT_DEF; else segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;