multi-channel tool: anchor footprints

adds a dropdown menu to select an anchor footprint from the
reference rule area. The corresponding footprints in the
target areas determine displacement and rotation off all the
replicated items. If no anchor footprint has been selected
the previous behavior is unchanged, i.e. rule area position
determines placement.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/20603
This commit is contained in:
Alexander Boehm
2025-06-10 17:33:26 +00:00
committed by eurofun1
parent 4678000a0e
commit 6dd366f7bf
7 changed files with 853 additions and 657 deletions
@@ -27,6 +27,8 @@
#include <pcb_edit_frame.h>
#include <tools/multichannel_tool.h>
#include <zone.h>
#include <board.h>
DIALOG_MULTICHANNEL_REPEAT_LAYOUT::DIALOG_MULTICHANNEL_REPEAT_LAYOUT (
PCB_BASE_FRAME* aFrame,
@@ -34,6 +36,7 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT::DIALOG_MULTICHANNEL_REPEAT_LAYOUT (
DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE( aFrame ),
m_parentTool( aParentTool )
{
m_board = aFrame->GetBoard();
auto data = m_parentTool->GetData();
m_refRAName->SetLabelText( data->m_refRA->m_area->GetZoneName() );
@@ -69,6 +72,7 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT::DIALOG_MULTICHANNEL_REPEAT_LAYOUT (
m_raGrid->SetColLabelValue( 0, wxT("Copy") );
m_raGrid->SetColLabelValue( 1, wxT("Target Rule Area") );
m_raGrid->SetColLabelValue( 2, wxT("Status") );
m_raGrid->SetColLabelValue( 3, wxT( "RefFp" ) );
m_raGrid->AutoSizeColumn( 1 );
m_raGrid->AppendRows( m_targetRAs.size() - 1 );
@@ -82,9 +86,19 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT::DIALOG_MULTICHANNEL_REPEAT_LAYOUT (
i++;
}
m_raGrid->SetMaxSize( wxSize( -1, 800 ) );
m_raGrid->SetMaxSize( wxSize( -1, 400 ) );
m_raGrid->Fit();
wxArrayString refFpNames;
refFpNames.push_back( "" );
for( FOOTPRINT* fp : data->m_refRA->m_raFootprints )
refFpNames.push_back( fp->GetReference() );
refFpNames.Sort();
m_refAnchorFp->Set( refFpNames );
m_refAnchorFp->SetSelection( 0 );
m_cbCopyPlacement->SetValue( data->m_options.m_copyPlacement );
m_cbCopyRouting->SetValue( data->m_options.m_copyRouting );
m_cbGroupItems->SetValue( data->m_options.m_groupItems );
@@ -117,6 +131,19 @@ bool DIALOG_MULTICHANNEL_REPEAT_LAYOUT::TransferDataFromWindow()
data->m_options.m_includeLockedItems = m_cbIncludeLockedComponents->GetValue();
data->m_options.m_moveOffRAComponents = m_cbIncludeOffRAComponents->GetValue();
if( m_refAnchorFp->GetString( m_refAnchorFp->GetSelection() ) == "" )
{
data->m_options.m_anchorFp = nullptr;
}
else
{
for( FOOTPRINT* fp : m_board->Footprints() )
{
if( fp->GetReference() == m_refAnchorFp->GetString( m_refAnchorFp->GetSelection() ) )
data->m_options.m_anchorFp = fp;
}
}
return true;
}
File diff suppressed because it is too large Load Diff
@@ -25,6 +25,7 @@
#define __DIALOG_MULTICHANNEL_REPEAT_LAYOUT__
#include <vector>
#include <board.h>
#include <widgets/unit_binder.h>
#include <dialogs/dialog_multichannel_repeat_layout_base.h>
@@ -54,6 +55,7 @@ private:
std::vector<TABLE_ENTRY> m_targetRAs;
MULTICHANNEL_TOOL* m_parentTool;
BOARD* m_board;
};
#endif
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -11,24 +11,21 @@
DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxSize( 800,-1 ), wxDefaultSize );
this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize );
m_GeneralBoxSizer = new wxBoxSizer( wxVERTICAL );
m_GeneralBoxSizer->SetMinSize( wxSize( 800,300 ) );
wxFlexGridSizer* fgSizer3;
fgSizer3 = new wxFlexGridSizer( 0, 1, 0, 0 );
fgSizer3->AddGrowableCol( 0 );
fgSizer3->AddGrowableRow( 2 );
fgSizer3->AddGrowableRow( 1 );
fgSizer3->SetFlexibleDirection( wxBOTH );
fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
fgSizer3->SetMinSize( wxSize( 800,300 ) );
fgSizer3->SetMinSize( wxSize( 600,-1 ) );
m_staticText4 = new wxStaticText( this, wxID_ANY, _("Target areas:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText4->Wrap( -1 );
fgSizer3->Add( m_staticText4, 0, wxALL, 5 );
m_raGrid = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_raGrid = new wxGrid( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
// Grid
m_raGrid->CreateGrid( 1, 3 );
@@ -39,8 +36,9 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE(
// Columns
m_raGrid->AutoSizeColumns();
m_raGrid->EnableDragColMove( true );
m_raGrid->EnableDragColMove( false );
m_raGrid->EnableDragColSize( true );
m_raGrid->SetColLabelSize( wxGRID_AUTOSIZE );
m_raGrid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
@@ -71,6 +69,24 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE(
fgSizer3->Add( bSizer4, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer41;
bSizer41 = new wxBoxSizer( wxHORIZONTAL );
m_staticText2 = new wxStaticText( this, wxID_ANY, _("Anchor footprint:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText2->Wrap( -1 );
bSizer41->Add( m_staticText2, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
wxArrayString m_refAnchorFpChoices;
m_refAnchorFp = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_refAnchorFpChoices, 0 );
m_refAnchorFp->SetSelection( 0 );
m_refAnchorFp->SetToolTip( _("Optional, use for precise and/or rotated placement.\nSelect reference rule area footprint, place corresponding\ntarget rule area footprint(s).") );
m_refAnchorFp->SetMaxSize( wxSize( -1,400 ) );
bSizer41->Add( m_refAnchorFp, 0, wxALL, 5 );
fgSizer3->Add( bSizer41, 1, wxEXPAND, 5 );
wxBoxSizer* bSizer13;
bSizer13 = new wxBoxSizer( wxVERTICAL );
@@ -118,12 +134,9 @@ DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE(
fgSizer3->Add( bottomButtonsSizer, 0, wxEXPAND|wxLEFT, 5 );
m_GeneralBoxSizer->Add( fgSizer3, 1, wxEXPAND, 5 );
this->SetSizer( m_GeneralBoxSizer );
this->SetSizer( fgSizer3 );
this->Layout();
m_GeneralBoxSizer->Fit( this );
fgSizer3->Fit( this );
// Connect Events
this->Connect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE::OnInitDlg ) );
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -19,6 +19,7 @@
#include <wx/settings.h>
#include <wx/grid.h>
#include <wx/sizer.h>
#include <wx/choice.h>
#include <wx/checkbox.h>
#include <wx/button.h>
#include <wx/dialog.h>
@@ -31,13 +32,14 @@
class DIALOG_MULTICHANNEL_REPEAT_LAYOUT_BASE : public DIALOG_SHIM
{
private:
wxBoxSizer* m_GeneralBoxSizer;
protected:
wxStaticText* m_staticText4;
wxGrid* m_raGrid;
wxStaticText* m_staticText1;
wxStaticText* m_refRAName;
wxStaticText* m_staticText2;
wxChoice* m_refAnchorFp;
wxCheckBox* m_cbCopyPlacement;
wxCheckBox* m_cbCopyRouting;
wxCheckBox* m_cbCopyOtherItems;
+41 -17
View File
@@ -528,7 +528,6 @@ int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
int totalCopied = 0;
BOARD_COMMIT commit( GetManager(), true );
for( auto& targetArea : m_areas.m_compatMap )
{
if( !targetArea.second.m_doCopy )
@@ -541,9 +540,8 @@ int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
if( !targetArea.second.m_isOk )
continue;
if( !copyRuleAreaContents( targetArea.second.m_matchingComponents, &commit, m_areas.m_refRA,
targetArea.first, m_areas.m_options, targetArea.second.m_affectedItems,
if( !copyRuleAreaContents( targetArea.second.m_matchingComponents, &commit, m_areas.m_refRA, targetArea.first,
m_areas.m_options, targetArea.second.m_affectedItems,
targetArea.second.m_groupableItems ) )
{
auto errMsg = wxString::Format(
@@ -561,6 +559,7 @@ int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
return -1;
}
totalCopied++;
wxSafeYield();
}
if( m_areas.m_options.m_groupItems )
@@ -707,10 +706,8 @@ int MULTICHANNEL_TOOL::findRoutedConnections( std::set<BOARD_ITEM*>&
}
bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatches,
BOARD_COMMIT* aCommit,
RULE_AREA* aRefArea, RULE_AREA* aTargetArea,
REPEAT_LAYOUT_OPTIONS aOpts,
bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatches, BOARD_COMMIT* aCommit,
RULE_AREA* aRefArea, RULE_AREA* aTargetArea, REPEAT_LAYOUT_OPTIONS aOpts,
std::unordered_set<BOARD_ITEM*>& aAffectedItems,
std::unordered_set<BOARD_ITEM*>& aGroupableItems )
{
@@ -718,7 +715,29 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
SHAPE_LINE_CHAIN refOutline = aRefArea->m_area->Outline()->COutline( 0 );
SHAPE_LINE_CHAIN targetOutline = aTargetArea->m_area->Outline()->COutline( 0 );
FOOTPRINT* targetAnchorFp = nullptr;
VECTOR2I disp = aTargetArea->m_center - aRefArea->m_center;
EDA_ANGLE rot = EDA_ANGLE( 0 );
if( aOpts.m_anchorFp )
{
for( auto& fpPair : aMatches )
{
if( fpPair.first->GetReference() == aOpts.m_anchorFp->GetReference() )
targetAnchorFp = fpPair.second;
}
if( targetAnchorFp )
{
VECTOR2I oldpos = aOpts.m_anchorFp->GetPosition();
rot = EDA_ANGLE( targetAnchorFp->GetOrientationDegrees() - aOpts.m_anchorFp->GetOrientationDegrees() );
aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( rot ) );
oldpos = aOpts.m_anchorFp->GetPosition();
VECTOR2I newpos = targetAnchorFp->GetPosition();
disp = newpos - oldpos;
aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( -rot ) );
}
}
SHAPE_POLY_SET refPoly;
refPoly.AddOutline( refOutline );
@@ -727,6 +746,7 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
SHAPE_POLY_SET targetPoly;
SHAPE_LINE_CHAIN newTargetOutline( refOutline );
newTargetOutline.Rotate( rot, VECTOR2( 0, 0 ) );
newTargetOutline.Move( disp );
targetPoly.AddOutline( newTargetOutline );
targetPoly.CacheTriangulation( false );
@@ -786,6 +806,7 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
fixupNet( static_cast<BOARD_CONNECTED_ITEM*>( item ), static_cast<BOARD_CONNECTED_ITEM*>( copied ),
aMatches );
}
copied->Rotate( VECTOR2( 0, 0 ), rot );
copied->Move( disp );
copied->SetParentGroup( nullptr );
const_cast<KIID&>( copied->m_Uuid ) = KIID();
@@ -900,6 +921,7 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
copied->ClearFlags();
copied->SetParentGroup( nullptr );
const_cast<KIID&>( copied->m_Uuid ) = KIID();
copied->Rotate( VECTOR2( 0, 0 ), rot );
copied->Move( disp );
aGroupableItems.insert( copied );
aCommit->Add( copied );
@@ -907,11 +929,6 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
}
}
aTargetArea->m_area->RemoveAllContours();
aTargetArea->m_area->AddPolygon( newTargetOutline );
aTargetArea->m_area->UnHatchBorder();
aTargetArea->m_area->HatchBorder();
if( aOpts.m_copyPlacement )
{
for( auto& fpPair : aMatches )
@@ -943,9 +960,9 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
targetFP->SetLayerAndFlip( refFP->GetLayer() );
targetFP->SetOrientation( refFP->GetOrientation() );
VECTOR2I targetPos = refFP->GetPosition() + disp;
targetFP->SetPosition( targetPos );
targetFP->SetPosition( refFP->GetPosition() );
targetFP->Rotate( VECTOR2( 0, 0 ), rot );
targetFP->Move( disp );
for( PCB_FIELD* refField : refFP->GetFields() )
{
if( !refField->IsVisible() )
@@ -955,7 +972,9 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
wxCHECK2( targetField, continue );
targetField->SetAttributes( refField->GetAttributes() );
targetField->SetPosition( refField->GetPosition() + disp );
targetField->SetPosition( refField->GetPosition() );
targetField->Rotate( VECTOR2( 0, 0 ), rot );
targetField->Move( disp );
targetField->SetIsKnockout( refField->IsKnockout() );
}
@@ -964,6 +983,11 @@ bool MULTICHANNEL_TOOL::copyRuleAreaContents( TMATCH::COMPONENT_MATCHES& aMatche
}
}
aTargetArea->m_area->RemoveAllContours();
aTargetArea->m_area->AddPolygon( newTargetOutline );
aTargetArea->m_area->UnHatchBorder();
aTargetArea->m_area->HatchBorder();
return true;
}
+1
View File
@@ -55,6 +55,7 @@ struct REPEAT_LAYOUT_OPTIONS
bool m_includeLockedItems = true;
bool m_keepOldRouting = false;
bool m_copyOnlyMatchingRAShapes = false;
FOOTPRINT* m_anchorFp = nullptr;
REPEAT_LAYOUT_EDGE_MODE m_edgeMode = REPEAT_LAYOUT_EDGE_MODE::INSIDE;
};