Simplify hatch damage/rebuild and move it out of view.

There should be far fewer hatched objects than other
objects, so we're spending too much effort finding
all the possible damage when we probably should
just be auto-regenerating all the hatching.

This also moves it out of being done during redraw,
which was proving problematic.  Plus the refill
zones architecture does it during commit, and has
a lot more miles behind it.
This commit is contained in:
Jeff Young 2025-04-07 11:16:13 +01:00
parent c5ff61ba9c
commit 0659290417
21 changed files with 123 additions and 168 deletions

View File

@ -566,20 +566,11 @@ UI_FILL_MODE EDA_SHAPE::GetFillModeProp() const
}
const SHAPE_POLY_SET& EDA_SHAPE::GetHatching() const
void EDA_SHAPE::UpdateHatching() const
{
if( m_hatchingDirty )
{
updateHatching();
m_hatchingDirty = false;
}
if( !m_hatchingDirty )
return;
return m_hatching;
}
void EDA_SHAPE::updateHatching() const
{
m_hatching.RemoveAllContours();
std::vector<double> slopes;
@ -587,7 +578,9 @@ void EDA_SHAPE::updateHatching() const
int spacing = GetHatchLineSpacing();
SHAPE_POLY_SET shapeBuffer;
if( GetFillMode() == FILL_T::CROSS_HATCH )
if( isMoving() )
return;
else if( GetFillMode() == FILL_T::CROSS_HATCH )
slopes = { 1.0, -1.0 };
else if( GetFillMode() == FILL_T::HATCH )
slopes = { -1.0 };
@ -603,10 +596,7 @@ void EDA_SHAPE::updateHatching() const
[&]( const std::vector<SEG>& hatchLines )
{
for( const SEG& seg : hatchLines )
{
TransformOvalToPolygon( m_hatching, seg.A, seg.B, lineWidth, ARC_LOW_DEF,
ERROR_INSIDE );
}
TransformOvalToPolygon( m_hatching, seg.A, seg.B, lineWidth, ARC_LOW_DEF, ERROR_INSIDE );
};
switch( m_shape )

View File

@ -349,6 +349,8 @@ void DISPLAY_FOOTPRINTS_FRAME::ReloadFootprint( FOOTPRINT* aFootprint )
pad->SetPinFunction( net.GetPinFunction() );
}
m_toolManager->RunAction( PCB_ACTIONS::rehatchShapes );
GetBoard()->Add( aFootprint );
updateView();
GetCanvas()->Refresh();

View File

