From e2cf94491529ae73739c9f8d16a8d744a666b50a Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 13 Jun 2025 16:36:50 +0100 Subject: [PATCH] Dirty clearance lines when rule areas change. Fixes https://gitlab.com/kicad/code/kicad/-/issues/20734 --- pcbnew/board_commit.cpp | 36 +++++++++++++++++++++++++++--------- pcbnew/board_commit.h | 3 ++- pcbnew/pcb_view.cpp | 27 +++++++++++++++++++++++++++ pcbnew/pcb_view.h | 11 +++++++---- pcbnew/undo_redo.cpp | 23 +++++++++++++++++++++-- 5 files changed, 84 insertions(+), 16 deletions(-) diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index e941f46323..dc008873a0 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -24,6 +24,8 @@ */ #include +#include +#include #include #include #include @@ -124,21 +126,28 @@ COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag } -void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector* aStaleZones ) +void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector* aStaleZones, + std::vector& aStaleRuleAreas ) { wxCHECK( aChangedItem, /* void */ ); if( aStaleZones && aChangedItem->Type() == PCB_ZONE_T ) aStaleZones->push_back( static_cast( aChangedItem ) ); - aChangedItem->RunOnChildren( - std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones ), - RECURSE_MODE::NO_RECURSE ); + aChangedItem->RunOnChildren( std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones, aStaleRuleAreas ), + RECURSE_MODE::NO_RECURSE ); BOARD* board = static_cast( m_toolMgr->GetModel() ); BOX2I damageBBox = aChangedItem->GetBoundingBox(); LSET damageLayers = aChangedItem->GetLayerSet(); + if( m_isBoardEditor && aChangedItem->Type() == PCB_ZONE_T ) + { + // A named zone can have custom DRC rules targetting it. + if( !static_cast( aChangedItem )->GetZoneName().IsEmpty() ) + aStaleRuleAreas.push_back( damageBBox ); + } + if( aStaleZones ) { if( damageLayers.test( Edge_Cuts ) || damageLayers.test( Margin ) ) @@ -166,7 +175,7 @@ void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) { - KIGFX::VIEW* view = m_toolMgr->GetView(); + KIGFX::PCB_VIEW* view = static_cast( m_toolMgr->GetView() ); BOARD* board = static_cast( m_toolMgr->GetModel() ); PCB_BASE_FRAME* frame = dynamic_cast( m_toolMgr->GetToolHolder() ); PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); @@ -184,6 +193,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) std::set staleTeardropTracks; std::vector staleZonesStorage; std::vector* staleZones = nullptr; + std::vector staleRuleAreas; if( Empty() ) return; @@ -319,7 +329,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) } if( boardItem->Type() != PCB_MARKER_T ) - propagateDamage( boardItem, staleZones ); + propagateDamage( boardItem, staleZones, staleRuleAreas ); if( view && boardItem->Type() != PCB_NETINFO_T ) view->Add( boardItem ); @@ -356,7 +366,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) ent.m_parent = parentFP->m_Uuid; if( boardItem->Type() != PCB_MARKER_T ) - propagateDamage( boardItem, staleZones ); + propagateDamage( boardItem, staleZones, staleRuleAreas ); switch( boardItem->Type() ) { @@ -438,8 +448,8 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) if( boardItem->Type() != PCB_MARKER_T ) { - propagateDamage( boardItemCopy, staleZones ); // before - propagateDamage( boardItem, staleZones ); // after + propagateDamage( boardItemCopy, staleZones, staleRuleAreas ); // before + propagateDamage( boardItem, staleZones, staleRuleAreas ); // after } updateComponentClasses( boardItem ); @@ -499,6 +509,14 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) frame->HideSolderMask(); } + PCBNEW_SETTINGS* settings = Pgm().GetSettingsManager().GetAppSettings( "pcbnew" ); + + if( !staleRuleAreas.empty() && ( settings->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS + || settings->m_Display.m_PadClearance ) ) + { + view->UpdateCollidingItems( staleRuleAreas, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T } ); + } + if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() ) { teardropMgr.UpdateTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks ); diff --git a/pcbnew/board_commit.h b/pcbnew/board_commit.h index 20f209f4f2..72a3621095 100644 --- a/pcbnew/board_commit.h +++ b/pcbnew/board_commit.h @@ -76,7 +76,8 @@ private: EDA_ITEM* makeImage( EDA_ITEM* aItem ) const override; - void propagateDamage( BOARD_ITEM* aItem, std::vector* aStaleZones ); + void propagateDamage( BOARD_ITEM* aItem, std::vector* aStaleZones, + std::vector& aStaleRuleAreas ); private: TOOL_MANAGER* m_toolMgr; diff --git a/pcbnew/pcb_view.cpp b/pcbnew/pcb_view.cpp index f014174226..68dbc03773 100644 --- a/pcbnew/pcb_view.cpp +++ b/pcbnew/pcb_view.cpp @@ -119,6 +119,33 @@ void PCB_VIEW::Update( const KIGFX::VIEW_ITEM* aItem ) const } +void PCB_VIEW::UpdateCollidingItems( const std::vector& aStaleAreas, + std::initializer_list aTypes ) +{ + UpdateAllItemsConditionally( + [&]( KIGFX::VIEW_ITEM* aItem ) -> int + { + if( aItem->IsBOARD_ITEM() ) + { + BOARD_ITEM* item = static_cast( aItem ); + + if( item->IsType( aTypes ) ) + { + BOX2I itemBBox = item->GetBoundingBox(); + + for( const BOX2I& bbox : aStaleAreas ) + { + if( itemBBox.Intersects( bbox ) ) + return KIGFX::REPAINT; + } + } + } + + return 0; + } ); +} + + void PCB_VIEW::UpdateDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions ) { KIGFX::PCB_PAINTER* painter = static_cast( GetPainter() ); diff --git a/pcbnew/pcb_view.h b/pcbnew/pcb_view.h index 5922238c41..352ff76805 100644 --- a/pcbnew/pcb_view.h +++ b/pcbnew/pcb_view.h @@ -22,8 +22,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __PCB_VIEW_H -#define __PCB_VIEW_H +#pragma once #include #include @@ -51,9 +50,13 @@ public: /// @copydoc VIEW::Update() virtual void Update( const VIEW_ITEM* aItem ) const override; + /** + * Sets the KIGFX::REPAINT on all items matching \a aTypes which intersect \a aStaleAreas. + */ + void UpdateCollidingItems( const std::vector& aStaleAreas, + std::initializer_list aTypes ); + void UpdateDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions ); }; } - -#endif diff --git a/pcbnew/undo_redo.cpp b/pcbnew/undo_redo.cpp index 1aae6b31f2..4901445e55 100644 --- a/pcbnew/undo_redo.cpp +++ b/pcbnew/undo_redo.cpp @@ -261,9 +261,10 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList ) bool reBuild_ratsnest = false; bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt bool solder_mask_dirty = false; + std::vector dirty_rule_areas; - auto view = GetCanvas()->GetView(); - auto connectivity = GetBoard()->GetConnectivity(); + KIGFX::PCB_VIEW* view = GetCanvas()->GetView(); + std::shared_ptr connectivity = GetBoard()->GetConnectivity(); GetBoard()->IncrementTimeStamp(); // clear caches @@ -454,6 +455,12 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList ) view->Hide( item, false ); parent->Add( item ); + if( item->Type() == PCB_ZONE_T && static_cast( item )->GetIsRuleArea() ) + { + dirty_rule_areas.push_back( item->GetBoundingBox() ); + dirty_rule_areas.push_back( image->GetBoundingBox() ); + } + update_item_change_state( item, ITEM_CHANGE_TYPE::CHANGED ); break; } @@ -468,6 +475,9 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList ) eda_item->SetFlags( UR_TRANSIENT ); + if( eda_item->Type() == PCB_ZONE_T && static_cast( eda_item )->GetIsRuleArea() ) + dirty_rule_areas.push_back( eda_item->GetBoundingBox() ); + break; case UNDO_REDO::DELETED: /* deleted items are put in List, as new items */ @@ -481,6 +491,9 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList ) if( eda_item->Type() != PCB_NETINFO_T ) view->Add( eda_item ); + if( eda_item->Type() == PCB_ZONE_T && static_cast( eda_item )->GetIsRuleArea() ) + dirty_rule_areas.push_back( eda_item->GetBoundingBox() ); + break; case UNDO_REDO::DRILLORIGIN: @@ -558,6 +571,12 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList ) if( IsType( FRAME_PCB_EDITOR ) ) { + if( !dirty_rule_areas.empty() && ( GetPcbNewSettings()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS + || GetPcbNewSettings()->m_Display.m_PadClearance ) ) + { + view->UpdateCollidingItems( dirty_rule_areas, { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T } ); + } + if( reBuild_ratsnest || deep_reBuild_ratsnest ) { // Connectivity may have changed; rebuild internal caches to remove stale items