diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 8b3c023916..d0ae615caf 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -2051,6 +2051,15 @@ void SHAPE_POLY_SET::triangulateSingle( const POLYGON& aPoly, } +MD5_HASH SHAPE_POLY_SET::GetHash() const +{ + if( !m_hash.IsValid() ) + return checksum(); + + return m_hash; +} + + bool SHAPE_POLY_SET::IsTriangulationUpToDate() const { if( !m_triangulationValid ) diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 4d0c4ed6c8..57e9c88b01 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -1168,6 +1168,8 @@ class SHAPE_POLY_SET : public SHAPE void CacheTriangulation(); bool IsTriangulationUpToDate() const; + MD5_HASH GetHash() const; + private: void triangulateSingle( const POLYGON& aPoly, SHAPE_POLY_SET::TRIANGULATED_POLYGON& aResult ); diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index c3d618ac06..34fcc39d4a 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -404,14 +404,6 @@ void DRC::RunTests( wxTextCtrl* aMessages ) testTracks( aMessages ? aMessages->GetParent() : m_pcbEditorFrame, true ); - // Before testing segments and unconnected, refill all zones: - // this is a good caution, because filled areas can be outdated. - if( aMessages ) - { - aMessages->AppendText( _( "Fill zones...\n" ) ); - wxSafeYield(); - } - // caller (a wxTopLevelFrame) is the wxDialog or the Pcb Editor frame that call DRC: wxWindow* caller = aMessages ? aMessages->GetParent() : m_pcbEditorFrame; @@ -420,11 +412,16 @@ void DRC::RunTests( wxTextCtrl* aMessages ) aMessages->AppendText( _( "Refilling all zones...\n" ) ); m_pcbEditorFrame->Fill_All_Zones( caller ); } + else + { + aMessages->AppendText( _( "Checking zone fills...\n" ) ); + m_pcbEditorFrame->Check_All_Zones( caller ); + } // test zone clearances to other zones if( aMessages ) { - aMessages->AppendText( _( "Test zones...\n" ) ); + aMessages->AppendText( _( "Zone to zone clearances...\n" ) ); wxSafeYield(); } diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index c84c486c7a..1e132e705f 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -1436,6 +1436,13 @@ public: */ int Fill_All_Zones( wxWindow * aActiveWindow ); + /** + * Function Check_All_Zones + * Checks for out-of-date fills and fills them if requested by the user. + * @param aActiveWindow + */ + void Check_All_Zones( wxWindow* aActiveWindow ); + /** * Function Add_Zone_Cutout diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 87bcb9bc91..dec08fe38f 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "zone_filler.h" @@ -75,12 +76,12 @@ ZONE_FILLER::~ZONE_FILLER() } -void ZONE_FILLER::SetProgressReporter( PROGRESS_REPORTER* aReporter ) +void ZONE_FILLER::SetProgressReporter( WX_PROGRESS_REPORTER* aReporter ) { m_progressReporter = aReporter; } -void ZONE_FILLER::Fill( std::vector aZones ) +void ZONE_FILLER::Fill( std::vector aZones, bool aCheck ) { int parallelThreadCount = std::max( ( int )std::thread::hardware_concurrency(), 2 ); @@ -90,9 +91,6 @@ void ZONE_FILLER::Fill( std::vector aZones ) if( !connectivity->TryLock() ) return; - // Remove segment zones - m_board->m_Zone.DeleteAll(); - for( auto zone : aZones ) { // Keepout zones are not filled @@ -114,28 +112,32 @@ void ZONE_FILLER::Fill( std::vector aZones ) if( m_progressReporter ) { - m_progressReporter->Report( _( "Calculating zone fills..." ) ); + m_progressReporter->Report( _( "Checking zone fills..." ) ); m_progressReporter->SetMaxProgress( toFill.size() ); } m_next = 0; + m_out_of_date = false; m_count_done = 0; std::vector fillWorkers; for( ssize_t ii = 0; ii < parallelThreadCount; ++ii ) { - fillWorkers.push_back( std::thread( [ this, toFill ]() + fillWorkers.push_back( std::thread( [ this, aCheck, toFill ]() { size_t i = m_next.fetch_add( 1 ); while( i < toFill.size() ) { SHAPE_POLY_SET rawPolys, finalPolys; - ZONE_SEGMENT_FILL segFill; - fillSingleZone( toFill[i].m_zone, rawPolys, finalPolys ); + ZONE_CONTAINER* zone = toFill[i].m_zone; + fillSingleZone( zone, rawPolys, finalPolys ); - toFill[i].m_zone->SetRawPolysList( rawPolys ); - toFill[i].m_zone->SetFilledPolysList( finalPolys ); - toFill[i].m_zone->SetIsFilled( true ); + if( aCheck && zone->GetFilledPolysList().GetHash() != finalPolys.GetHash() ) + m_out_of_date.store( true ); + + zone->SetRawPolysList( rawPolys ); + zone->SetFilledPolysList( finalPolys ); + zone->SetIsFilled( true ); if( m_progressReporter ) m_progressReporter->AdvanceProgress(); @@ -181,10 +183,36 @@ void ZONE_FILLER::Fill( std::vector aZones ) zone.m_zone->SetFilledPolysList( poly ); } + if( aCheck && m_out_of_date ) + { + bool cancel = !IsOK( nullptr, _( "Zone fills may be out-of-date. Re-fill all zones?" ) ); + + if( m_progressReporter ) + { + // Sigh. Patch another case of "fall behind" dialogs on Mac. + if( m_progressReporter->GetParent() ) + m_progressReporter->GetParent()->Raise(); + m_progressReporter->Raise(); + } + + if( cancel ) + { + if( m_commit ) + m_commit->Revert(); + + connectivity->SetProgressReporter( nullptr ); + connectivity->Unlock(); + return; + } + } + + // Remove segment zones + m_board->m_Zone.DeleteAll(); + if( m_progressReporter ) { m_progressReporter->AdvancePhase(); - m_progressReporter->Report( _( "Caching polygon triangulations..." ) ); + m_progressReporter->Report( _( "Performing polygon fills..." ) ); m_progressReporter->SetMaxProgress( toFill.size() ); } @@ -236,7 +264,7 @@ void ZONE_FILLER::Fill( std::vector aZones ) if( m_progressReporter ) { m_progressReporter->AdvancePhase(); - m_progressReporter->Report( _( "Fill with segments..." ) ); + m_progressReporter->Report( _( "Performing segment fills..." ) ); m_progressReporter->SetMaxProgress( zones_to_fill_count ); } diff --git a/pcbnew/zone_filler.h b/pcbnew/zone_filler.h index 2bd4b5d0ed..a166d1d714 100644 --- a/pcbnew/zone_filler.h +++ b/pcbnew/zone_filler.h @@ -29,7 +29,7 @@ #include #include -class PROGRESS_REPORTER; +class WX_PROGRESS_REPORTER; class BOARD; class COMMIT; class SHAPE_POLY_SET; @@ -41,9 +41,8 @@ public: ZONE_FILLER( BOARD* aBoard, COMMIT* aCommit = nullptr ); ~ZONE_FILLER(); - void SetProgressReporter( PROGRESS_REPORTER* aReporter ); - void Fill( std::vector aZones ); - void Unfill( std::vector aZones ); + void SetProgressReporter( WX_PROGRESS_REPORTER* aReporter ); + void Fill( std::vector aZones, bool aCheck = false ); private: @@ -118,11 +117,12 @@ private: BOARD* m_board; COMMIT* m_commit; - PROGRESS_REPORTER* m_progressReporter; + WX_PROGRESS_REPORTER* m_progressReporter; std::atomic_size_t m_next; // An index into the vector of zones to fill. // Used by the variuos parallel thread sets during // fill operations. + std::atomic_bool m_out_of_date; std::atomic_size_t m_count_done; }; diff --git a/pcbnew/zones_by_polygon_fill_functions.cpp b/pcbnew/zones_by_polygon_fill_functions.cpp index a234eefbe2..e8ae03ec82 100644 --- a/pcbnew/zones_by_polygon_fill_functions.cpp +++ b/pcbnew/zones_by_polygon_fill_functions.cpp @@ -101,3 +101,21 @@ int PCB_EDIT_FRAME::Fill_All_Zones( wxWindow* aActiveWindow ) toolMgr->RunAction( PCB_ACTIONS::zoneFillAll, true ); return 0; } + + +void PCB_EDIT_FRAME::Check_All_Zones( wxWindow* aActiveWindow ) +{ + std::vector toFill; + + for( auto zone : GetBoard()->Zones() ) + toFill.push_back(zone); + + BOARD_COMMIT commit( this ); + + std::unique_ptr progressReporter( + new WX_PROGRESS_REPORTER( aActiveWindow, _( "Checking Zones" ), 3 ) ); + + ZONE_FILLER filler( GetBoard(), &commit ); + filler.SetProgressReporter( progressReporter.get() ); + filler.Fill( toFill, true ); +}