@ -35,6 +35,7 @@
#include <sch_draw_panel.h>
#include <sch_view.h>
#include <sch_painter.h>
#include <sch_shape.h>
#include <settings/settings_manager.h>
#include <confirm.h>
#include <preview_items/selection_area.h>
@ -397,13 +398,20 @@ void SCH_BASE_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpda
}
else
{
if( aItem->Type() == SCH_SHAPE_T )
static_cast<SCH_SHAPE*>( aItem )->UpdateHatching();
if( !isAddOrDelete )
GetCanvas()->GetView()->Update( aItem );
// Some children are drawn from their parents. Mark them for re-paint.
if( parent
&& parent->IsType( { SCH_SYMBOL_T, SCH_SHEET_T, SCH_LABEL_LOCATE_ANY_T, SCH_TABLE_T } ) )
if( parent && ( parent->Type() == SCH_SYMBOL_T
|| parent->Type() == SCH_SHEET_T
|| parent->Type() == SCH_LABEL_LOCATE_ANY_T
|| parent->Type() == SCH_TABLE_T ) )
{
GetCanvas()->GetView()->Update( parent, KIGFX::REPAINT );
}
}
/*

View File

@ -139,6 +139,8 @@ protected:
m_fill = aFlag ? FILL_T::FILLED_WITH_COLOR : FILL_T::NO_FILL;
}
bool isMoving() const override { return IsMoving(); }
/**
* @copydoc SCH_ITEM::compare()
*

View File

@ -26,10 +26,9 @@
#include <refdes_utils.h>
#include <hash.h>
#include <sch_screen.h>
#include <sch_item.h>
#include <sch_marker.h>
#include <sch_label.h>
#include <sch_reference_list.h>
#include <sch_shape.h>
#include <symbol_library.h>
#include <sch_sheet_path.h>
#include <sch_symbol.h>
@ -381,7 +380,9 @@ void SCH_SHEET_PATH::UpdateAllScreenReferences() const
std::back_inserter( items ),
[]( SCH_ITEM* aItem )
{
return ( aItem->Type() == SCH_SYMBOL_T || aItem->Type() == SCH_GLOBAL_LABEL_T );
return ( aItem->Type() == SCH_SYMBOL_T
|| aItem->Type() == SCH_GLOBAL_LABEL_T
|| aItem->Type() == SCH_SHAPE_T );
} );
for( SCH_ITEM* item : items )
@ -411,6 +412,11 @@ void SCH_SHEET_PATH::UpdateAllScreenReferences() const
LastScreen()->Update( intersheetRefs );
}
}
else if( item->Type() == SCH_SHAPE_T )
{
SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( item );
shape->UpdateHatching();
}
}
}

View File

@ -612,12 +612,10 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
std::vector<DANGLING_END_ITEM> endPointsByType = internalPoints;
std::vector<DANGLING_END_ITEM> endPointsByPos = endPointsByType;
DANGLING_END_ITEM_HELPER::sort_dangling_end_items( endPointsByType,
endPointsByPos );
DANGLING_END_ITEM_HELPER::sort_dangling_end_items( endPointsByType, endPointsByPos );
for( EDA_ITEM* item : selection )
static_cast<SCH_ITEM*>( item )->UpdateDanglingState( endPointsByType,
endPointsByPos );
static_cast<SCH_ITEM*>( item )->UpdateDanglingState( endPointsByType, endPointsByPos );
}
@ -641,6 +639,12 @@ bool SCH_MOVE_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, SCH_COMMIT* aComm
item->SetFlags( IS_MOVING );
if( SCH_SHAPE* shape = dynamic_cast<SCH_SHAPE*>( item ) )
{
shape->SetHatchingDirty();
shape->UpdateHatching();
}
if( SCH_ITEM* schItem = dynamic_cast<SCH_ITEM*>( item ) )
schItem->SetStoredPos( schItem->GetPosition() );
}

View File

@ -35,6 +35,7 @@
#include <sch_edit_frame.h>
#include <sch_view.h>
#include <symbol_edit_frame.h>
#include <sch_shape.h>
class SCH_SELECTION;
@ -108,34 +109,7 @@ protected:
*/
void updateItem( EDA_ITEM* aItem, bool aUpdateRTree ) const
{
switch( aItem->Type() )
{
case SCH_SHEET_PIN_T:
getView()->Update( aItem );
getView()->Update( aItem->GetParent() );
// Moving sheet pins does not change the BBox.
break;
case SCH_PIN_T:
case SCH_FIELD_T:
case SCH_TABLECELL_T:
getView()->Update( aItem );
getView()->Update( aItem->GetParent() );
if( aUpdateRTree )
m_frame->GetScreen()->Update( static_cast<SCH_ITEM*>( aItem->GetParent() ) );
break;
default:
getView()->Update( aItem );
if( aUpdateRTree && dynamic_cast<SCH_ITEM*>( aItem ) )
m_frame->GetScreen()->Update( static_cast<SCH_ITEM*>( aItem ) );
break;
}
m_frame->UpdateItem( aItem, false, aUpdateRTree );
}
///< Similar to m_frame->SaveCopyInUndoList(), but handles items that are owned by their

View File

