kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #29539
[PATCH] Speed improvement for Duplicate functionality
Hey all,
I noticed that the Duplicate functionality in pcbnew / modedit was woefully
slow when copying more than a handful of items.
I benchmarked it with 1000 items, it took 58 seconds to perform the
duplication (KiCAD was at 100% CPU the whole time).
I have attached a patch that reduces this time to ~200ms for the same set
of items.
The speed issue is due to each item being passed through the tool framework
multiple times. The patch adds a tool function to select / deselect a list
of items with a single call
Regards,
Oliver
From 70822a2d7e831a047d98552ff4faf7eef8bc884a Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Sun, 28 May 2017 22:33:43 +1000
Subject: [PATCH] Improved speed of Duplicate action
- Removed repetitive tool calls
---
pcbnew/tools/edit_tool.cpp | 56 +++++++++++++++++++-------------
pcbnew/tools/pcb_actions.h | 6 ++++
pcbnew/tools/selection_tool.cpp | 72 ++++++++++++++++++++++++++++++++++++++++-
pcbnew/tools/selection_tool.h | 6 ++++
4 files changed, 116 insertions(+), 24 deletions(-)
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
index 2211fed..73a5907 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -297,7 +297,9 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
// Drag items to the current cursor position
for( auto item : selection )
+ {
static_cast<BOARD_ITEM*>( item )->Move( movement + m_offset );
+ }
}
else if( !m_dragging ) // Prepare to start dragging
{
@@ -828,10 +830,14 @@ int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
}
+/**
+ * Duplicate items that are currently selected (by SELECTION_TOOL)
+ *
+ * Each selected item is duplicated and pushed to new_items list
+ * Old selection is cleared, and new items are then selected.
+ */
int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
{
- // Note: original items are no more modified.
-
bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
// Be sure that there is at least one item that we can modify
@@ -843,28 +849,24 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
// we have a selection to work on now, so start the tool process
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
- std::vector<BOARD_ITEM*> old_items;
+ std::vector<BOARD_ITEM*> new_items;
- for( auto item : selection )
- {
- if( item )
- old_items.push_back( static_cast<BOARD_ITEM*>( item ) );
- }
+ BOARD_ITEM* orig_item = nullptr;
+ BOARD_ITEM* dupe_item = nullptr;
- for( unsigned i = 0; i < old_items.size(); ++i )
+ // Duplicate each selected item
+ for( auto item : selection )
{
- BOARD_ITEM* item = old_items[i];
-
- // Unselect the item, so we won't pick it up again
- // Do this first, so a single-item duplicate will correctly call
- // SetCurItem and show the item properties
- m_toolMgr->RunAction( PCB_ACTIONS::unselectItem, true, item );
+ orig_item = static_cast<BOARD_ITEM*>( item );
- BOARD_ITEM* new_item = NULL;
+ if( !orig_item )
+ {
+ continue;
+ }
if( m_editModules )
{
- new_item = editFrame->GetBoard()->m_Modules->Duplicate( item, increment );
+ dupe_item = editFrame->GetBoard()->m_Modules->Duplicate( orig_item, increment );
}
else
{
@@ -874,23 +876,31 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
// so zones are not duplicated
if( item->Type() != PCB_ZONE_AREA_T )
#endif
- new_item = editFrame->GetBoard()->Duplicate( item );
+ dupe_item = editFrame->GetBoard()->Duplicate( orig_item );
}
- if( new_item )
+ if( dupe_item )
{
- m_commit->Add( new_item );
+ // Clear the selection flag here, otherwise the SELECTION_TOOL
+ // will not properly select it later on
+ dupe_item->ClearSelected();
- // Select the new item, so we can pick it up
- m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, new_item );
+ new_items.push_back( dupe_item );
+ m_commit->Add( dupe_item );
}
}
+ // Clear the old selection first
+ m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
+
+ // Select the new items
+ m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &new_items );
+
// record the new items as added
if( !selection.Empty() )
{
editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
- (int) old_items.size() ) );
+ (int) new_items.size() ) );
// If items were duplicated, pick them up
// this works well for "dropping" copies around and pushes the commit
diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
index 59bf543..03d3b3f 100644
--- a/pcbnew/tools/pcb_actions.h
+++ b/pcbnew/tools/pcb_actions.h
@@ -55,9 +55,15 @@ public:
/// Selects an item (specified as the event parameter).
static TOOL_ACTION selectItem;
+ /// Selects a list of items (specified as the event parameter)
+ static TOOL_ACTION selectItems;
+
/// Unselects an item (specified as the event parameter).
static TOOL_ACTION unselectItem;
+ /// Unselects a list of items (specified as the event parameter)
+ static TOOL_ACTION unselectItems;
+
/// Selects a connection between junctions.
static TOOL_ACTION selectConnection;
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index dd339f3..23d83d9 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -70,10 +70,18 @@ TOOL_ACTION PCB_ACTIONS::selectItem( "pcbnew.InteractiveSelection.SelectItem",
AS_GLOBAL, 0,
"", "" ); // No description, it is not supposed to be shown anywhere
+TOOL_ACTION PCB_ACTIONS::selectItems( "pcbnew.InteractiveSelection.SelectItems",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
TOOL_ACTION PCB_ACTIONS::unselectItem( "pcbnew.InteractiveSelection.UnselectItem",
AS_GLOBAL, 0,
"", "" ); // No description, it is not supposed to be shown anywhere
+TOOL_ACTION PCB_ACTIONS::unselectItems( "pcbnew.InteractiveSelection.UnselectItems",
+ AS_GLOBAL, 0,
+ "", "" ); // No description, it is not supposed to be shown anywhere
+
TOOL_ACTION PCB_ACTIONS::selectionClear( "pcbnew.InteractiveSelection.Clear",
AS_GLOBAL, 0,
"", "" ); // No description, it is not supposed to be shown anywhere
@@ -235,7 +243,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// This will be ignored if the SHIFT modifier is pressed
m_subtractive = !m_additive && evt->Modifier( MD_CTRL );
- // single click? Select single object
+ // Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
{
if( evt->Modifier( MD_CTRL ) && !m_editModules )
@@ -244,8 +252,11 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else
{
+ // If no modifier keys are pressed, clear the selection
if( !m_additive )
+ {
clearSelection();
+ }
selectPoint( evt->Position() );
}
@@ -585,7 +596,9 @@ void SELECTION_TOOL::SetTransitions()
Go( &SELECTION_TOOL::CursorSelection, PCB_ACTIONS::selectionCursor.MakeEvent() );
Go( &SELECTION_TOOL::ClearSelection, PCB_ACTIONS::selectionClear.MakeEvent() );
Go( &SELECTION_TOOL::SelectItem, PCB_ACTIONS::selectItem.MakeEvent() );
+ Go( &SELECTION_TOOL::SelectItems, PCB_ACTIONS::selectItems.MakeEvent() );
Go( &SELECTION_TOOL::UnselectItem, PCB_ACTIONS::unselectItem.MakeEvent() );
+ Go( &SELECTION_TOOL::UnselectItems, PCB_ACTIONS::unselectItems.MakeEvent() );
Go( &SELECTION_TOOL::find, PCB_ACTIONS::find.MakeEvent() );
Go( &SELECTION_TOOL::findMove, PCB_ACTIONS::findMove.MakeEvent() );
Go( &SELECTION_TOOL::filterSelection, PCB_ACTIONS::filterSelection.MakeEvent() );
@@ -672,6 +685,33 @@ int SELECTION_TOOL::ClearSelection( const TOOL_EVENT& aEvent )
return 0;
}
+
+/**
+ * Select multiple items (vector passed as event parameter)
+ */
+int SELECTION_TOOL::SelectItems( const TOOL_EVENT& aEvent )
+{
+ std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
+
+ if( items )
+ {
+ // Perform individual selection of each item
+ // before processing the event.
+ for( auto item : *items )
+ {
+ select( item );
+ }
+
+ m_toolMgr->ProcessEvent( SelectedEvent );
+ }
+
+ return 0;
+}
+
+
+/**
+ * Select a single item (passed as event parameter)
+ */
int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
{
// Check if there is an item to be selected
@@ -689,6 +729,31 @@ int SELECTION_TOOL::SelectItem( const TOOL_EVENT& aEvent )
}
+/**
+ * Unselect multiple items (vector passed as event parameter)
+ */
+int SELECTION_TOOL::UnselectItems( const TOOL_EVENT& aEvent )
+{
+ std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
+
+ if( items )
+ {
+ // Perform individual unselection of each item
+ // before processing the event
+ for( auto item : *items )
+ {
+ unselect( item );
+ }
+
+ m_toolMgr->ProcessEvent( UnselectedEvent );
+ }
+
+ return 0;
+}
+
+/**
+ * Unselect a single item (passed as event parameter)
+ */
int SELECTION_TOOL::UnselectItem( const TOOL_EVENT& aEvent )
{
// Check if there is an item to be selected
@@ -1165,10 +1230,14 @@ int SELECTION_TOOL::filterSelection( const TOOL_EVENT& aEvent )
void SELECTION_TOOL::clearSelection()
{
if( m_selection.Empty() )
+ {
return;
+ }
for( auto item : m_selection )
+ {
unselectVisually( static_cast<BOARD_ITEM*>( item ) );
+ }
m_selection.Clear();
@@ -1393,6 +1462,7 @@ bool SELECTION_TOOL::selectable( const BOARD_ITEM* aItem ) const
return board()->IsLayerVisible( aItem->GetLayer() );
}
+
void SELECTION_TOOL::select( BOARD_ITEM* aItem )
{
if( aItem->IsSelected() )
diff --git a/pcbnew/tools/selection_tool.h b/pcbnew/tools/selection_tool.h
index 642842d..dab2f5b 100644
--- a/pcbnew/tools/selection_tool.h
+++ b/pcbnew/tools/selection_tool.h
@@ -114,9 +114,15 @@ public:
///> Item selection event handler.
int SelectItem( const TOOL_EVENT& aEvent );
+ ///> Multiple item selection event handler
+ int SelectItems( const TOOL_EVENT& aEvent );
+
///> Item unselection event handler.
int UnselectItem( const TOOL_EVENT& aEvent );
+ ///> Multiple item unselection event handler
+ int UnselectItems( const TOOL_EVENT& aEvent );
+
///> Event sent after an item is selected.
static const TOOL_EVENT SelectedEvent;
--
2.7.4
Follow ups