From 6d12859c147d813788857be0b71133b961caa0fb Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Tue, 25 Aug 2020 19:03:21 +0100 Subject: [PATCH] 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 --- common/advanced_config.cpp | 9 ++++-- pcbnew/class_zone.cpp | 9 ++---- pcbnew/zone_filler.cpp | 66 ++++++++++++++++---------------------- 3 files changed, 37 insertions(+), 47 deletions(-) diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 9541dd4962..7fbc8f2211 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -216,11 +216,16 @@ const ADVANCED_CFG& ADVANCED_CFG::GetCfg() void ADVANCED_CFG::loadFromConfigFile() { - const auto k_advanced = getAdvancedCfgFilename(); + const wxFileName k_advanced = getAdvancedCfgFilename(); if( !k_advanced.FileExists() ) { wxLogTrace( AdvancedConfigMask, "File does not exist %s", k_advanced.GetFullPath() ); + + // load the defaults + wxConfig emptyConfig; + loadSettings( emptyConfig ); + return; } @@ -239,7 +244,7 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) &m_realTimeConnectivity, true ) ); 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, &m_coroutineStackSize, AC_STACK::default_stack, diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index d220e5a839..d6285cd74f 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -1213,7 +1213,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER case ZONE_SETTINGS::SMOOTHING_FILLET: { - auto board = GetBoard(); + BOARD* board = GetBoard(); int maxError = ARC_HIGH_DEF; if( board ) @@ -1222,13 +1222,8 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER aSmoothedPoly = aSmoothedPoly.Fillet( m_cornerRadius, maxError ); break; } + 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; } diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 09cf5f6c1f..16d436737c 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -572,18 +572,18 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA dummyEdge.SetParent( m_board ); dummyEdge.SetLayer( Edge_Cuts ); - // 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 + // A small extra clearance to be sure actual track clearances are not smaller than + // requested clearance due to many approximations in calculations, like arc to segment + // approx, rounding issues, etc. + // 1 micron is a good value int extra_margin = Millimeter2iu( ADVANCED_CFG::GetCfg().m_extraClearance ); BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); int zone_clearance = aZone->GetLocalClearance(); EDA_RECT zone_boundingbox = aZone->GetBoundingBox(); - // items outside the zone bounding box are skipped, so it needs to be inflated by - // the largest clearance value found in the netclasses and rules + // Items outside the zone bounding box are skipped, so it needs to be inflated by the + // largest clearance value found in the netclasses and rules int biggest_clearance = std::max( zone_clearance, bds.GetBiggestClearanceValue() ); zone_boundingbox.Inflate( biggest_clearance + extra_margin ); @@ -685,7 +685,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA ignoreLineWidth = true; } - int gap = aZone->GetClearance( aLayer, aItem ); + int gap = aZone->GetClearance( aLayer, aItem ) + extra_margin; 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 ) { 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 // 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 numSegs = std::max( GetArcToSegmentCount( half_min_width, m_high_def, 360.0 ), 6 ); - // 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; + SHAPE_POLY_SET::CORNER_STRATEGY cornerStrategy; + + if( aZone->GetCornerSmoothingType() == ZONE_SETTINGS::SMOOTHING_FILLET ) + cornerStrategy = SHAPE_POLY_SET::ROUND_ACUTE_CORNERS; + else + cornerStrategy = SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS; std::deque thermalSpokes; 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 if( half_min_width - epsilon > epsilon ) { - testAreas.Deflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); - testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); + testAreas.Deflate( half_min_width - epsilon, numSegs, cornerStrategy ); + testAreas.Inflate( half_min_width - epsilon, numSegs, cornerStrategy ); } 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() ) 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 ) dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" ); @@ -880,9 +868,10 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I return; 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, intermediatecornerStrategy ); + aRawPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy ); if( s_DumpZonesWhenFilling ) 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 if( aZone->GetFilledPolysUseThickness() ) { - // If we're stroking the zone with a min_width stroke then this will naturally - // inflate the zone by half_min_width + // If we're stroking the zone with a min_width stroke then this will naturally inflate + // the zone by half_min_width } else if( half_min_width - epsilon > epsilon ) { aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); - 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. - if( aZone->GetMinThickness() > (int)aZone->GetCornerRadius() ) - aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST ); + aRawPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy ); } + // 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 ); if( s_DumpZonesWhenFilling ) @@ -966,7 +956,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, if( m_brdOutlinesValid ) 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 )