@ -145,7 +145,7 @@ public:
UI_FILL_MODE GetFillModeProp() const;
void SetHatchingDirty() { m_hatchingDirty = true; }
const SHAPE_POLY_SET& GetHatching() const;
const SHAPE_POLY_SET& GetHatching() const { return m_hatching; }
bool IsClosed() const;
@ -403,6 +403,8 @@ public:
int GetRectangleHeight() const;
int GetRectangleWidth() const;
virtual void UpdateHatching() const;
/**
* Convert the shape to a closed polygon.
*
@ -464,7 +466,7 @@ protected:
void endEdit( bool aClosed = true );
void setEditState( int aState ) { m_editState = aState; }
virtual void updateHatching() const;
virtual bool isMoving() const { return false; }
/**
* Make a set of #SHAPE objects representing the #EDA_SHAPE.

View File

@ -129,8 +129,7 @@ COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag
}
void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>* aStaleZones,
std::vector<PCB_SHAPE*>* aStaleHatchedShapes )
void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>* aStaleZones )
{
wxCHECK( aChangedItem, /* void */ );
@ -138,7 +137,7 @@ void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>
aStaleZones->push_back( static_cast<ZONE*>( aChangedItem ) );
aChangedItem->RunOnChildren(
std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones, aStaleHatchedShapes ),
std::bind( &BOARD_COMMIT::propagateDamage, this, _1, aStaleZones ),
RECURSE_MODE::NO_RECURSE );
BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
@ -167,56 +166,6 @@ void BOARD_COMMIT::propagateDamage( BOARD_ITEM* aChangedItem, std::vector<ZONE*>
}
}
}
auto checkItem =
[&]( BOARD_ITEM* item )
{
if( item->Type() != PCB_SHAPE_T )
return;
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
if( !shape->IsHatchedFill() )
return;
if( ( shape->GetLayerSet() & damageLayers ).any()
&& shape->GetBoundingBox().Intersects( damageBBox ) )
{
aStaleHatchedShapes->push_back( shape );
}
};
if( aStaleHatchedShapes && ( aChangedItem->Type() == PCB_FIELD_T
|| aChangedItem->Type() == PCB_TEXT_T
|| aChangedItem->Type() == PCB_TEXTBOX_T
|| aChangedItem->Type() == PCB_SHAPE_T ) )
{
if( aChangedItem->IsOnLayer( F_CrtYd ) )
{
damageBBox = aChangedItem->GetParentFootprint()->GetBoundingBox();
damageLayers |= LSET::FrontMask();
}
else if( aChangedItem->IsOnLayer( B_CrtYd ) )
{
damageBBox = aChangedItem->GetParentFootprint()->GetBoundingBox();
damageLayers |= LSET::BackMask();
}
for( FOOTPRINT* footprint : board->Footprints() )
{
footprint->RunOnChildren(
[&]( BOARD_ITEM* child )
{
checkItem( child );
},
RECURSE_MODE::RECURSE );
}
for( BOARD_ITEM* item : board->Drawings() )
{
checkItem( item );
}
}
}
@ -240,7 +189,6 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
PCB_GROUP* addedGroup = nullptr;
std::vector<ZONE*> staleZonesStorage;
std::vector<ZONE*>* staleZones = nullptr;
std::vector<PCB_SHAPE*> staleHatchedShapes;
if( Empty() )
return;
@ -375,7 +323,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
addedGroup = static_cast<PCB_GROUP*>( boardItem );
if( boardItem->Type() != PCB_MARKER_T )
propagateDamage( boardItem, staleZones, &staleHatchedShapes );
propagateDamage( boardItem, staleZones );
if( view && boardItem->Type() != PCB_NETINFO_T )
view->Add( boardItem );
@ -407,7 +355,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, &staleHatchedShapes );
propagateDamage( boardItem, staleZones );
switch( boardItem->Type() )
{
@ -521,8 +469,8 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
if( boardItem->Type() != PCB_MARKER_T )
{
propagateDamage( boardItemCopy, staleZones, &staleHatchedShapes ); // before
propagateDamage( boardItem, staleZones, &staleHatchedShapes ); // after
propagateDamage( boardItemCopy, staleZones ); // before
propagateDamage( boardItem, staleZones ); // after
}
updateComponentClasses( boardItem );
@ -658,13 +606,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
m_toolMgr->PostAction( PCB_ACTIONS::zoneFillDirty );
}
for( PCB_SHAPE* shape : staleHatchedShapes )
{
shape->SetHatchingDirty();
if( view )
view->Update( shape );
}
m_toolMgr->PostAction( PCB_ACTIONS::rehatchShapes );
if( selectedModified )
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );

View File

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

View File

@ -819,6 +819,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
loadedBoard->BuildListOfNets();
ResolveDRCExclusions( true );
m_toolManager->RunAction( PCB_ACTIONS::repairBoard, true);
m_toolManager->RunAction( PCB_ACTIONS::rehatchShapes );
if( loadedBoard->IsModified() )
OnModify();

View File

