Fix some issues related to SHAPE_ARC:
- Some are related to shape errors when the allowed error to approximate circle by segment is large and arc radius small. - fix the actual error used in ConvertToPolyline(). - Use SHAPE_ARC::DefaultAccuracyForPCB() instead of a fixed value as extra margin in zones. It should not change something, because it is also a fixed value (5 micrometers), but it is not a magic number. -TransformArcToPolygon() fix some issues and add a new algo, based on the arc actual outline shape (initial algo is still available in code, just in case).
This commit is contained in:
parent
0d8f0d361e
commit
17737af130
|
@ -164,6 +164,15 @@ public:
|
|||
*/
|
||||
double GetLength() const;
|
||||
|
||||
/**
|
||||
* @return a default accuray value for ConvertToPolyline() to build the polyline.
|
||||
* ** Note that the default is ARC_HIGH_DEF in PCBNew units
|
||||
* This is to allow common geometry collision functions
|
||||
* Other programs should call this using explicit accuracy values
|
||||
* TODO: unify KiCad internal units
|
||||
*/
|
||||
static double DefaultAccuracyForPCB(){ return 0.005 * PCB_IU_PER_MM; }
|
||||
|
||||
/**
|
||||
* Constructs a SHAPE_LINE_CHAIN of segments from a given arc
|
||||
* @param aAccuracy maximum divergence from true arc given in internal units
|
||||
|
@ -171,10 +180,14 @@ public:
|
|||
* This is to allow common geometry collision functions
|
||||
* Other programs should call this using explicit accuracy values
|
||||
* TODO: unify KiCad internal units
|
||||
* @param aEffectiveAccuracy is the actual divergence from true arc given
|
||||
* the approximation error is between -aEffectiveAccuracy/2 and +aEffectiveAccuracy/2
|
||||
* in internal units
|
||||
*
|
||||
* @return a SHAPE_LINE_CHAIN
|
||||
*/
|
||||
const SHAPE_LINE_CHAIN ConvertToPolyline( double aAccuracy = 0.005 * PCB_IU_PER_MM ) const;
|
||||
const SHAPE_LINE_CHAIN ConvertToPolyline( double aAccuracy = DefaultAccuracyForPCB(),
|
||||
double* aEffectiveAccuracy = nullptr ) const;
|
||||
|
||||
private:
|
||||
bool ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) const
|
||||
|
|
|
@ -377,23 +377,120 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const
|
|||
}
|
||||
|
||||
|
||||
static int convertArcToPolyline( SHAPE_LINE_CHAIN& aPolyline, VECTOR2I aCenter, int aRadius,
|
||||
double aStartAngle, double aArcAngle, double aAccuracy,
|
||||
ERROR_LOC aErrorLoc )
|
||||
{
|
||||
double endAngle = aStartAngle + aArcAngle;
|
||||
int n = 2;
|
||||
|
||||
if( aRadius >= aAccuracy )
|
||||
n = GetArcToSegmentCount( aRadius, aAccuracy, aArcAngle )+1; // n >= 3
|
||||
|
||||
if( aErrorLoc == ERROR_OUTSIDE )
|
||||
{
|
||||
int seg360 = std::abs( KiROUND( n * 360.0 / aArcAngle ) );
|
||||
int actual_delta_radius = CircleToEndSegmentDeltaRadius( aRadius, seg360 );
|
||||
aRadius += actual_delta_radius;
|
||||
}
|
||||
|
||||
for( int i = 0; i <= n ; i++ )
|
||||
{
|
||||
double rot = aStartAngle;
|
||||
rot += ( aArcAngle * i ) / n;
|
||||
|
||||
double x = aCenter.x + aRadius * cos( rot * M_PI / 180.0 );
|
||||
double y = aCenter.y + aRadius * sin( rot * M_PI / 180.0 );
|
||||
|
||||
aPolyline.Append( KiROUND( x ), KiROUND( y ) );
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoint aMid,
|
||||
wxPoint aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc )
|
||||
{
|
||||
SHAPE_ARC arc( aStart, aMid, aEnd, aWidth );
|
||||
SHAPE_LINE_CHAIN arcSpine = arc.ConvertToPolyline( aError );
|
||||
// Currentlye have currently 2 algos:
|
||||
// the first approximates the thick arc from its outlines
|
||||
// the second approximates the thick arc from segments given by SHAPE_ARC
|
||||
// using SHAPE_ARC::ConvertToPolyline
|
||||
// The actual approximation errors are similar but not exactly the same.
|
||||
//
|
||||
// For now, both algorithms are kept, the second is the initial algo used in Kicad.
|
||||
|
||||
#if 1
|
||||
// This appproximation convert the 2 ends to polygons, arc outer to polyline
|
||||
// and arc inner to polyline and merge shapes.
|
||||
int radial_offset = ( aWidth + 1 ) / 2;
|
||||
|
||||
SHAPE_POLY_SET polyshape;
|
||||
std::vector<VECTOR2I> outside_pts;
|
||||
|
||||
/// We start by making rounded ends on the arc
|
||||
TransformCircleToPolygon( polyshape,
|
||||
wxPoint( arcSpine.GetPoint( 0 ).x, arcSpine.GetPoint( 0 ).y ), radial_offset, aError,
|
||||
aErrorLoc );
|
||||
TransformCircleToPolygon( polyshape, aStart, radial_offset, aError, aErrorLoc );
|
||||
TransformCircleToPolygon( polyshape, aEnd, radial_offset, aError, aErrorLoc );
|
||||
|
||||
TransformCircleToPolygon( polyshape,
|
||||
wxPoint( arcSpine.GetPoint( -1 ).x, arcSpine.GetPoint( -1 ).y ), radial_offset, aError,
|
||||
aErrorLoc );
|
||||
// The circle polygon is built with a even number of segments, so the
|
||||
// horizontal diameter has 2 corners on the biggest diameter
|
||||
// Rotate these 2 corners to match the start and ens points of inner and outer
|
||||
// end points of the arc appoximation outlines, build below.
|
||||
// The final shape is much better.
|
||||
double arc_angle_start_deg = arc.GetStartAngle();
|
||||
double arc_angle = arc.GetCentralAngle();
|
||||
double arc_angle_end_deg = arc_angle_start_deg + arc_angle;
|
||||
|
||||
if( arc_angle_start_deg != 0 && arc_angle_start_deg != 180.0 )
|
||||
polyshape.Outline(0).Rotate( arc_angle_start_deg * M_PI/180.0, aStart );
|
||||
|
||||
if( arc_angle_end_deg != 0 && arc_angle_end_deg != 180.0 )
|
||||
polyshape.Outline(1).Rotate( arc_angle_end_deg * M_PI/180.0, aEnd );
|
||||
|
||||
VECTOR2I center = arc.GetCenter();
|
||||
int radius = arc.GetRadius();
|
||||
|
||||
int arc_outer_radius = radius + radial_offset;
|
||||
int arc_inner_radius = radius - radial_offset;
|
||||
ERROR_LOC errorLocInner = ERROR_OUTSIDE;
|
||||
ERROR_LOC errorLocOuter = ERROR_INSIDE;
|
||||
|
||||
if( aErrorLoc == ERROR_OUTSIDE )
|
||||
{
|
||||
errorLocInner = ERROR_INSIDE;
|
||||
errorLocOuter = ERROR_OUTSIDE;
|
||||
}
|
||||
|
||||
polyshape.NewOutline();
|
||||
|
||||
convertArcToPolyline( polyshape.Outline(2), center, arc_outer_radius,
|
||||
arc_angle_start_deg, arc_angle, aError, errorLocOuter );
|
||||
|
||||
if( arc_inner_radius > 0 )
|
||||
convertArcToPolyline( polyshape.Outline(2), center, arc_inner_radius,
|
||||
arc_angle_end_deg, -arc_angle, aError, errorLocInner );
|
||||
else
|
||||
polyshape.Append( center );
|
||||
#else
|
||||
// This appproximation use SHAPE_ARC to convert the 2 ends to polygons,
|
||||
// approximate arc to polyline, convert the polyline corners to outer and inner
|
||||
// corners of outer and inner utliners and merge shapes.
|
||||
double defaultErr;
|
||||
SHAPE_LINE_CHAIN arcSpine = arc.ConvertToPolyline( SHAPE_ARC::DefaultAccuracyForPCB(),
|
||||
&defaultErr);
|
||||
int radius = arc.GetRadius();
|
||||
int radial_offset = ( aWidth + 1 ) / 2;
|
||||
SHAPE_POLY_SET polyshape;
|
||||
std::vector<VECTOR2I> outside_pts;
|
||||
|
||||
// delta is the effective error approximation to build a polyline from an arc
|
||||
int segCnt360 = arcSpine.GetSegmentCount()*360.0/arc.GetCentralAngle();;
|
||||
int delta = CircleToEndSegmentDeltaRadius( radius+radial_offset, std::abs(segCnt360) );
|
||||
|
||||
/// We start by making rounded ends on the arc
|
||||
TransformCircleToPolygon( polyshape, aStart, radial_offset, aError, aErrorLoc );
|
||||
TransformCircleToPolygon( polyshape, aEnd, radial_offset, aError, aErrorLoc );
|
||||
|
||||
// The circle polygon is built with a even number of segments, so the
|
||||
// horizontal diameter has 2 corners on the biggest diameter
|
||||
|
@ -411,9 +508,9 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoi
|
|||
polyshape.Outline(1).Rotate( arc_angle_end_deg * M_PI/180.0, arcSpine.GetPoint( -1 ) );
|
||||
|
||||
if( aErrorLoc == ERROR_OUTSIDE )
|
||||
radial_offset += aError;
|
||||
radial_offset += delta + defaultErr/2;
|
||||
else
|
||||
radial_offset -= aError/2;
|
||||
radial_offset -= defaultErr/2;
|
||||
|
||||
if( radial_offset < 0 )
|
||||
radial_offset = 0;
|
||||
|
@ -421,7 +518,6 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoi
|
|||
polyshape.NewOutline();
|
||||
|
||||
VECTOR2I center = arc.GetCenter();
|
||||
int radius = ( arc.GetP0() - center ).EuclideanNorm();
|
||||
int last_index = arcSpine.GetPointCount() -1;
|
||||
|
||||
for( std::size_t ii = 0; ii <= last_index; ++ii )
|
||||
|
@ -429,16 +525,13 @@ void TransformArcToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPoi
|
|||
VECTOR2I offset = arcSpine.GetPoint( ii ) - center;
|
||||
int curr_rd = radius;
|
||||
|
||||
// This correction gives a better position of intermediate points of the sides of arc.
|
||||
if( ii > 0 && ii < last_index )
|
||||
curr_rd += aError/2;
|
||||
|
||||
polyshape.Append( offset.Resize( curr_rd - radial_offset ) + center );
|
||||
outside_pts.emplace_back( offset.Resize( curr_rd + radial_offset ) + center );
|
||||
}
|
||||
|
||||
for( auto it = outside_pts.rbegin(); it != outside_pts.rend(); ++it )
|
||||
polyshape.Append( *it );
|
||||
#endif
|
||||
|
||||
// Can be removed, but usefull to display the outline:
|
||||
polyshape.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2017 CERN
|
||||
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -414,12 +414,13 @@ double SHAPE_ARC::GetRadius() const
|
|||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy ) const
|
||||
const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy,
|
||||
double* aEffectiveAccuracy ) const
|
||||
{
|
||||
SHAPE_LINE_CHAIN rv;
|
||||
double r = GetRadius();
|
||||
double sa = GetStartAngle();
|
||||
auto c = GetCenter();
|
||||
VECTOR2I c = GetCenter();
|
||||
double ca = GetCentralAngle();
|
||||
|
||||
int n;
|
||||
|
@ -427,16 +428,29 @@ const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy ) const
|
|||
// To calculate the arc to segment count, use the external radius instead of the radius.
|
||||
// for a arc with small radius and large width, the difference can be significant
|
||||
double external_radius = r+(m_width/2);
|
||||
double effectiveAccuracy;
|
||||
|
||||
if( external_radius < aAccuracy )
|
||||
if( external_radius < aAccuracy/2 ) // Should be a very rare case
|
||||
{
|
||||
// In this case, the arc is approximated by one segment, with a effective error
|
||||
// between -aAccuracy/2 and +aAccuracy/2, as expected.
|
||||
n = 0;
|
||||
effectiveAccuracy = external_radius;
|
||||
}
|
||||
else
|
||||
n = GetArcToSegmentCount( external_radius, aAccuracy, ca );
|
||||
{
|
||||
double arc_angle = std::abs( ca );
|
||||
n = GetArcToSegmentCount( external_radius, aAccuracy, arc_angle );
|
||||
|
||||
// Recalculate the effective error of approximation, that can be < aAccuracy
|
||||
int seg360 = n * 360.0 / arc_angle;
|
||||
effectiveAccuracy = CircleToEndSegmentDeltaRadius( external_radius, seg360 );
|
||||
}
|
||||
|
||||
// Split the error on either side of the arc. Since we want the start and end points
|
||||
// to be exactly on the arc, the first and last segments need to be shorter to stay within
|
||||
// the error band (since segments normally start 1/2 the error band outside the arc).
|
||||
r += aAccuracy / 2;
|
||||
r += effectiveAccuracy / 2;
|
||||
n = n * 2;
|
||||
|
||||
rv.Append( m_start );
|
||||
|
@ -456,6 +470,9 @@ const SHAPE_LINE_CHAIN SHAPE_ARC::ConvertToPolyline( double aAccuracy ) const
|
|||
|
||||
rv.Append( m_end );
|
||||
|
||||
if( aEffectiveAccuracy )
|
||||
*aEffectiveAccuracy = effectiveAccuracy;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -768,12 +768,16 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
|
|||
}
|
||||
else
|
||||
{
|
||||
// Gives more clearance to arcs (the arc to area conv is not perfect)
|
||||
// Gives more clearance to arcs (the arc to area drc test is not perfect)
|
||||
// extra_margin is not enought here
|
||||
// This is a workaround, that can be removed when (if?) the arcs
|
||||
// This is a workaround, that can be removed when (if?) the DRC arcs
|
||||
// issues are fixed
|
||||
// The root cause is the fact the DRC approximates the arc shape
|
||||
// by a segmentlist with a error = +- SHAPE_ARC::DefaultAccuracyForPCB()/2
|
||||
// and the arc to polygons approximation also creates approxiamtions when
|
||||
// filling the zone
|
||||
if( aTrack->Type() == PCB_ARC_T )
|
||||
gap += Millimeter2iu( 0.004 );
|
||||
gap += SHAPE_ARC::DefaultAccuracyForPCB();
|
||||
|
||||
aTrack->TransformShapeWithClearanceToPolygon( aHoles, aLayer, gap,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
|
|
Loading…
Reference in New Issue