Circle to polygon conversion: a few enhancements:

* Remove duplicate code
* fix incorrect formulas is some places
* add comments
This commit is contained in:
jean-pierre charras 2018-03-08 13:48:29 +01:00
parent 9c62792245
commit 51fe063524
7 changed files with 158 additions and 42 deletions

View File

@ -350,6 +350,7 @@ set( COMMON_SRCS
tool/zoom_tool.cpp
geometry/convex_hull.cpp
geometry/geometry_utils.cpp
geometry/seg.cpp
geometry/shape.cpp
geometry/shape_collisions.cpp

View File

@ -0,0 +1,65 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file geometry_utils.cpp
* @brief a few functions useful in geometry calculations.
*/
#include <math.h>
#include <common.h>
#include <geometry/geometry_utils.h>
int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree )
{
// calculate the number of segments to approximate a circle by segments
// given the max distance between the middle of a segment and the circle
// error relative to the radius value:
double rel_error = (double)aErrorMax / aRadius;
// minimal arc increment in degrees:
double step = 180 / M_PI * acos( 1.0 - rel_error ) * 2;
// the minimal seg count for a arc
int segCount = KiROUND( fabs( aArcAngleDegree ) / step );
// Ensure at least one segment is used
return std::max( segCount, 1 );
}
double GetCircletoPolyCorrectionFactor( int aSegCountforCircle )
{
/* calculates the coeff to compensate radius reduction of circle
* due to the segment approx.
* For a circle the min radius is radius * cos( 2PI / aSegCountforCircle / 2)
* this is the distance between the center and the middle of the segment.
* therfore, to move the middle of the segment to the circle (distance = radius)
* the correctionFactor is 1 /cos( PI/aSegCountforCircle )
*/
double correctionFactor = 1.0 / cos( M_PI / aSegCountforCircle );
return correctionFactor;
}

View File

@ -0,0 +1,58 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file geometry_utils.h
* @brief a few functions useful in geometry calculations.
*/
#ifndef GEOMETRY_UTILS_H
#define GEOMETRY_UTILS_H
/**
* @return the number of segments to approximate a arc by segments
* with a given max error (this number is >= 1)
* @param aRadius is the radius od the circle or arc
* @param aErrorMax is the max error
* This is the max distance between the middle of a segment and the circle.
* @param aArcAngleDegree is the arc angle in degrees
*/
int GetArcToSegmentCount( int aRadius, int aErrorMax, double aArcAngleDegree );
/**
* @return the correction factor to approximate a circle by segùments
* @param aSegCountforCircle is the number of segments to approximate the circle
*
* When creating a polygon from a circle, the polygon is inside the circle.
* Only corners are on the circle.
* This is incorrect when building clearance areas of circles, that need to build
* the equivalent polygon outside the circle
* The correction factor is a scaling factor to apply to the radius to build a
* polygon outside the circle (only the middle of each segment is on the circle
*/
double GetCircletoPolyCorrectionFactor( int aSegCountforCircle );
#endif // #ifndef GEOMETRY_UTILS_H

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009-2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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
@ -47,6 +47,7 @@
#include <class_module.h>
#include <class_edge_mod.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/geometry_utils.h>
// These variables are parameters used in addTextSegmToPoly.
// But addTextSegmToPoly is a call-back function,
@ -55,6 +56,11 @@ static int s_textWidth;
static int s_textCircle2SegmentCount;
static SHAPE_POLY_SET* s_cornerBuffer;
// 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
double s_error_max = Millimeter2iu( 0.01 );
// This is a call back function, used by DrawGraphicText to draw the 3D text shape:
static void addTextSegmToPoly( int x0, int y0, int xf, int yf )
{
@ -68,7 +74,7 @@ void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_
{
// Number of segments to convert a circle to a polygon
const int segcountforcircle = 18;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
double correctionFactor = GetCircletoPolyCorrectionFactor( segcountforcircle );
// convert tracks and vias:
for( TRACK* track = m_Track; track != NULL; track = track->Next() )
@ -504,25 +510,12 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
if( m_Shape == S_CIRCLE || m_Shape == S_ARC )
{
// this is an ugly hack, but I don't want to change the file format now that we are in feature freeze.
// the circle to segment count parameter is essentially useless for larger circle/arc shapes as
// it doesn't provide sufficient approximation accuracy.
// Here we compute the necessary number of segments based on error between circle and segments
// The max error is the distance between the middle of a segment, and the circle
//
// Warning: too small values can create very long calculation time in zone filling
double error_max = Millimeter2iu( 0.05 );
// error relative to the radius value
double rel_error = error_max / GetRadius();
// best arc increment
double step = 180 / M_PI * acos( 1.0 - rel_error );
// the best seg count for a circle
int segCount = KiROUND( 360.0 / step );
int segCount = GetArcToSegmentCount( GetRadius(), s_error_max, 360.0 );
if( segCount > aCircleToSegmentsCount )
{
aCircleToSegmentsCount = segCount;
aCorrectionFactor = 1.0 / cos( M_PI / (aCircleToSegmentsCount * 2) );
aCorrectionFactor = GetCircletoPolyCorrectionFactor( aCircleToSegmentsCount );
}
}

View File

@ -35,6 +35,7 @@
#include <class_drawsegment.h>
#include <base_units.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/geometry_utils.h>
/**
@ -167,16 +168,11 @@ static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT*
}
// 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 )
int calcArcSteps( int aRadius, int aSweptAngle )
{
int circleSteps = M_PI / acos( ( aRadius - ARC_ACCURACY ) / aRadius );
int arcSteps = circleSteps * fabs( aSweptAngle ) / 3600.0;
return std::max( arcSteps, 1 );
}
/**
* Function ConvertOutlineToPolygon
@ -238,7 +234,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
wxPoint pstart = graphic->GetArcStart();
wxPoint center = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = calcArcSteps( graphic->GetRadius(), angle );
int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
(double)angle / 10.0 );
wxPoint pt;
for( int step = 1; step<=steps; ++step )
@ -296,9 +293,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
// Output the Edge.Cuts perimeter as circle or polygon.
if( graphic->GetShape() == S_CIRCLE )
{
TransformCircleToPolygon( aPolygons, graphic->GetCenter(),
graphic->GetRadius(),
calcArcSteps( graphic->GetRadius(), 3600 ) );
int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 );
TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), steps );
}
else
{
@ -344,7 +340,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
wxPoint pend = graphic->GetArcEnd();
wxPoint pcenter = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = calcArcSteps( graphic->GetRadius(), angle );
int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
(double)angle / 10.0 );
if( !close_enough( prevPt, pstart, prox ) )
{
@ -436,7 +433,7 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
double angle = 3600.0;
wxPoint start = center;
int radius = graphic->GetRadius();
int steps = calcArcSteps( radius, angle );
int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY, 360.0 );
wxPoint nextPt;
start.x += radius;
@ -494,7 +491,8 @@ bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
wxPoint pend = graphic->GetArcEnd();
wxPoint pcenter = graphic->GetCenter();
double angle = -graphic->GetAngle();
int steps = calcArcSteps( graphic->GetRadius(), angle );
int steps = GetArcToSegmentCount( graphic->GetRadius(), ARC_ACCURACY,
(double)angle / 10.0 );
if( !close_enough( prevPt, pstart, prox ) )
{

View File

@ -56,6 +56,7 @@
#include <geometry/shape_poly_set.h>
#include <geometry/convex_hull.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/geometry_utils.h>
#include "specctra.h"
@ -765,11 +766,13 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule )
double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
const int SEG_PER_CIRCLE = 36; // seg count to approximate circle by line segments
// seg count to approximate circle by line segments
int err_max = Millimeter2iu( 0.05 );
int seg_per_circle = GetArcToSegmentCount( radius, err_max, 360.0 );
for( int ii = 0; ii < SEG_PER_CIRCLE; ++ii )
for( int ii = 0; ii < seg_per_circle; ++ii )
{
double radians = 2*M_PI / SEG_PER_CIRCLE * ii;
double radians = 2*M_PI / seg_per_circle * ii;
wxPoint point( KiROUND( radius * cos( radians ) ),
KiROUND( radius * sin( radians ) ) );

View File

@ -44,6 +44,7 @@
#include <geometry/shape_poly_set.h>
#include <geometry/shape_file_io.h>
#include <geometry/convex_hull.h>
#include <geometry/geometry_utils.h>
#include "zone_filler.h"
@ -299,10 +300,10 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
/* calculates the coeff to compensate radius reduction of holes clearance
* due to the segment approx.
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
* correctionFactor is 1 /cos( PI/s_CircleToSegmentsCount )
* For a circle the min radius is radius * cos( 2PI / segsPerCircle / 2)
* correctionFactor is 1 /cos( PI/segsPerCircle )
*/
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
correctionFactor = GetCircletoPolyCorrectionFactor( segsPerCircle );
aFeatures.RemoveAllContours();
@ -683,11 +684,8 @@ void ZONE_FILLER::computeRawFilledAreas( const ZONE_CONTAINER* aZone,
segsPerCircle = ARC_APPROX_SEGMENTS_COUNT_LOW_DEF;
/* calculates the coeff to compensate radius reduction of holes clearance
* due to the segment approx.
* For a circle the min radius is radius * cos( 2PI / s_CircleToSegmentsCount / 2)
* s_Correction is 1 /cos( PI/s_CircleToSegmentsCount )
*/
correctionFactor = 1.0 / cos( M_PI / (double) segsPerCircle );
correctionFactor = GetCircletoPolyCorrectionFactor( segsPerCircle );
if( s_DumpZonesWhenFilling )
dumper->BeginGroup( "clipper-zone" );