Give up on trying to get Clipper's miter strategies to work.

Instead, make sure we remove the clearance holes and outline at
the end so that copper isn't allowed to "spill" into areas it
shouldn't be in.

Also fixes a bug where the Advance Cfg defaults weren't getting
loaded when the file didn't exist.

Fixes https://gitlab.com/kicad/code/kicad/issues/3812

Fixes https://gitlab.com/kicad/code/kicad/issues/5306
This commit is contained in:
Jeff Young 2020-08-25 19:03:21 +01:00
parent 0d65d76a9a
commit 6d12859c14
3 changed files with 37 additions and 47 deletions

View File

@ -216,11 +216,16 @@ const ADVANCED_CFG& ADVANCED_CFG::GetCfg()
void ADVANCED_CFG::loadFromConfigFile() void ADVANCED_CFG::loadFromConfigFile()
{ {
const auto k_advanced = getAdvancedCfgFilename(); const wxFileName k_advanced = getAdvancedCfgFilename();
if( !k_advanced.FileExists() ) if( !k_advanced.FileExists() )
{ {
wxLogTrace( AdvancedConfigMask, "File does not exist %s", k_advanced.GetFullPath() ); wxLogTrace( AdvancedConfigMask, "File does not exist %s", k_advanced.GetFullPath() );
// load the defaults
wxConfig emptyConfig;
loadSettings( emptyConfig );
return; return;
} }
@ -239,7 +244,7 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
&m_realTimeConnectivity, true ) ); &m_realTimeConnectivity, true ) );
configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::ExtraFillMargin, configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::ExtraFillMargin,
&m_extraClearance, 0.002, 0.0, 1.0 ) ); &m_extraClearance, 0.001, 0.0, 1.0 ) );
configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::CoroutineStackSize, configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::CoroutineStackSize,
&m_coroutineStackSize, AC_STACK::default_stack, &m_coroutineStackSize, AC_STACK::default_stack,

View File

@ -1213,7 +1213,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER
case ZONE_SETTINGS::SMOOTHING_FILLET: case ZONE_SETTINGS::SMOOTHING_FILLET:
{ {
auto board = GetBoard(); BOARD* board = GetBoard();
int maxError = ARC_HIGH_DEF; int maxError = ARC_HIGH_DEF;
if( board ) if( board )
@ -1222,13 +1222,8 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER
aSmoothedPoly = aSmoothedPoly.Fillet( m_cornerRadius, maxError ); aSmoothedPoly = aSmoothedPoly.Fillet( m_cornerRadius, maxError );
break; break;
} }
default: default:
// Acute angles between adjacent edges can create issues in calculations,
// in inflate/deflate outlines transforms, especially when the angle is very small.
// We can avoid issues by creating a very small chamfer which remove acute angles,
// or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
// clearance areas
aSmoothedPoly = aSmoothedPoly.Chamfer( Millimeter2iu( 0.0 ) );
break; break;
} }

View File

