From 3cf5db3ce56ec7d9a2b9bdc8314824c5031b3195 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Tue, 4 Aug 2020 19:53:16 +0100 Subject: [PATCH] Allow cancelling of zone fills. Fixes https://gitlab.com/kicad/code/kicad/issues/5035 --- common/widgets/progress_reporter.cpp | 14 +++- include/widgets/progress_reporter.h | 2 + pcbnew/zone_filler.cpp | 104 +++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 7 deletions(-) diff --git a/common/widgets/progress_reporter.cpp b/common/widgets/progress_reporter.cpp index c3a2d1e3a8..bf7b5f770e 100644 --- a/common/widgets/progress_reporter.cpp +++ b/common/widgets/progress_reporter.cpp @@ -30,7 +30,8 @@ PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) : m_phase( 0 ), m_numPhases( aNumPhases ), m_progress( 0 ), - m_maxProgress( 1 ) + m_maxProgress( 1 ), + m_cancelled( false ) { } @@ -90,7 +91,10 @@ bool PROGRESS_REPORTER::KeepRefreshing( bool aWait ) while( m_progress < m_maxProgress && m_maxProgress > 0 ) { if( !updateUI() ) + { + m_cancelled.store( true ); return false; + } wxMilliSleep( 20 ); } @@ -98,7 +102,13 @@ bool PROGRESS_REPORTER::KeepRefreshing( bool aWait ) } else { - return updateUI(); + if( !updateUI() ) + { + m_cancelled.store( true ); + return false; + } + + return true; } } diff --git a/include/widgets/progress_reporter.h b/include/widgets/progress_reporter.h index 5bc099d9e1..696f7bbdd6 100644 --- a/include/widgets/progress_reporter.h +++ b/include/widgets/progress_reporter.h @@ -92,6 +92,7 @@ class PROGRESS_REPORTER */ virtual void SetTitle( const wxString& aTitle ) {} + bool IsCancelled() const { return m_cancelled.load(); } protected: @@ -105,6 +106,7 @@ class PROGRESS_REPORTER std::atomic_int m_numPhases; std::atomic_int m_progress; std::atomic_int m_maxProgress; + std::atomic_bool m_cancelled; }; diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index d173859fe2..5d93212e13 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -159,8 +159,8 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck } std::atomic nextItem( 0 ); - size_t parallelThreadCount = - std::min( std::thread::hardware_concurrency(), aZones.size() ); + size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), + aZones.size() ); std::vector> returns( parallelThreadCount ); auto fill_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t @@ -184,8 +184,13 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck zone->SetIsFilled( true ); if( m_progressReporter ) + { m_progressReporter->AdvanceProgress(); + if( m_progressReporter->IsCancelled() ) + break; + } + num++; } @@ -206,8 +211,13 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck do { if( m_progressReporter ) + { m_progressReporter->KeepRefreshing(); + if( m_progressReporter->IsCancelled() ) + break; + } + status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) ); } while( status != std::future_status::ready ); } @@ -219,11 +229,29 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Removing insulated copper islands..." ) ); m_progressReporter->KeepRefreshing(); + + if( m_progressReporter->IsCancelled() ) + { + if( m_commit ) + m_commit->Revert(); + + connectivity->SetProgressReporter( nullptr ); + return false; + } } connectivity->SetProgressReporter( m_progressReporter ); connectivity->FindIsolatedCopperIslands( islandsList ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + { + if( m_commit ) + m_commit->Revert(); + + connectivity->SetProgressReporter( nullptr ); + return false; + } + // Now remove insulated copper islands and islands outside the board edge bool outOfDate = false; @@ -285,6 +313,15 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() ) outOfDate = true; + + if( m_progressReporter && m_progressReporter->IsCancelled() ) + { + if( m_commit ) + m_commit->Revert(); + + connectivity->SetProgressReporter( nullptr ); + return false; + } } } @@ -314,7 +351,6 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck m_progressReporter->SetMaxProgress( toFill.size() ); } - nextItem = 0; auto tri_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t @@ -327,7 +363,12 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck num++; if( m_progressReporter ) + { m_progressReporter->AdvanceProgress(); + + if( m_progressReporter->IsCancelled() ) + break; + } } return num; @@ -347,8 +388,13 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck do { if( m_progressReporter ) + { m_progressReporter->KeepRefreshing(); + if( m_progressReporter->IsCancelled() ) + break; + } + status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) ); } while( status != std::future_status::ready ); } @@ -359,6 +405,15 @@ bool ZONE_FILLER::Fill( const std::vector& aZones, bool aCheck m_progressReporter->AdvancePhase(); m_progressReporter->Report( _( "Committing changes..." ) ); m_progressReporter->KeepRefreshing(); + + if( m_progressReporter->IsCancelled() ) + { + if( m_commit ) + m_commit->Revert(); + + connectivity->SetProgressReporter( nullptr ); + return false; + } } connectivity->SetProgressReporter( nullptr ); @@ -751,18 +806,30 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I if( s_DumpZonesWhenFilling ) dumper->BeginGroup( "clipper-zone" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + knockoutThermalReliefs( aZone, aLayer, aRawPolys ); if( s_DumpZonesWhenFilling ) dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + buildCopperItemClearances( aZone, aLayer, clearanceHoles ); if( s_DumpZonesWhenFilling ) dumper->Write( &aRawPolys, "clearance holes" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + buildThermalSpokes( aZone, aLayer, thermalSpokes ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + // Create a temporary zone that we can hit-test spoke-ends against. It's only temporary // because the "real" subtract-clearance-holes has to be done after the spokes are added. static const bool USE_BBOX_CACHES = true; @@ -776,9 +843,13 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy ); } + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed // things up a bit. testAreas.BuildBBoxCaches(); + int interval = 0; for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes ) { @@ -791,6 +862,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I continue; } + if( interval++ > 400 ) + { + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + + interval = 0; + } + // Hit-test against other spokes for( const SHAPE_LINE_CHAIN& other : thermalSpokes ) { @@ -802,6 +881,9 @@ 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 ); @@ -810,6 +892,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I if( s_DumpZonesWhenFilling ) dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); // Prune features that don't meet minimum-width criteria if( half_min_width - epsilon > epsilon ) @@ -818,6 +903,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I if( s_DumpZonesWhenFilling ) dumper->Write( &aRawPolys, "solid-areas-before-hatching" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + // Now remove the non filled areas due to the hatch pattern if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN ) addHatchFillTypeOnZone( aZone, aLayer, aRawPolys ); @@ -825,6 +913,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I if( s_DumpZonesWhenFilling ) dumper->Write( &aRawPolys, "solid-areas-after-hatching" ); + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return; + // Re-inflate after pruning of areas that don't meet minimum-width criteria if( aZone->GetFilledPolysUseThickness() ) { @@ -874,10 +965,13 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) ) return false; + if( m_progressReporter && m_progressReporter->IsCancelled() ) + return false; + if( aZone->IsOnCopperLayer() ) { - computeRawFilledArea( - aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys ); + computeRawFilledArea( aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, + aFinalPolys ); } else {