Dirty clearance lines when rule areas change.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20734
This commit is contained in:
Jeff Young 2025-06-13 16:36:50 +01:00
parent aaa0e74311
commit e2cf944915
5 changed files with 84 additions and 16 deletions

View File

@ -24,6 +24,8 @@
*/ */
#include <macros.h> #include <macros.h>
#include <pgm_base.h>
#include <settings/settings_manager.h>
#include <board.h> #include <board.h>
#include <footprint.h> #include <footprint.h>
#include <lset.h> #include <lset.h>
@ -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<ZONE*>* aStaleZones ) void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>* aStaleZones,
std::vector<BOX2I>& aStaleRuleAreas )
{ {
wxCHECK( aChangedItem, /* void */ ); wxCHECK( aChangedItem, /* void */ );
if( aStaleZones && aChangedItem->Type() == PCB_ZONE_T ) if( aStaleZones && aChangedItem->Type() == PCB_ZONE_T )
aStaleZones->push_back( static_cast<ZONE*>( aChangedItem ) ); aStaleZones->push_back( static_cast<ZONE*>( aChangedItem ) );
aChangedItem->RunOnChildren( aChangedItem->RunOnChildren( std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones, aStaleRuleAreas ),
std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones ), RECURSE_MODE::NO_RECURSE );
RECURSE_MODE::NO_RECURSE );
BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() ); BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
BOX2I damageBBox = aChangedItem->GetBoundingBox(); BOX2I damageBBox = aChangedItem->GetBoundingBox();
LSET damageLayers = aChangedItem->GetLayerSet(); 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<ZONE*>( aChangedItem )->GetZoneName().IsEmpty() )
aStaleRuleAreas.push_back( damageBBox );
}
if( aStaleZones ) if( aStaleZones )
{ {
if( damageLayers.test( Edge_Cuts ) || damageLayers.test( Margin ) ) if( damageLayers.test( Edge_Cuts ) || damageLayers.test( Margin ) )
@ -166,7 +175,7 @@ void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>
void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags ) void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
{ {
KIGFX::VIEW* view = m_toolMgr->GetView(); KIGFX::PCB_VIEW* view = static_cast<KIGFX::PCB_VIEW*>( m_toolMgr->GetView() );
BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() ); BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() ); PCB_BASE_FRAME* frame = dynamic_cast<PCB_BASE_FRAME*>( m_toolMgr->GetToolHolder() );
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>(); PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
@ -184,6 +193,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
std::set<PCB_TRACK*> staleTeardropTracks; std::set<PCB_TRACK*> staleTeardropTracks;
std::vector<ZONE*> staleZonesStorage; std::vector<ZONE*> staleZonesStorage;
std::vector<ZONE*>* staleZones = nullptr; std::vector<ZONE*>* staleZones = nullptr;
std::vector<BOX2I> staleRuleAreas;
if( Empty() ) if( Empty() )
return; return;
@ -319,7 +329,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
} }
if( boardItem->Type() != PCB_MARKER_T ) if( boardItem->Type() != PCB_MARKER_T )
propagateDamage( boardItem, staleZones ); propagateDamage( boardItem, staleZones, staleRuleAreas );
if( view && boardItem->Type() != PCB_NETINFO_T ) if( view && boardItem->Type() != PCB_NETINFO_T )
view->Add( boardItem ); view->Add( boardItem );
@ -356,7 +366,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
ent.m_parent = parentFP->m_Uuid; ent.m_parent = parentFP->m_Uuid;
if( boardItem->Type() != PCB_MARKER_T ) if( boardItem->Type() != PCB_MARKER_T )
propagateDamage( boardItem, staleZones ); propagateDamage( boardItem, staleZones, staleRuleAreas );
switch( boardItem->Type() ) switch( boardItem->Type() )
{ {
@ -438,8 +448,8 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
if( boardItem->Type() != PCB_MARKER_T ) if( boardItem->Type() != PCB_MARKER_T )
{ {
propagateDamage( boardItemCopy, staleZones ); // before propagateDamage( boardItemCopy, staleZones, staleRuleAreas ); // before
propagateDamage( boardItem, staleZones ); // after propagateDamage( boardItem, staleZones, staleRuleAreas ); // after
} }
updateComponentClasses( boardItem ); updateComponentClasses( boardItem );
@ -499,6 +509,14 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
frame->HideSolderMask(); frame->HideSolderMask();
} }
PCBNEW_SETTINGS* settings = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>( "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() ) if( !staleTeardropPadsAndVias.empty() || !staleTeardropTracks.empty() )
{ {
teardropMgr.UpdateTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks ); teardropMgr.UpdateTeardrops( *this, &staleTeardropPadsAndVias, &staleTeardropTracks );

View File

@ -76,7 +76,8 @@ private:
EDA_ITEM* makeImage( EDA_ITEM* aItem ) const override; EDA_ITEM* makeImage( EDA_ITEM* aItem ) const override;
void propagateDamage( BOARD_ITEM* aItem, std::vector<ZONE*>* aStaleZones ); void propagateDamage( BOARD_ITEM* aItem, std::vector<ZONE*>* aStaleZones,
std::vector<BOX2I>& aStaleRuleAreas );
private: private:
TOOL_MANAGER* m_toolMgr; TOOL_MANAGER* m_toolMgr;

View File

@ -119,6 +119,33 @@ void PCB_VIEW::Update( const KIGFX::VIEW_ITEM* aItem ) const
} }
void PCB_VIEW::UpdateCollidingItems( const std::vector<BOX2I>& aStaleAreas,
std::initializer_list<KICAD_T> aTypes )
{
UpdateAllItemsConditionally(
[&]( KIGFX::VIEW_ITEM* aItem ) -> int
{
if( aItem->IsBOARD_ITEM() )
{
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( 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 ) void PCB_VIEW::UpdateDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions )
{ {
KIGFX::PCB_PAINTER* painter = static_cast<KIGFX::PCB_PAINTER*>( GetPainter() ); KIGFX::PCB_PAINTER* painter = static_cast<KIGFX::PCB_PAINTER*>( GetPainter() );

View File

@ -22,8 +22,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#ifndef __PCB_VIEW_H #pragma once
#define __PCB_VIEW_H
#include <layer_ids.h> #include <layer_ids.h>
#include <view/view.h> #include <view/view.h>
@ -51,9 +50,13 @@ public:
/// @copydoc VIEW::Update() /// @copydoc VIEW::Update()
virtual void Update( const VIEW_ITEM* aItem ) const override; 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<BOX2I>& aStaleAreas,
std::initializer_list<KICAD_T> aTypes );
void UpdateDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions ); void UpdateDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions );
}; };
} }
#endif

