Allow cancelling of zone fills.

Fixes https://gitlab.com/kicad/code/kicad/issues/5035
This commit is contained in:
Jeff Young 2020-08-04 19:53:16 +01:00
parent 77fd384da5
commit 3cf5db3ce5
3 changed files with 113 additions and 7 deletions

View File

@ -30,7 +30,8 @@ PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) :
m_phase( 0 ), m_phase( 0 ),
m_numPhases( aNumPhases ), m_numPhases( aNumPhases ),
m_progress( 0 ), 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 ) while( m_progress < m_maxProgress && m_maxProgress > 0 )
{ {
if( !updateUI() ) if( !updateUI() )
{
m_cancelled.store( true );
return false; return false;
}
wxMilliSleep( 20 ); wxMilliSleep( 20 );
} }
@ -98,7 +102,13 @@ bool PROGRESS_REPORTER::KeepRefreshing( bool aWait )
} }
else else
{ {
return updateUI(); if( !updateUI() )
{
m_cancelled.store( true );
return false;
}
return true;
} }
} }

View File

@ -92,6 +92,7 @@ class PROGRESS_REPORTER
*/ */
virtual void SetTitle( const wxString& aTitle ) {} virtual void SetTitle( const wxString& aTitle ) {}
bool IsCancelled() const { return m_cancelled.load(); }
protected: protected:
@ -105,6 +106,7 @@ class PROGRESS_REPORTER
std::atomic_int m_numPhases; std::atomic_int m_numPhases;
std::atomic_int m_progress; std::atomic_int m_progress;
std::atomic_int m_maxProgress; std::atomic_int m_maxProgress;
std::atomic_bool m_cancelled;
}; };

View File

@ -159,8 +159,8 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
} }
std::atomic<size_t> nextItem( 0 ); std::atomic<size_t> nextItem( 0 );
size_t parallelThreadCount = size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
std::min<size_t>( std::thread::hardware_concurrency(), aZones.size() ); aZones.size() );
std::vector<std::future<size_t>> returns( parallelThreadCount ); std::vector<std::future<size_t>> returns( parallelThreadCount );
auto fill_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t auto fill_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
@ -184,8 +184,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
zone->SetIsFilled( true ); zone->SetIsFilled( true );
if( m_progressReporter ) if( m_progressReporter )
{
m_progressReporter->AdvanceProgress(); m_progressReporter->AdvanceProgress();
if( m_progressReporter->IsCancelled() )
break;
}
num++; num++;
} }
@ -206,8 +211,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
do do
{ {
if( m_progressReporter ) if( m_progressReporter )
{
m_progressReporter->KeepRefreshing(); m_progressReporter->KeepRefreshing();
if( m_progressReporter->IsCancelled() )
break;
}
status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) ); status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
} while( status != std::future_status::ready ); } while( status != std::future_status::ready );
} }
@ -219,11 +229,29 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
m_progressReporter->AdvancePhase(); m_progressReporter->AdvancePhase();
m_progressReporter->Report( _( "Removing insulated copper islands..." ) ); m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
m_progressReporter->KeepRefreshing(); m_progressReporter->KeepRefreshing();
if( m_progressReporter->IsCancelled() )
{
if( m_commit )
m_commit->Revert();
connectivity->SetProgressReporter( nullptr );
return false;
}
} }
connectivity->SetProgressReporter( m_progressReporter ); connectivity->SetProgressReporter( m_progressReporter );
connectivity->FindIsolatedCopperIslands( islandsList ); 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 // Now remove insulated copper islands and islands outside the board edge
bool outOfDate = false; bool outOfDate = false;
@ -285,6 +313,15 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() ) if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() )
outOfDate = true; 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<ZONE_CONTAINER*>& aZones, bool aCheck
m_progressReporter->SetMaxProgress( toFill.size() ); m_progressReporter->SetMaxProgress( toFill.size() );
} }
nextItem = 0; nextItem = 0;
auto tri_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t auto tri_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
@ -327,7 +363,12 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
num++; num++;
if( m_progressReporter ) if( m_progressReporter )
{
m_progressReporter->AdvanceProgress(); m_progressReporter->AdvanceProgress();
if( m_progressReporter->IsCancelled() )
break;
}
} }
return num; return num;
@ -347,8 +388,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
do do
{ {
if( m_progressReporter ) if( m_progressReporter )
{
m_progressReporter->KeepRefreshing(); m_progressReporter->KeepRefreshing();
if( m_progressReporter->IsCancelled() )
break;
}
status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) ); status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
} while( status != std::future_status::ready ); } while( status != std::future_status::ready );
} }
@ -359,6 +405,15 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
m_progressReporter->AdvancePhase(); m_progressReporter->AdvancePhase();
m_progressReporter->Report( _( "Committing changes..." ) ); m_progressReporter->Report( _( "Committing changes..." ) );
m_progressReporter->KeepRefreshing(); m_progressReporter->KeepRefreshing();
if( m_progressReporter->IsCancelled() )
{
if( m_commit )
m_commit->Revert();
connectivity->SetProgressReporter( nullptr );
return false;
}
} }
connectivity->SetProgressReporter( nullptr ); connectivity->SetProgressReporter( nullptr );
@ -751,18 +806,30 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->BeginGroup( "clipper-zone" ); dumper->BeginGroup( "clipper-zone" );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
knockoutThermalReliefs( aZone, aLayer, aRawPolys ); knockoutThermalReliefs( aZone, aLayer, aRawPolys );
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" ); dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
buildCopperItemClearances( aZone, aLayer, clearanceHoles ); buildCopperItemClearances( aZone, aLayer, clearanceHoles );
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "clearance holes" ); dumper->Write( &aRawPolys, "clearance holes" );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
buildThermalSpokes( aZone, aLayer, thermalSpokes ); 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 // 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. // because the "real" subtract-clearance-holes has to be done after the spokes are added.
static const bool USE_BBOX_CACHES = true; 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 ); 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 // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
// things up a bit. // things up a bit.
testAreas.BuildBBoxCaches(); testAreas.BuildBBoxCaches();
int interval = 0;
for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes ) for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes )
{ {
@ -791,6 +862,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
continue; continue;
} }
if( interval++ > 400 )
{
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
interval = 0;
}
// Hit-test against other spokes // Hit-test against other spokes
for( const SHAPE_LINE_CHAIN& other : thermalSpokes ) 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 // Ensure previous changes (adding thermal stubs) do not add
// filled areas outside the zone boundary // filled areas outside the zone boundary
aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST ); 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 ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" ); dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" );
if( m_progressReporter && m_progressReporter->IsCancelled() )
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 )
@ -818,6 +903,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-before-hatching" ); 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 // Now 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 )
addHatchFillTypeOnZone( aZone, aLayer, aRawPolys ); addHatchFillTypeOnZone( aZone, aLayer, aRawPolys );
@ -825,6 +913,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
if( s_DumpZonesWhenFilling ) if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-after-hatching" ); 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 // Re-inflate after pruning of areas that don't meet minimum-width criteria
if( aZone->GetFilledPolysUseThickness() ) if( aZone->GetFilledPolysUseThickness() )
{ {
@ -874,10 +965,13 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) ) if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) )
return false; return false;
if( m_progressReporter && m_progressReporter->IsCancelled() )
return false;
if( aZone->IsOnCopperLayer() ) if( aZone->IsOnCopperLayer() )
{ {
computeRawFilledArea( computeRawFilledArea( aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys,
aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys ); aFinalPolys );
} }
else else
{ {