@ -572,18 +572,18 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
dummyEdge.SetParent( m_board ); dummyEdge.SetParent( m_board );
dummyEdge.SetLayer( Edge_Cuts ); dummyEdge.SetLayer( Edge_Cuts );
// a small extra clearance to be sure actual track clearance is not smaller // A small extra clearance to be sure actual track clearances are not smaller than
// than requested clearance due to many approximations in calculations, // requested clearance due to many approximations in calculations, like arc to segment
// like arc to segment approx, rounding issues... // approx, rounding issues, etc.
// 2 microns are a good value // 1 micron is a good value
int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_extraClearance ); int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_extraClearance );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
int zone_clearance = aZone->GetLocalClearance(); int zone_clearance = aZone->GetLocalClearance();
EDA_RECT zone_boundingbox = aZone->GetBoundingBox(); EDA_RECT zone_boundingbox = aZone->GetBoundingBox();
// items outside the zone bounding box are skipped, so it needs to be inflated by // Items outside the zone bounding box are skipped, so it needs to be inflated by the
// the largest clearance value found in the netclasses and rules // largest clearance value found in the netclasses and rules
int biggest_clearance = std::max( zone_clearance, bds.GetBiggestClearanceValue() ); int biggest_clearance = std::max( zone_clearance, bds.GetBiggestClearanceValue() );
zone_boundingbox.Inflate( biggest_clearance + extra_margin ); zone_boundingbox.Inflate( biggest_clearance + extra_margin );
@ -685,7 +685,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
ignoreLineWidth = true; ignoreLineWidth = true;
} }
int gap = aZone->GetClearance( aLayer, aItem ); int gap = aZone->GetClearance( aLayer, aItem ) + extra_margin;
addKnockout( aItem, layer, gap, ignoreLineWidth, aHoles ); addKnockout( aItem, layer, gap, ignoreLineWidth, aHoles );
} }
@ -756,7 +756,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
SHAPE_POLY_SET& aFinalPolys ) SHAPE_POLY_SET& aFinalPolys )
{ {
m_high_def = m_board->GetDesignSettings().m_MaxError; m_high_def = m_board->GetDesignSettings().m_MaxError;
m_low_def = std::min( ARC_LOW_DEF, int( m_high_def*1.5 ) ); // Reasonable value m_low_def = std::min( ARC_LOW_DEF, int( m_high_def * 1.5 ) ); // Reasonable value
// Features which are min_width should survive pruning; features that are *less* than // Features which are min_width should survive pruning; features that are *less* than
// min_width should not. Therefore we subtract epsilon from the min_width when // min_width should not. Therefore we subtract epsilon from the min_width when
@ -765,19 +765,12 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
int epsilon = Millimeter2iu( 0.001 ); int epsilon = Millimeter2iu( 0.001 );
int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 ); int numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 );
// solid polygons are deflated and inflated during calculations. SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy;
// Polygons deflate usually do not create issues.
// Polygons inflate is a tricky transform, because it can create excessively long and narrow 'spikes' if( aZone->GetCornerSmoothingType() == ZONE_SETTINGS::SMOOTHING_FILLET )
// especially for acute angles. cornerStrategy = SHAPE_POLY_SET::ROUND_ACUTE_CORNERS;
// But in very case, the inflate transform caannot create bigger shapes than initial shapes. else
// so the corner strategy is very important. cornerStrategy = SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS;
// 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; std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
SHAPE_POLY_SET clearanceHoles; SHAPE_POLY_SET clearanceHoles;
@ -823,8 +816,8 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
// Prune features that don't meet minimum-width criteria // Prune features that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon ) if( half_min_width - epsilon > epsilon )
{ {
testAreas.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); testAreas.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); testAreas.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
} }
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
@ -868,11 +861,6 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
return; return;
// Ensure previous changes (adding thermal stubs) do not add
// filled areas outside the zone boundary
aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" ); dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" );
@ -880,9 +868,10 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
return; return;
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
// Prune features that don't meet minimum-width criteria // Prune features that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon ) if( half_min_width - epsilon > epsilon )
aRawPolys.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); aRawPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-before-hatching" ); dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
@ -903,20 +892,21 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
// Re-inflate after pruning of areas that don't meet minimum-width criteria // Re-inflate after pruning of areas that don't meet minimum-width criteria
if( aZone->GetFilledPolysUseThickness() ) if( aZone->GetFilledPolysUseThickness() )
{ {
// If we're stroking the zone with a min_width stroke then this will naturally // If we're stroking the zone with a min_width stroke then this will naturally inflate
// inflate the zone by half_min_width // the zone by half_min_width
} }
else if( half_min_width - epsilon > epsilon ) else if( half_min_width - epsilon > epsilon )
{ {
aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
aRawPolys.Inflate( half_min_width - epsilon, numSegs, finalcornerStrategy ); aRawPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
// 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.
if( aZone->GetMinThickness() > (int)aZone->GetCornerRadius() )
aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
} }
// Ensure additive changes (thermal stubs and particularly inflating acute corners) do not
// add copper outside the zone boundary or inside the clearance holes
aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST ); aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
@ -966,7 +956,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
if( m_brdOutlinesValid ) if( m_brdOutlinesValid )
smoothedPoly.BooleanIntersection( m_boardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); 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 // Remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN ) if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )