Zone filler: fix a few issues, in Smoothed Polygons mode.
- Fix non optimal shape of solid polygons after inflate transform. - Add a small extra clearance ((2 microns) when creating clearance areas (especially, convert arc to polygons create small differences) - Add a small threshold (1 micron) in drc test distances to avoid false positives - fix a minor issue in TransformOvalToPolygon that created sometimes a not perfect shape Fixes #3812 https://gitlab.com/kicad/code/kicad/issues/3812
This commit is contained in:
parent
88e0ef548d
commit
48ae188b15
|
@ -865,12 +865,16 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b,
|
||||
POLYGON_MODE aFastMode );
|
||||
|
||||
enum CORNER_STRATEGY
|
||||
enum CORNER_STRATEGY ///< define how inflate transform build inflated polygon
|
||||
{
|
||||
ALLOW_ACUTE_CORNERS,
|
||||
CHOP_ACUTE_CORNERS,
|
||||
ROUND_ACUTE_CORNERS,
|
||||
ROUND_ALL_CORNERS
|
||||
ALLOW_ACUTE_CORNERS, ///< just inflate the polygon. Acute angles create spikes
|
||||
CHAMFER_ACUTE_CORNERS, ///< Acute angles are chamfered
|
||||
ROUND_ACUTE_CORNERS, ///< Acute angles are rounded
|
||||
CHAMFER_ALL_CORNERS, ///< All angles are chamfered.
|
||||
///< The distance between new and old polygon edges is not
|
||||
///< constant, but do not change a lot
|
||||
ROUND_ALL_CORNERS ///< All angles are rounded.
|
||||
///< The distance between new and old polygon edges is constant
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -97,6 +97,12 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
|
|||
// of the segment.
|
||||
int radius = aWidth / 2;
|
||||
int numSegs = std::max( GetArcToSegmentCount( radius, aError, 360.0 ), 6 );
|
||||
|
||||
// Because we want to create 2 arcs (one at each segment end) numSegs must be
|
||||
// a even value (we will used numSegs/2 later)
|
||||
if( numSegs % 2 != 0 )
|
||||
numSegs++;
|
||||
|
||||
int delta = 3600 / numSegs; // rotate angle in 0.1 degree
|
||||
double correction = GetCircletoPolyCorrectionFactor( numSegs );
|
||||
|
||||
|
|
|
@ -538,9 +538,38 @@ void SHAPE_POLY_SET::Inflate( int aAmount, int aCircleSegmentsCount,
|
|||
// N.B. see the Clipper documentation for jtSquare/jtMiter/jtRound. They are poorly named
|
||||
// and are not what you'd think they are.
|
||||
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/JoinType.htm
|
||||
JoinType joinType = aCornerStrategy == ROUND_ALL_CORNERS ? jtRound : jtMiter;
|
||||
double miterLimit = aCornerStrategy == ALLOW_ACUTE_CORNERS ? 10 : 1.5;
|
||||
JoinType miterFallback = aCornerStrategy == ROUND_ACUTE_CORNERS ? jtRound : jtSquare;
|
||||
JoinType joinType; // The way corners are offsetted
|
||||
double miterLimit = 2.0; // Smaller value when using jtMiter for joinType
|
||||
JoinType miterFallback;
|
||||
|
||||
switch( aCornerStrategy )
|
||||
{
|
||||
case ALLOW_ACUTE_CORNERS:
|
||||
joinType = jtMiter;
|
||||
miterLimit = 10; // Allows large spikes
|
||||
miterFallback = jtSquare;
|
||||
break;
|
||||
|
||||
case CHAMFER_ACUTE_CORNERS: // Acute angles are chamfered
|
||||
joinType = jtMiter;
|
||||
miterFallback = jtRound;
|
||||
break;
|
||||
|
||||
case ROUND_ACUTE_CORNERS: // Acute angles are rounded
|
||||
joinType = jtMiter;
|
||||
miterFallback = jtSquare;
|
||||
break;
|
||||
|
||||
case CHAMFER_ALL_CORNERS: // All angles are chamfered.
|
||||
joinType = jtSquare;
|
||||
miterFallback = jtSquare;
|
||||
break;
|
||||
|
||||
case ROUND_ALL_CORNERS: // All angles are rounded.
|
||||
joinType = jtRound;
|
||||
miterFallback = jtSquare;
|
||||
break;
|
||||
}
|
||||
|
||||
for( const POLYGON& poly : m_polys )
|
||||
{
|
||||
|
|
|
@ -675,7 +675,13 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato
|
|||
int clearance = std::max( ref_seg_clearance, zone->GetClearance() );
|
||||
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
|
||||
|
||||
if( outline->Distance( refSeg, ref_seg_width ) < clearance )
|
||||
int error = clearance - outline->Distance( refSeg, ref_seg_width );
|
||||
|
||||
// to avoid false positive, due to rounding issues and approxiamtions
|
||||
// in distance and clearance calculations, use a small threshold for distance
|
||||
// (1 micron)
|
||||
#define THRESHOLD_DIST Millimeter2iu( 0.001 )
|
||||
if( error > THRESHOLD_DIST )
|
||||
addMarkerToPcb( m_markerFactory.NewMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,6 +514,12 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POL
|
|||
*/
|
||||
void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aHoles )
|
||||
{
|
||||
// a small extra clearance to be sure actual track clearance is not smaller
|
||||
// than requested clearance due to many approximations in calculations,
|
||||
// like arc to segment approx, rounding issues...
|
||||
// 2 microns are a good value
|
||||
int extra_margin = Millimeter2iu( 0.002 );
|
||||
|
||||
int zone_clearance = aZone->GetClearance();
|
||||
int edgeClearance = m_board->GetDesignSettings().m_CopperEdgeClearance;
|
||||
int zone_to_edgecut_clearance = std::max( aZone->GetZoneClearance(), edgeClearance );
|
||||
|
@ -522,7 +528,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
|
|||
// the bounding box is the zone bounding box + the biggest clearance found in Netclass list
|
||||
EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
|
||||
int biggest_clearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
|
||||
biggest_clearance = std::max( biggest_clearance, zone_clearance );
|
||||
biggest_clearance = std::max( biggest_clearance, zone_clearance ) + extra_margin;
|
||||
zone_boundingbox.Inflate( biggest_clearance );
|
||||
|
||||
// Use a dummy pad to calculate hole clearance when a pad has a hole but is not on the
|
||||
|
@ -570,7 +576,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
|
|||
if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) )
|
||||
continue;
|
||||
|
||||
int gap = std::max( zone_clearance, track->GetClearance() );
|
||||
int gap = std::max( zone_clearance, track->GetClearance() ) + extra_margin;
|
||||
EDA_RECT item_boundingbox = track->GetBoundingBox();
|
||||
|
||||
if( item_boundingbox.Intersects( zone_boundingbox ) )
|
||||
|
@ -686,10 +692,19 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
int epsilon = Millimeter2iu( 0.001 );
|
||||
int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
|
||||
|
||||
SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy = SHAPE_POLY_SET::CHOP_ACUTE_CORNERS;
|
||||
|
||||
if( aZone->GetCornerSmoothingType() == ZONE_SETTINGS::SMOOTHING_FILLET )
|
||||
cornerStrategy = SHAPE_POLY_SET::ROUND_ACUTE_CORNERS;
|
||||
// solid polygons are deflated and inflated during calculations.
|
||||
// Polygons deflate usually do not create issues.
|
||||
// Polygons inflate is a tricky transform, because it can create excessively long and narrow 'spikes'
|
||||
// especially for acute angles.
|
||||
// But in very case, the inflate transform caannot create bigger shapes than initial shapes.
|
||||
// so the corner strategy is very important.
|
||||
// The best is SHAPE_POLY_SET::ROUND_ALL_CORNERS.
|
||||
// unfortunately, it creates a lot of small segments.
|
||||
// SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS is not acceptable
|
||||
// So for intermediate transforms, we use CHAMFER_ALL_CORNERS.
|
||||
// For final transform, we use ROUND_ALL_CORNERS
|
||||
SHAPE_POLY_SET::CORNER_STRATEGY intermediatecornerStrategy = SHAPE_POLY_SET::CHAMFER_ALL_CORNERS;
|
||||
SHAPE_POLY_SET::CORNER_STRATEGY finalcornerStrategy = SHAPE_POLY_SET::ROUND_ALL_CORNERS;
|
||||
|
||||
std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
|
||||
SHAPE_POLY_SET clearanceHoles;
|
||||
|
@ -723,8 +738,8 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
// Prune features that don't meet minimum-width criteria
|
||||
if( half_min_width - epsilon > epsilon )
|
||||
{
|
||||
testAreas.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
|
||||
testAreas.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
|
||||
testAreas.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
|
||||
testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
|
||||
}
|
||||
|
||||
// Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
|
||||
|
@ -764,7 +779,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
|
||||
// Prune features that don't meet minimum-width criteria
|
||||
if( half_min_width - epsilon > epsilon )
|
||||
aRawPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
|
||||
aRawPolys.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
|
||||
|
||||
if( s_DumpZonesWhenFilling )
|
||||
dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
|
||||
|
@ -785,7 +800,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
else if( half_min_width - epsilon > epsilon )
|
||||
{
|
||||
aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
aRawPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
|
||||
aRawPolys.Inflate( half_min_width - epsilon, numSegs, finalcornerStrategy );
|
||||
|
||||
// If we've deflated/inflated by something near our corner radius then we will have
|
||||
// ended up with too-sharp corners. Apply outline smoothing again.
|
||||
|
@ -839,9 +854,9 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
|
|||
int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
|
||||
|
||||
if( m_brdOutlinesValid )
|
||||
smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_FAST );
|
||||
smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
||||
smoothedPoly.Deflate( half_min_width - epsilon, numSegs );
|
||||
smoothedPoly.Deflate( half_min_width/* - epsilon*/, numSegs );
|
||||
|
||||
// Remove the non filled areas due to the hatch pattern
|
||||
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
|
||||
|
|
|
@ -58,24 +58,25 @@ enum class ZONE_HATCH_STYLE
|
|||
class ZONE_SETTINGS
|
||||
{
|
||||
public:
|
||||
// the actual zone outline shape can be slightly modified (smoothed):
|
||||
enum {
|
||||
SMOOTHING_UNDEFINED = -1,
|
||||
SMOOTHING_NONE = 0,
|
||||
SMOOTHING_CHAMFER,
|
||||
SMOOTHING_FILLET,
|
||||
SMOOTHING_LAST
|
||||
SMOOTHING_NONE = 0, // Zone outline is used without change
|
||||
SMOOTHING_CHAMFER, // Zone outline is used after chamfering corners
|
||||
SMOOTHING_FILLET, // Zone outline is used after rounding corners
|
||||
SMOOTHING_LAST // sentinel
|
||||
};
|
||||
|
||||
ZONE_FILL_MODE m_FillMode;
|
||||
|
||||
int m_ZonePriority; ///< Priority (0 ... N) of the zone
|
||||
|
||||
int m_ZoneClearance; ///< Clearance value
|
||||
int m_ZoneClearance; ///< Minimal clearance value
|
||||
int m_ZoneMinThickness; ///< Min thickness value in filled areas
|
||||
int m_HatchFillTypeThickness; ///< Grid style shape: thickness of lines (if 0 -> solid shape)
|
||||
int m_HatchFillTypeGap; ///< Grid style shape: clearance between lines (0 -> solid shape)
|
||||
double m_HatchFillTypeOrientation; ///< Grid style shape: orientation of grid lines in degrees
|
||||
int m_HatchFillTypeSmoothingLevel; ///< Grid pattern smoothing type, similar to corner smoothing type
|
||||
int m_HatchFillTypeThickness; ///< Grid style shape: thickness of lines (if 0 -> solid shape)
|
||||
int m_HatchFillTypeGap; ///< Grid style shape: clearance between lines (0 -> solid shape)
|
||||
double m_HatchFillTypeOrientation; ///< Grid style shape: orientation of grid lines in degrees
|
||||
int m_HatchFillTypeSmoothingLevel; ///< Grid pattern smoothing type, similar to corner smoothing type
|
||||
///< 0 = no smoothing, 1 = fillet, >= 2 = arc
|
||||
double m_HatchFillTypeSmoothingValue; ///< Grid pattern chamfer distance/fillet value
|
||||
///< this is the ratio between the gap and the chamfer size
|
||||
|
|
Loading…
Reference in New Issue