From 69fe1368f10f2804cd1aa163c47e84893ddec340 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sun, 24 Nov 2024 11:29:17 -0500 Subject: [PATCH] Multichannel: support free items; fix group handling Fixes https://gitlab.com/kicad/code/kicad/-/issues/19096 Fixes https://gitlab.com/kicad/code/kicad/-/issues/19095 Fixes https://gitlab.com/kicad/code/kicad/-/issues/19097 --- .../dialog_multichannel_repeat_layout.cpp | 2 + .../dialog_multichannel_repeat_layout.fbp | 159 +++++++++++++----- ...dialog_multichannel_repeat_layout_base.cpp | 9 +- .../dialog_multichannel_repeat_layout_base.h | 4 +- pcbnew/tools/multichannel_tool.cpp | 136 +++++++++++++++ pcbnew/tools/multichannel_tool.h | 2 + 6 files changed, 262 insertions(+), 50 deletions(-) diff --git a/pcbnew/dialogs/dialog_multichannel_repeat_layout.cpp b/pcbnew/dialogs/dialog_multichannel_repeat_layout.cpp index d27e78e46c..a4b7e95971 100644 --- a/pcbnew/dialogs/dialog_multichannel_repeat_layout.cpp +++ b/pcbnew/dialogs/dialog_multichannel_repeat_layout.cpp @@ -88,6 +88,7 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT::DIALOG_MULTICHANNEL_REPEAT_LAYOUT ( m_cbCopyPlacement->SetValue( data->m_options.m_copyPlacement ); m_cbCopyRouting->SetValue( data->m_options.m_copyRouting ); m_cbGroupItems->SetValue( data->m_options.m_groupItems ); + m_cbCopyOtherItems->SetValue( data->m_options.m_copyOtherItems ); m_cbIncludeLockedComponents->SetValue( data->m_options.m_includeLockedItems ); m_cbIncludeOffRAComponents->SetValue( data->m_options.m_moveOffRAComponents ); @@ -111,6 +112,7 @@ bool DIALOG_MULTICHANNEL_REPEAT_LAYOUT::TransferDataFromWindow() data->m_options.m_copyPlacement = m_cbCopyPlacement->GetValue(); data->m_options.m_copyRouting = m_cbCopyRouting->GetValue(); + data->m_options.m_copyOtherItems = m_cbCopyOtherItems->GetValue(); data->m_options.m_groupItems = m_cbGroupItems->GetValue(); data->m_options.m_includeLockedItems = m_cbIncludeLockedComponents->GetValue(); data->m_options.m_moveOffRAComponents = m_cbIncludeOffRAComponents->GetValue(); diff --git a/pcbnew/dialogs/dialog_multichannel_repeat_layout.fbp b/pcbnew/dialogs/dialog_multichannel_repeat_layout.fbp index c55da32818..6a21876435 100644 --- a/pcbnew/dialogs/dialog_multichannel_repeat_layout.fbp +++ b/pcbnew/dialogs/dialog_multichannel_repeat_layout.fbp @@ -1,34 +1,36 @@ - + - C++ - 1 - source_name - 0 - 0 + + 1 + connect + none + + + 0 + 0 res UTF-8 - connect dialog_multichannel_repeat_layout_base 1000 - none - - 1 + 1 + UI dialog_multichannel_repeat_layout - . - + 0 + source_name + 1 + 0 + source_name + + + 1 1 - 1 - 1 - 1 - UI - 0 - 0 0 + 0 0 wxAUI_MGR_DEFAULT @@ -89,10 +91,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -151,10 +153,10 @@ 1 1 1 - + 0 - - + 0 + 0 1 1 @@ -249,10 +251,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -311,10 +313,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -384,10 +386,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -409,7 +411,7 @@ 0 0 wxID_ANY - Copy placement + Copy footprint placement 0 @@ -449,10 +451,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -505,6 +507,71 @@ + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Copy other items + + 0 + + + 0 + + 1 + m_cbCopyOtherItems + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Copy text, shapes, zones, and other items inside the source rule area + + wxFILTER_NONE + wxDefaultValidator + + + + + + 5 wxALL @@ -514,10 +581,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -579,10 +646,10 @@ 1 1 1 - + 0 - - + 0 + 0 @@ -644,10 +711,10 @@ 1 1 1 - + 0 - - + 0 + 0 diff --git a/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.cpp b/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.cpp index 2e70a07304..3b504526e9 100644 --- a/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.cpp +++ b/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf) +// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -74,12 +74,17 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE( wxBoxSizer* bSizer13; bSizer13 = new wxBoxSizer( wxVERTICAL ); - m_cbCopyPlacement = new wxCheckBox( this, wxID_ANY, _("Copy placement"), wxDefaultPosition, wxDefaultSize, 0 ); + m_cbCopyPlacement = new wxCheckBox( this, wxID_ANY, _("Copy footprint placement"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer13->Add( m_cbCopyPlacement, 0, wxALL, 5 ); m_cbCopyRouting = new wxCheckBox( this, wxID_ANY, _("Copy routing"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer13->Add( m_cbCopyRouting, 0, wxALL, 5 ); + m_cbCopyOtherItems = new wxCheckBox( this, wxID_ANY, _("Copy other items"), wxDefaultPosition, wxDefaultSize, 0 ); + m_cbCopyOtherItems->SetToolTip( _("Copy text, shapes, zones, and other items inside the source rule area") ); + + bSizer13->Add( m_cbCopyOtherItems, 0, wxALL, 5 ); + m_cbGroupItems = new wxCheckBox( this, wxID_ANY, _("Group components with their placement rule areas"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer13->Add( m_cbGroupItems, 0, wxALL, 5 ); diff --git a/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.h b/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.h index 422b47fe74..48f8a00170 100644 --- a/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.h +++ b/pcbnew/dialogs/dialog_multichannel_repeat_layout_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf) +// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -25,7 +25,6 @@ /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// /// Class DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE /////////////////////////////////////////////////////////////////////////////// @@ -41,6 +40,7 @@ class DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE : public DIALOG_SHIM wxStaticText* m_refRAName; wxCheckBox* m_cbCopyPlacement; wxCheckBox* m_cbCopyRouting; + wxCheckBox* m_cbCopyOtherItems; wxCheckBox* m_cbGroupItems; wxCheckBox* m_cbIncludeLockedComponents; wxCheckBox* m_cbIncludeOffRAComponents; diff --git a/pcbnew/tools/multichannel_tool.cpp b/pcbnew/tools/multichannel_tool.cpp index d5f1871b41..6a915228ac 100644 --- a/pcbnew/tools/multichannel_tool.cpp +++ b/pcbnew/tools/multichannel_tool.cpp @@ -129,6 +129,91 @@ bool MULTICHANNEL_TOOL::identifyComponentsInRuleArea( ZONE* aRul } +bool MULTICHANNEL_TOOL::findOtherItemsInRuleArea( ZONE* aRuleArea, std::set& aItems ) +{ + std::vector result; + + PCBEXPR_COMPILER compiler( new PCBEXPR_UNIT_RESOLVER ); + PCBEXPR_UCODE ucode; + PCBEXPR_CONTEXT ctx, preflightCtx; + + auto reportError = [&]( const wxString& aMessage, int aOffset ) + { + wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s"), aMessage ); + }; + + ctx.SetErrorCallback( reportError ); + preflightCtx.SetErrorCallback( reportError ); + compiler.SetErrorCallback( reportError ); + + bool restoreBlankName = false; + + if( aRuleArea->GetZoneName().IsEmpty() ) + { + restoreBlankName = true; + aRuleArea->SetZoneName( aRuleArea->m_Uuid.AsString() ); + } + + wxString ruleText = wxString::Format( wxT( "A.enclosedByArea('%s')" ), aRuleArea->GetZoneName() ); + + if( !compiler.Compile( ruleText, &ucode, &preflightCtx ) ) + { + if( restoreBlankName ) + aRuleArea->SetZoneName( wxEmptyString ); + + return false; + } + + auto testAndAdd = + [&]( BOARD_ITEM* aItem ) + { + ctx.SetItems( aItem, aItem ); + auto val = ucode.Run( &ctx ); + + if( val->AsDouble() != 0.0 ) + aItems.insert( aItem ); + }; + + for( ZONE* zone : board()->Zones() ) + { + if( zone == aRuleArea ) + continue; + + testAndAdd( zone ); + } + + for( BOARD_ITEM* drawing : board()->Drawings() ) + testAndAdd( drawing ); + + for( PCB_GROUP* group : board()->Groups() ) + { + // A group is cloned in its entirety if *all* children are contained + bool addGroup = true; + + group->RunOnDescendants( + [&]( BOARD_ITEM* aItem ) + { + if( aItem->IsType( { PCB_ZONE_T, PCB_SHAPE_T, PCB_DIMENSION_T } ) ) + { + ctx.SetItems( aItem, aItem ); + auto val = ucode.Run( &ctx ); + + if( val->AsDouble() == 0.0 ) + addGroup = false; + } + } ); + + if( addGroup ) + aItems.insert( group ); + } + + if( restoreBlankName ) + aRuleArea->SetZoneName( wxEmptyString ); + + return true; +} + + std::set MULTICHANNEL_TOOL::queryComponentsInSheet( wxString aSheetName ) const { std::set rv; @@ -583,17 +668,68 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche { if( !aRefArea->m_area->GetLayerSet().Contains( item->GetLayer() ) ) continue; + if( !aTargetArea->m_area->GetLayerSet().Contains( item->GetLayer() ) ) continue; BOARD_ITEM* copied = static_cast( item->Clone() ); copied->Move( disp ); + copied->SetParentGroup( nullptr ); aGroupableItems.insert( copied ); aCommit->Add( copied ); } } + if( aOpts.m_copyOtherItems ) + { + std::set sourceItems; + + findOtherItemsInRuleArea( aRefArea->m_area, sourceItems ); + + for( BOARD_ITEM* item : sourceItems ) + { + if( !aRefArea->m_area->GetLayerSet().Contains( item->GetLayer() ) ) + continue; + + if( !aTargetArea->m_area->GetLayerSet().Contains( item->GetLayer() ) ) + continue; + + // Groups that are fully-contained within the area are added themselves; copy their + // items as part of DeepClone rather than explicitly + if( item->GetParentGroup() && sourceItems.contains( item->GetParentGroup() ) ) + continue; + + BOARD_ITEM* copied; + + if( item->Type() == PCB_GROUP_T ) + { + copied = static_cast( item )->DeepClone(); + } + else + { + copied = static_cast( item->Clone() ); + } + + copied->ClearFlags(); + copied->SetParentGroup( nullptr ); + copied->Move( disp ); + aGroupableItems.insert( copied ); + aCommit->Add( copied ); + + getView()->Query( copied->GetBoundingBox(), + [&]( KIGFX::VIEW_ITEM* viewItem ) -> bool + { + BOARD_ITEM* existingItem = static_cast( viewItem ); + + if( existingItem && existingItem->Similarity( *copied ) == 1.0 ) + aCommit->Remove( existingItem ); + + return true; + } ); + } + } + aTargetArea->m_area->RemoveAllContours(); aTargetArea->m_area->AddPolygon( newTargetOutline ); aTargetArea->m_area->UnHatchBorder(); diff --git a/pcbnew/tools/multichannel_tool.h b/pcbnew/tools/multichannel_tool.h index 262aa6fd95..283ed9ee60 100644 --- a/pcbnew/tools/multichannel_tool.h +++ b/pcbnew/tools/multichannel_tool.h @@ -48,6 +48,7 @@ struct REPEAT_LAYOUT_OPTIONS { bool m_copyRouting = true; bool m_copyPlacement = true; + bool m_copyOtherItems = true; bool m_groupItems = false; bool m_moveOffRAComponents = true; bool m_includeLockedItems = true; @@ -123,6 +124,7 @@ private: wxString stripComponentIndex( const wxString& aRef ) const; bool identifyComponentsInRuleArea( ZONE* aRuleArea, std::set& aComponents ); + bool findOtherItemsInRuleArea( ZONE* aRuleArea, std::set& aItems ); const SHAPE_LINE_CHAIN buildRAOutline( std::set& aFootprints, int aMargin ); std::set queryComponentsInSheet( wxString aSheetName ) const; std::set