@ -245,6 +245,8 @@ FOOTPRINT_VIEWER_FRAME::FOOTPRINT_VIEWER_FRAME( KIWAY* aKiway, wxWindow* aParent
{
GetBoard()->Add( footprint );
setFPWatcher( footprint );
m_toolManager->RunAction( PCB_ACTIONS::rehatchShapes );
}
}
@ -714,6 +716,8 @@ void FOOTPRINT_VIEWER_FRAME::displayFootprint( FOOTPRINT* aFootprint )
}
GetBoard()->Add( aFootprint );
m_toolManager->RunAction( PCB_ACTIONS::rehatchShapes );
}

View File

@ -238,6 +238,8 @@ void PCB_BASE_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
aFootprint->SetOrientation( ANGLE_0 );
GetBoard()->UpdateUserUnits( aFootprint, GetCanvas()->GetView() );
m_toolManager->RunAction( PCB_ACTIONS::rehatchShapes );
}
}

View File

@ -131,8 +131,6 @@ bool PCB_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
// TODO m_hasSolderMask and m_solderMaskMargin
m_hatchingDirty = true;
return true;
}
@ -307,9 +305,13 @@ std::vector<VECTOR2I> PCB_SHAPE::GetConnectionPoints() const
}
void PCB_SHAPE::updateHatching() const
void PCB_SHAPE::UpdateHatching() const
{
EDA_SHAPE::updateHatching();
// Force update; we don't bother to propagate damage from all the things that might
// knock-out parts of our hatching.
m_hatchingDirty = true;
EDA_SHAPE::UpdateHatching();
if( !m_hatching.IsEmpty() )
{
@ -356,7 +358,7 @@ void PCB_SHAPE::updateHatching() const
footprint->RunOnChildren(
[&]( BOARD_ITEM* item )
{
if( item->Type() == PCB_FIELD_T
if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_SHAPE_T )
&& item->GetLayer() == layer
&& item->GetBoundingBox().Intersects( bbox ) )
{
@ -598,8 +600,6 @@ void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
default:
UNIMPLEMENTED_FOR( SHAPE_T_asString() );
}
m_hatchingDirty = true;
}

View File

@ -149,6 +149,8 @@ public:
void Scale( double aScale );
void UpdateHatching() const override;
/**
* Convert the shape to a closed polygon. Circles and arcs are approximated by segments.
*
@ -199,7 +201,7 @@ public:
protected:
void swapData( BOARD_ITEM* aImage ) override;
void updateHatching() const override;
bool isMoving() const override { return IsMoving(); }
struct cmp_drawings
{

View File

@ -585,10 +585,16 @@ bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit
item->SetFlags( IS_MOVING );
if( item->Type() == PCB_SHAPE_T )
static_cast<PCB_SHAPE*>( item )->UpdateHatching();
static_cast<BOARD_ITEM*>( item )->RunOnChildren(
[&]( BOARD_ITEM* bItem )
[&]( BOARD_ITEM* child )
{
item->SetFlags( IS_MOVING );
child->SetFlags( IS_MOVING );
if( child->Type() == PCB_SHAPE_T )
static_cast<PCB_SHAPE*>( child )->UpdateHatching();
},
RECURSE_MODE::RECURSE );
}
@ -633,10 +639,10 @@ bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit
FPs.push_back( static_cast<FOOTPRINT*>( item ) );
item->RunOnChildren(
[&]( BOARD_ITEM* descendent )
[&]( BOARD_ITEM* child )
{
if( descendent->Type() == PCB_FOOTPRINT_T )
FPs.push_back( static_cast<FOOTPRINT*>( descendent ) );
if( child->Type() == PCB_FOOTPRINT_T )
FPs.push_back( static_cast<FOOTPRINT*>( child ) );
},
RECURSE_MODE::RECURSE );
}

View File

@ -1445,6 +1445,11 @@ TOOL_ACTION PCB_ACTIONS::flipBoard( TOOL_ACTION_ARGS()
.Tooltip( _( "View board from the opposite side" ) )
.Icon( BITMAPS::flip_board ) );
TOOL_ACTION PCB_ACTIONS::rehatchShapes( TOOL_ACTION_ARGS()
.Name( "pcbnew.Control.rehatchShapes" )
.Scope( AS_CONTEXT ) );
// Display modes
TOOL_ACTION PCB_ACTIONS::showRatsnest( TOOL_ACTION_ARGS()
.Name( "pcbnew.Control.showRatsnest" )

View File

@ -371,6 +371,8 @@ public:
static TOOL_ACTION flipBoard;
static TOOL_ACTION rehatchShapes;
// Track & via size control
static TOOL_ACTION trackWidthInc;
static TOOL_ACTION trackWidthDec;

View File

@ -2174,6 +2174,31 @@ int PCB_CONTROL::FlipPcbView( const TOOL_EVENT& aEvent )
return 0;
}
void PCB_CONTROL::rehatchBoardItem( BOARD_ITEM* aItem )
{
if( aItem->Type() == PCB_SHAPE_T )
{
static_cast<PCB_SHAPE*>( aItem )->UpdateHatching();
if( view() )
view()->Update( aItem );
}
}
int PCB_CONTROL::RehatchShapes( const TOOL_EVENT& aEvent )
{
for( FOOTPRINT* footprint : board()->Footprints() )
footprint->RunOnChildren( std::bind( &PCB_CONTROL::rehatchBoardItem, this, _1 ), NO_RECURSE );
for( BOARD_ITEM* item : board()->Drawings() )
rehatchBoardItem( item );
return 0;
}
// clang-format off
void PCB_CONTROL::setTransitions()
{
@ -2204,6 +2229,7 @@ void PCB_CONTROL::setTransitions()
Go( &PCB_CONTROL::NetColorModeCycle, PCB_ACTIONS::netColorModeCycle.MakeEvent() );
Go( &PCB_CONTROL::RatsnestModeCycle, PCB_ACTIONS::ratsnestModeCycle.MakeEvent() );
Go( &PCB_CONTROL::FlipPcbView, PCB_ACTIONS::flipBoard.MakeEvent() );
Go( &PCB_CONTROL::RehatchShapes, PCB_ACTIONS::rehatchShapes.MakeEvent() );
// Layer control
Go( &PCB_CONTROL::LayerSwitch, PCB_ACTIONS::layerTop.MakeEvent() );

View File

@ -112,6 +112,8 @@ public:
int FlipPcbView( const TOOL_EVENT& aEvent );
int RehatchShapes( const TOOL_EVENT& aEvent );
// Drag and drop
int DdAppendBoard( const TOOL_EVENT& aEvent );
int DdAddLibrary( const TOOL_EVENT& aEvent );
@ -151,15 +153,16 @@ private:
bool placeBoardItems( BOARD_COMMIT* aCommit, BOARD* aBoard, bool aAnchorAtOrigin,
bool aReannotateDuplicates );
///< Pointer to the currently used edit frame.
PCB_BASE_FRAME* m_frame;
void rehatchBoardItem( BOARD_ITEM* aItem );
private:
PCB_BASE_FRAME* m_frame;
///< Grid origin marker.
std::unique_ptr<KIGFX::ORIGIN_VIEWITEM> m_gridOrigin;
BOARD_ITEM* m_pickerItem;
BOARD_ITEM* m_pickerItem;
std::unique_ptr<STATUS_TEXT_POPUP> m_statusPopup;
std::unique_ptr<STATUS_TEXT_POPUP> m_statusPopup;
};
#endif

View File

@ -41,6 +41,7 @@ using namespace std::placeholders;
#include <connectivity/connectivity_data.h>
#include <tool/tool_manager.h>
#include <tool/actions.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_selection_tool.h>
#include <tools/pcb_control.h>
#include <tools/board_editor_control.h>
@ -620,33 +621,7 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList )
}
}
auto checkHatching =
[&]( BOARD_ITEM* item )
{
if( item->Type() == PCB_SHAPE_T )
{
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
if( shape->IsHatchedFill() )
{
shape->SetHatchingDirty();
view->Update( shape );
}
}
};
for( BOARD_ITEM* item : GetBoard()->Drawings() )
checkHatching( item );
for( FOOTPRINT* footprint : GetBoard()->Footprints() )
{
footprint->RunOnChildren(
[&]( BOARD_ITEM* item )
{
checkHatching( item );
},
RECURSE_MODE::RECURSE );
}
GetToolManager()->PostAction( PCB_ACTIONS::rehatchShapes );
if( added_items.size() > 0 || deleted_items.size() > 0 || changed_items.size() > 0 )
GetBoard()->OnItemsCompositeUpdate( added_items, deleted_items, changed_items );