View File

@ -261,9 +261,10 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
bool reBuild_ratsnest = false; bool reBuild_ratsnest = false;
bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt bool deep_reBuild_ratsnest = false; // true later if pointers must be rebuilt
bool solder_mask_dirty = false; bool solder_mask_dirty = false;
std::vector<BOX2I> dirty_rule_areas;
auto view = GetCanvas()->GetView(); KIGFX::PCB_VIEW* view = GetCanvas()->GetView();
auto connectivity = GetBoard()->GetConnectivity(); std::shared_ptr<CONNECTIVITY_DATA> connectivity = GetBoard()->GetConnectivity();
GetBoard()->IncrementTimeStamp(); // clear caches GetBoard()->IncrementTimeStamp(); // clear caches
@ -454,6 +455,12 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
view->Hide( item, false ); view->Hide( item, false );
parent->Add( item ); parent->Add( item );
if( item->Type() == PCB_ZONE_T && static_cast<ZONE*>( 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 ); update_item_change_state( item, ITEM_CHANGE_TYPE::CHANGED );
break; break;
} }
@ -468,6 +475,9 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
eda_item->SetFlags( UR_TRANSIENT ); eda_item->SetFlags( UR_TRANSIENT );
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
break; break;
case UNDO_REDO::DELETED: /* deleted items are put in List, as new items */ 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 ) if( eda_item->Type() != PCB_NETINFO_T )
view->Add( eda_item ); view->Add( eda_item );
if( eda_item->Type() == PCB_ZONE_T && static_cast<ZONE*>( eda_item )->GetIsRuleArea() )
dirty_rule_areas.push_back( eda_item->GetBoundingBox() );
break; break;
case UNDO_REDO::DRILLORIGIN: case UNDO_REDO::DRILLORIGIN:
@ -558,6 +571,12 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
if( IsType( FRAME_PCB_EDITOR ) ) 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 ) if( reBuild_ratsnest || deep_reBuild_ratsnest )
{ {
// Connectivity may have changed; rebuild internal caches to remove stale items // Connectivity may have changed; rebuild internal caches to remove stale items