Switch zone fillets to absolute-error algorithm.

And some general cleanup to related constants, etc.
This commit is contained in:
Jeff Young 2018-03-22 18:02:45 +00:00
parent d2d0660647
commit fbf10e941b
9 changed files with 46 additions and 35 deletions

View File

@ -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 );

View File

@ -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_

View File

@ -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.

View File

@ -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;

View File

@ -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:

View File

@ -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 ) )
{

View File

@ -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)

View File

@ -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 )
{

View File

@ -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;