Cross-probing/selection for multiple items (SCH->PCB)

This commit is contained in:
dsa-t 2022-01-16 20:29:03 +00:00 committed by Jon Evans
parent 51adf1ace4
commit bc1ff6756f
31 changed files with 974 additions and 389 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -129,6 +129,9 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
addParamsForWindow( &m_Window, "window" );
m_params.emplace_back( new PARAM<bool>( "cross_probing.on_selection",
&m_CrossProbing.on_selection, true ) );
m_params.emplace_back( new PARAM<bool>( "cross_probing.center_on_items",
&m_CrossProbing.center_on_items, true ) );

View File

@ -174,6 +174,17 @@ wxString EscapeString( const wxString& aSource, ESCAPE_CONTEXT aContext )
else
converted += c;
}
else if( aContext == CTX_IPC )
{
if( c == '/' )
converted += "{slash}";
else if( c == ',' )
converted += "{comma}";
else if( c == '\"' )
converted += "{dblquote}";
else
converted += c;
}
else if( aContext == CTX_QUOTED_STR )
{
if( c == '\"' )
@ -276,6 +287,7 @@ wxString UnescapeString( const wxString& aSource )
else if( token == wxS( "backslash" ) ) newbuf.append( wxS( "\\" ) );
else if( token == wxS( "slash" ) ) newbuf.append( wxS( "/" ) );
else if( token == wxS( "bar" ) ) newbuf.append( wxS( "|" ) );
else if( token == wxS( "comma" ) ) newbuf.append( wxS( "," ) );
else if( token == wxS( "colon" ) ) newbuf.append( wxS( ":" ) );
else if( token == wxS( "space" ) ) newbuf.append( wxS( " " ) );
else if( token == wxS( "dollar" ) ) newbuf.append( wxS( "$" ) );

View File

@ -62,6 +62,12 @@ SELECTION_CONDITION SELECTION_CONDITIONS::HasType( KICAD_T aType )
}
SELECTION_CONDITION SELECTION_CONDITIONS::HasTypes( const KICAD_T aTypes[] )
{
return std::bind( &SELECTION_CONDITIONS::hasTypesFunc, _1, aTypes );
}
SELECTION_CONDITION SELECTION_CONDITIONS::OnlyType( KICAD_T aType )
{
return std::bind( &SELECTION_CONDITIONS::onlyTypeFunc, _1, aType );
@ -94,7 +100,10 @@ SELECTION_CONDITION SELECTION_CONDITIONS::LessThan( int aNumber )
bool SELECTION_CONDITIONS::hasTypeFunc( const SELECTION& aSelection, KICAD_T aType )
{
for( const auto& item : aSelection )
if( aSelection.Empty() )
return false;
for( const EDA_ITEM* item : aSelection )
{
if( item->Type() == aType )
return true;
@ -104,6 +113,21 @@ bool SELECTION_CONDITIONS::hasTypeFunc( const SELECTION& aSelection, KICAD_T aTy
}
bool SELECTION_CONDITIONS::hasTypesFunc( const SELECTION& aSelection, const KICAD_T aTypes[] )
{
if( aSelection.Empty() )
return false;
for( const EDA_ITEM* item : aSelection )
{
if( item->IsType( aTypes ) )
return true;
}
return false;
}
bool SELECTION_CONDITIONS::onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType )
{
if( aSelection.Empty() )
@ -111,7 +135,7 @@ bool SELECTION_CONDITIONS::onlyTypeFunc( const SELECTION& aSelection, KICAD_T aT
KICAD_T types[] = { aType, EOT };
for( const auto& item : aSelection )
for( const EDA_ITEM* item : aSelection )
{
if( !item->IsType( types ) )
return false;
@ -126,7 +150,7 @@ bool SELECTION_CONDITIONS::onlyTypesFunc( const SELECTION& aSelection, const KIC
if( aSelection.Empty() )
return false;
for( const auto& item : aSelection )
for( const EDA_ITEM* item : aSelection )
{
if( !item->IsType( aTypes ) )
return false;

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2004-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -473,6 +473,116 @@ void SCH_EDIT_FRAME::SendMessageToPCBNEW( EDA_ITEM* aObjectToSync, SCH_SYMBOL* a
}
std::string FormatProbeItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aItems )
{
std::string command = "";
std::set<wxString> parts;
for( EDA_ITEM* item : aItems )
{
switch( item->Type() )
{
case SCH_SYMBOL_T:
{
SCH_SYMBOL* symbol = (SCH_SYMBOL*) item;
wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
parts.insert( "F" + EscapeString( ref, CTX_IPC ) );
break;
}
case SCH_SHEET_T:
{
// For cross probing, we need the full path of the sheet, because
// in complex hierarchies the sheet uuid of not unique
SCH_SHEET* sheet = (SCH_SHEET*) item;
wxString full_path;
SCH_SHEET* parent = sheet;
while( ( parent = dynamic_cast<SCH_SHEET*>( parent->GetParent() ) ) )
{
if( parent->GetParent() && parent->GetParent()->Type() == SCH_SHEET_T )
{
// The root sheet has no parent sheet and path is just "/"
full_path.Prepend( parent->m_Uuid.AsString() );
full_path.Prepend( "/" );
}
}
full_path += "/" + sheet->m_Uuid.AsString();
parts.insert( "S" + full_path );
break;
}
case SCH_PIN_T:
{
SCH_PIN* pin = (SCH_PIN*) item;
SCH_SYMBOL* symbol = pin->GetParentSymbol();
wxString ref = symbol->GetField( REFERENCE_FIELD )->GetText();
parts.insert( "P" + EscapeString( ref, CTX_IPC ) + "/"
+ EscapeString( pin->GetShownNumber(), CTX_IPC ) );
break;
}
default: break;
}
}
if( !parts.empty() )
{
command = "$SELECT: ";
if( aSelectConnections )
command += "1";
else
command += "0";
command += ",";
for( wxString part : parts )
{
command += part;
command += ",";
}
command.pop_back();
}
return command;
}
void SCH_EDIT_FRAME::SendSelectItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aItems )
{
std::string packet = FormatProbeItems( aSelectConnections, aItems );
if( !packet.empty() )
{
if( Kiface().IsSingle() )
{
SendCommand( MSG_TO_PCB, packet );
}
else
{
// Typically ExpressMail is going to be s-expression packets, but since
// we have existing interpreter of the selection packet on the other
// side in place, we use that here.
Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_SELECTION, packet, this );
}
}
}
void SCH_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
{
// The command is a keyword followed by a quoted string.

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -66,6 +66,7 @@ void PANEL_EESCHEMA_DISPLAY_OPTIONS::loadEEschemaSettings( EESCHEMA_SETTINGS* cf
m_selWidthCtrl->SetValue( cfg->m_Selection.selection_thickness );
m_highlightWidthCtrl->SetValue( cfg->m_Selection.highlight_thickness );
m_checkCrossProbeOnSelection->SetValue( cfg->m_CrossProbing.on_selection );
m_checkCrossProbeCenter->SetValue( cfg->m_CrossProbing.center_on_items );
m_checkCrossProbeZoom->SetValue( cfg->m_CrossProbing.zoom_to_fit );
m_checkCrossProbeAutoHighlight->SetValue( cfg->m_CrossProbing.auto_highlight );
@ -106,9 +107,10 @@ bool PANEL_EESCHEMA_DISPLAY_OPTIONS::TransferDataFromWindow()
cfg->m_Selection.selection_thickness = KiROUND( m_selWidthCtrl->GetValue() );
cfg->m_Selection.highlight_thickness = KiROUND( m_highlightWidthCtrl->GetValue() );
cfg->m_CrossProbing.on_selection = m_checkCrossProbeOnSelection->GetValue();
cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
m_galOptsPanel->TransferDataFromWindow();

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -112,6 +112,10 @@ PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE::PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE( wxWind
wxStaticBoxSizer* sbSizer31;
sbSizer31 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Cross-probing") ), wxVERTICAL );
m_checkCrossProbeOnSelection = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Cross-probe on selection"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeOnSelection->SetValue(true);
sbSizer31->Add( m_checkCrossProbeOnSelection, 0, wxALL, 5 );
m_checkCrossProbeCenter = new wxCheckBox( sbSizer31->GetStaticBox(), wxID_ANY, _("Center view on cross-probed items"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeCenter->SetValue(true);
sbSizer31->Add( m_checkCrossProbeCenter, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<FileVersion major="1" minor="16" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
@ -14,6 +14,7 @@
<property name="file">panel_eeschema_display_options_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">PanelEeschemaDisplayOptions</property>
@ -25,6 +26,7 @@
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
@ -46,6 +48,7 @@
<property name="size">-1,-1</property>
<property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
@ -1169,6 +1172,70 @@
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">1</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Cross-probe on selection</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_checkCrossProbeOnSelection</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -54,6 +54,7 @@ class PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
wxStaticText* m_highlightColorNote;
wxStaticText* m_highlightWidthLabel;
wxSpinCtrlDouble* m_highlightWidthCtrl;
wxCheckBox* m_checkCrossProbeOnSelection;
wxCheckBox* m_checkCrossProbeCenter;
wxCheckBox* m_checkCrossProbeZoom;
wxCheckBox* m_checkCrossProbeAutoHighlight;
@ -61,6 +62,7 @@ class PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
public:
PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_EESCHEMA_DISPLAY_OPTIONS_BASE();
};

View File

@ -307,6 +307,14 @@ public:
*/
void SendMessageToPCBNEW( EDA_ITEM* aObjectToSync, SCH_SYMBOL* aPart );
/**
* Sends items to Pcbnew for selection
*
* @param aSelectConnections - set to select connected tracks/vias too
* @param aElements are the items to select
*/
void SendSelectItems( bool aSelectConnections, const std::deque<EDA_ITEM*>& aElements );
/**
* Sends a net name to Pcbnew for highlighting
*

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -641,9 +641,10 @@ TOOL_ACTION EE_ACTIONS::generateBOM( "eeschema.EditorControl.generateBOM",
_( "Generate BOM..." ), _( "Generate a bill of materials for the current schematic" ),
BITMAPS::post_bom );
TOOL_ACTION EE_ACTIONS::explicitCrossProbe( "eeschema.EditorControl.explicitCrossProbe",
TOOL_ACTION EE_ACTIONS::selectOnPCB( "eeschema.EditorControl.selectOnPCB",
AS_GLOBAL, 0, "",
_( "Highlight on PCB" ), _( "Highlight corresponding items in PCB editor" ),
_( "Select on PCB" ),
_( "Select corresponding items in PCB editor" ),
BITMAPS::select_same_sheet );
TOOL_ACTION EE_ACTIONS::toggleHiddenPins( "eeschema.EditorControl.showHiddenPins",

View File

@ -201,7 +201,7 @@ public:
static TOOL_ACTION toggleERCExclusions;
static TOOL_ACTION toggleSyncedPinsMode;
static TOOL_ACTION restartMove;
static TOOL_ACTION explicitCrossProbe;
static TOOL_ACTION selectOnPCB;
static TOOL_ACTION pushPinLength;
static TOOL_ACTION pushPinNameSize;
static TOOL_ACTION pushPinNumSize;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -171,11 +171,14 @@ bool EE_SELECTION_TOOL::Init()
SCH_PIN_T,
EOT };
static KICAD_T crossProbingTypes[] = { SCH_SYMBOL_T, SCH_PIN_T, SCH_SHEET_T, EOT };
auto wireSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_ITEM_LOCATE_WIRE_T );
auto busSelection = E_C::MoreThan( 0 ) && E_C::OnlyType( SCH_ITEM_LOCATE_BUS_T );
auto wireOrBusSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( wireOrBusTypes );
auto connectedSelection = E_C::MoreThan( 0 ) && E_C::OnlyTypes( connectedTypes );
auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
auto sheetSelection = E_C::Count( 1 ) && E_C::OnlyType( SCH_SHEET_T );
auto crossProbingSelection = E_C::MoreThan( 0 ) && E_C::HasTypes( crossProbingTypes );
auto schEditSheetPageNumberCondition =
[&] ( const SELECTION& aSel )
@ -211,7 +214,7 @@ bool EE_SELECTION_TOOL::Init()
auto& menu = m_menu.GetMenu();
menu.AddItem( EE_ACTIONS::enterSheet, sheetSelection && EE_CONDITIONS::Idle, 1 );
menu.AddItem( EE_ACTIONS::explicitCrossProbe, sheetSelection && EE_CONDITIONS::Idle, 1 );
menu.AddItem( EE_ACTIONS::selectOnPCB, crossProbingSelection && EE_CONDITIONS::Idle, 1 );
menu.AddItem( EE_ACTIONS::leaveSheet, belowRootSheetCondition, 1 );
menu.AddSeparator( 100 );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -672,63 +672,14 @@ void SCH_EDITOR_CONTROL::doCrossProbeSchToPcb( const TOOL_EVENT& aEvent, bool aF
if( m_probingPcbToSch )
return;
EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
SCH_ITEM* item = nullptr;
SCH_SYMBOL* symbol = nullptr;
if( aForce )
{
EE_SELECTION& selection = selTool->RequestSelection();
if( selection.GetSize() >= 1 )
item = (SCH_ITEM*) selection.Front();
}
else
{
EE_SELECTION& selection = selTool->GetSelection();
if( selection.GetSize() >= 1 )
item = (SCH_ITEM*) selection.Front();
}
if( !item )
{
if( aForce )
m_frame->SendMessageToPCBNEW( nullptr, nullptr );
if( !aForce && !m_frame->eeconfig()->m_CrossProbing.on_selection )
return;
}
EE_SELECTION_TOOL* selTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
switch( item->Type() )
{
case SCH_FIELD_T:
case LIB_FIELD_T:
if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
{
symbol = (SCH_SYMBOL*) item->GetParent();
m_frame->SendMessageToPCBNEW( item, symbol );
}
break;
EE_SELECTION& selection = aForce ? selTool->RequestSelection() : selTool->GetSelection();
case SCH_SYMBOL_T:
symbol = (SCH_SYMBOL*) item;
m_frame->SendMessageToPCBNEW( item, symbol );
break;
case SCH_PIN_T:
symbol = (SCH_SYMBOL*) item->GetParent();
m_frame->SendMessageToPCBNEW( static_cast<SCH_PIN*>( item ), symbol );
break;
case SCH_SHEET_T:
if( aForce )
m_frame->SendMessageToPCBNEW( item, nullptr );
break;
default:
break;
}
m_frame->SendSelectItems( false, selection.GetItems() );
}
@ -2318,7 +2269,7 @@ void SCH_EDITOR_CONTROL::setTransitions()
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::SelectedEvent );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::UnselectedEvent );
Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::ClearedEvent );
Go( &SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb, EE_ACTIONS::explicitCrossProbe.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ExplicitCrossProbeToPcb, EE_ACTIONS::selectOnPCB.MakeEvent() );
#ifdef KICAD_SPICE
Go( &SCH_EDITOR_CONTROL::SimProbe, EE_ACTIONS::simProbe.MakeEvent() );

View File

@ -84,8 +84,7 @@ public:
///< Notifies pcbnew about the selected item.
int CrossProbeToPcb( const TOOL_EVENT& aEvent );
///< Equivalent to the above, but initiated by the user. We also do SCH_SHEETs on this
///< one (they're too slow on big projects for the auto version above).
///< Equivalent to the above, but initiated by the user.
int ExplicitCrossProbeToPcb( const TOOL_EVENT& aEvent );
#ifdef KICAD_SPICE
@ -151,7 +150,7 @@ public:
* Find a symbol in the schematic and an item in this symbol.
*
* @param aPath The symbol path to find. Pass nullptr to search by aReference.
* @param aReference The symbol reference designator to find, or to display in
* @param aReference The symbol reference designator to find, or to display in
* status bar if aPath is specified
* @param aSearchHierarchy If false, search the current sheet only. Otherwise,
* the entire hierarchy

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 CERN
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.TXT for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.TXT for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -37,6 +37,7 @@
enum MAIL_T
{
MAIL_CROSS_PROBE, // PCB<->SCH, CVPCB->SCH cross-probing.
MAIL_SELECTION, // SCH->PCB selection synchronization.
MAIL_ASSIGN_FOOTPRINTS, // CVPCB->SCH footprint stuffing
MAIL_SCH_SAVE, // CVPCB->SCH save the schematic
MAIL_EESCHEMA_NETLIST, // SCH->CVPCB netlist immediately after launching CVPCB

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -29,6 +29,7 @@
*/
struct CROSS_PROBING_SETTINGS
{
bool on_selection; ///< Synchronize the selection for multiple items too
bool center_on_items; ///< Automatically pan to cross-probed items
bool zoom_to_fit; ///< Zoom to fit items (ignored if center_on_items is off)
bool auto_highlight; ///< Automatically turn on highlight mode in the target frame

View File

@ -53,6 +53,7 @@ enum ESCAPE_CONTEXT
{
CTX_NETNAME,
CTX_LIBID,
CTX_IPC,
CTX_QUOTED_STR,
CTX_LINE,
CTX_FILENAME,

View File

@ -131,6 +131,16 @@ public:
*/
static SELECTION_CONDITION HasType( KICAD_T aType );
/**
* Create a functor that tests if among the selected items there is at least one of a
* given types.
*
* @param aTypes is an array containing types that are searched. It has to be ended with
* #KICAD_T::EOT as end marker.
* @return Functor testing for presence of items of a given types.
*/
static SELECTION_CONDITION HasTypes( const KICAD_T aTypes[] );
/**
* Create a functor that tests if the selected items are *only* of given type.
*
@ -179,6 +189,9 @@ private:
///< Helper function used by HasType()
static bool hasTypeFunc( const SELECTION& aSelection, KICAD_T aType );
///< Helper function used by HasTypes()
static bool hasTypesFunc( const SELECTION& aSelection, const KICAD_T aTypes[] );
///< Helper function used by OnlyType()
static bool onlyTypeFunc( const SELECTION& aSelection, KICAD_T aType );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -41,6 +41,7 @@
#include <eda_dde.h>
#include <kiface_base.h>
#include <kiway_express.h>
#include <string_utils.h>
#include <netlist_reader/pcb_netlist.h>
#include <netlist_reader/board_netlist_updater.h>
#include <painter.h>
@ -277,133 +278,7 @@ void PCB_EDIT_FRAME::ExecuteRemoteCommand( const char* cmdline )
{
if( crossProbingSettings.zoom_to_fit )
{
//#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
#ifdef DEFAULT_PCBNEW_CODE
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
// The "fabs" on x ensures the right answer when the view is flipped
screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( fabs( bbSize.x / screenSize.x ),
fabs( bbSize.y / screenSize.y ) );
// Try not to zoom on every cross-probe; it gets very noisy
if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
view->SetScale( view->GetScale() / ratio );
#endif // DEFAULT_PCBNEW_CODE
#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
// This code tries to come up with a zoom factor that doesn't simply zoom in
// to the cross probed component, but instead shows a reasonable amount of the
// circuit around it to provide context. This reduces or eliminates the need
// to manually change the zoom because it's too close.
// Using the default text height as a constant to compare against, use the
// height of the bounding box of visible items for a footprint to figure out
// if this is a big footprint (like a processor) or a small footprint (like a resistor).
// This ratio is not useful by itself as a scaling factor. It must be "bent" to
// provide good scaling at varying component sizes. Bigger components need less
// scaling than small ones.
double currTextHeight = Millimeter2iu( DEFAULT_TEXT_SIZE );
double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
// This will end up as the scaling factor we apply to "ratio".
double compRatioBent = 1.0;
// This is similar to the original KiCad code that scaled the zoom to make sure
// components were visible on screen. It's simply a ratio of screen size to
// component size, and its job is to zoom in to make the component fullscreen.
// Earlier in the code the component BBox is given a 20% margin to add some
// breathing room. We compare the height of this enlarged component bbox to the
// default text height. If a component will end up with the sides clipped, we
// adjust later to make sure it fits on screen.
//
// The "fabs" on x ensures the right answer when the view is flipped
screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
// Original KiCad code for how much to scale the zoom
double kicadRatio = std::max( fabs( bbSize.x / screenSize.x ),
fabs( bbSize.y / screenSize.y ) );
// LUT to scale zoom ratio to provide reasonable schematic context. Must work
// with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
// "first" is used as the input and "second" as the output
//
// "first" = compRatio (footprint height / default text height)
// "second" = Amount to scale ratio by
std::vector<std::pair<double, double>> lut{
{ 1, 8 },
{ 1.5, 5 },
{ 3, 3 },
{ 4.5, 2.5 },
{ 8, 2.0 },
{ 12, 1.7 },
{ 16, 1.5 },
{ 24, 1.3 },
{ 32, 1.0 },
};
std::vector<std::pair<double, double>>::iterator it;
compRatioBent = lut.back().second; // Large component default
if( compRatio >= lut.front().first )
{
// Use LUT to do linear interpolation of "compRatio" within "first", then
// use that result to linearly interpolate "second" which gives the scaling
// factor needed.
for( it = lut.begin(); it < lut.end() - 1; it++ )
{
if( it->first <= compRatio && next( it )->first >= compRatio )
{
double diffx = compRatio - it->first;
double diffn = next( it )->first - it->first;
compRatioBent =
it->second + ( next( it )->second - it->second ) * diffx / diffn;
break; // We have our interpolated value
}
}
}
else
{
compRatioBent = lut.front().second; // Small component default
}
// If the width of the part we're probing is bigger than what the screen width will be
// after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
// part's width will be encompassed within the screen. This will apply to parts that
// are much wider than they are tall.
if( bbSize.x > screenSize.x * ratio * compRatioBent )
{
// Use standard KiCad zoom algorithm for parts too wide to fit screen/
ratio = kicadRatio;
compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
wxLogTrace( "CROSS_PROBE_SCALE",
"Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f",
ratio );
}
// Now that "compRatioBent" holds our final scaling factor we apply it to the original
// fullscreen zoom ratio to arrive at the final ratio itself.
ratio *= compRatioBent;
bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
// Try not to zoom on every cross-probe; it gets very noisy
if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
view->SetScale( view->GetScale() / ratio );
#endif // ifndef DEFAULT_PCBNEW_CODE
GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->zoomFitCrossProbeBBox( bbox );
}
FocusOnLocation( (wxPoint) bbox.Centre() );
@ -510,6 +385,77 @@ void PCB_EDIT_FRAME::SendCrossProbeNetName( const wxString& aNetName )
}
std::vector<BOARD_ITEM*> PCB_EDIT_FRAME::FindItemsFromSyncSelection( std::string syncStr )
{
wxArrayString syncArray = wxStringTokenize( syncStr, "," );
std::vector<BOARD_ITEM*> items;
for( FOOTPRINT* footprint : GetBoard()->Footprints() )
{
if( footprint == nullptr )
continue;
wxString fpSheetPath = footprint->GetPath().AsString().BeforeLast( '/' );
wxString fpUUID = footprint->m_Uuid.AsString();
if( fpSheetPath.IsEmpty() )
fpSheetPath += '/';
if( fpUUID.empty() )
continue;
wxString fpRefEscaped = EscapeString( footprint->GetReference(), CTX_IPC );
for( wxString syncEntry : syncArray )
{
if( syncEntry.empty() )
continue;
wxString syncData = syncEntry.substr( 1 );
switch( syncEntry.GetChar( 0 ).GetValue() )
{
case 'S': // Select sheet with subsheets: S<Sheet path>
if( fpSheetPath.StartsWith( syncData ) )
{
items.push_back( footprint );
}
break;
case 'F': // Select footprint: F<Reference>
if( syncData == fpRefEscaped )
{
items.push_back( footprint );
}
break;
case 'P': // Select pad: P<Footprint reference>/<Pad number>
{
if( syncData.StartsWith( fpRefEscaped ) )
{
wxString selectPadNumberEscaped =
syncData.substr( fpRefEscaped.size() + 1 ); // Skips the slash
wxString selectPadNumber = UnescapeString( selectPadNumberEscaped );
for( PAD* pad : footprint->Pads() )
{
if( selectPadNumber == pad->GetNumber() )
{
items.push_back( pad );
}
}
}
break;
}
default: break;
}
}
}
return items;
}
void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
{
std::string& payload = mail.GetPayload();
@ -575,6 +521,50 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
ExecuteRemoteCommand( payload.c_str() );
break;
case MAIL_SELECTION:
{
// $SELECT: <mode 0 - only footprints, 1 - with connections>,<spec1>,<spec2>,<spec3>
std::string prefix = "$SELECT: ";
if( !payload.compare( 0, prefix.size(), prefix ) )
{
std::string del = ",";
std::string paramStr = payload.substr( prefix.size() );
int modeEnd = paramStr.find( del );
bool selectConnections = false;
try
{
if( std::stoi( paramStr.substr( 0, modeEnd ) ) == 1 )
selectConnections = true;
}
catch( std::invalid_argument& )
{
wxFAIL;
}
std::vector<BOARD_ITEM*> items =
FindItemsFromSyncSelection( paramStr.substr( modeEnd + 1 ) );
m_syncingSchToPcbSelection = true; // recursion guard
if( selectConnections )
{
GetToolManager()->RunAction( PCB_ACTIONS::syncSelectionWithNets, true,
static_cast<void*>( &items ) );
}
else
{
GetToolManager()->RunAction( PCB_ACTIONS::syncSelection, true,
static_cast<void*>( &items ) );
}
m_syncingSchToPcbSelection = false;
}
break;
}
case MAIL_PCB_UPDATE:
m_toolManager->RunAction( ACTIONS::updatePcbFromSchematic, true );
break;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Jean-Pierre Charras, jean-pierre.charras at wanadoo.fr
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -57,6 +57,7 @@ void PANEL_DISPLAY_OPTIONS::loadPCBSettings( PCBNEW_SETTINGS* aCfg )
m_OptDisplayPadNoConn->SetValue( aCfg->m_Display.m_DisplayPadNoConnects );
m_ShowNetNamesOption->SetSelection( aCfg->m_Display.m_DisplayNetNamesMode );
m_live3Drefresh->SetValue( aCfg->m_Display.m_Live3DRefresh );
m_checkCrossProbeOnSelection->SetValue( aCfg->m_CrossProbing.on_selection );
m_checkCrossProbeCenter->SetValue( aCfg->m_CrossProbing.center_on_items );
m_checkCrossProbeZoom->SetValue( aCfg->m_CrossProbing.zoom_to_fit );
m_checkCrossProbeAutoHighlight->SetValue( aCfg->m_CrossProbing.auto_highlight );
@ -99,7 +100,7 @@ bool PANEL_DISPLAY_OPTIONS::TransferDataFromWindow()
cfg->m_Display.m_DisplayPadNoConnects = m_OptDisplayPadNoConn->GetValue();
cfg->m_Display.m_DisplayNetNamesMode = m_ShowNetNamesOption->GetSelection();
cfg->m_Display.m_Live3DRefresh = m_live3Drefresh->GetValue();
cfg->m_CrossProbing.center_on_items = m_checkCrossProbeCenter->GetValue();
cfg->m_CrossProbing.on_selection = m_checkCrossProbeOnSelection->GetValue();
cfg->m_CrossProbing.zoom_to_fit = m_checkCrossProbeZoom->GetValue();
cfg->m_CrossProbing.auto_highlight = m_checkCrossProbeAutoHighlight->GetValue();
}

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -73,6 +73,10 @@ PANEL_DISPLAY_OPTIONS_BASE::PANEL_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWind
wxStaticBoxSizer* sbSizer3;
sbSizer3 = new wxStaticBoxSizer( new wxStaticBox( pcbPage, wxID_ANY, _("Cross-probing") ), wxVERTICAL );
m_checkCrossProbeOnSelection = new wxCheckBox( sbSizer3->GetStaticBox(), wxID_ANY, _("Cross-probe on selection"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeOnSelection->SetValue(true);
sbSizer3->Add( m_checkCrossProbeOnSelection, 0, wxALL, 5 );
m_checkCrossProbeCenter = new wxCheckBox( sbSizer3->GetStaticBox(), wxID_ANY, _("Scroll cross-probed items into view"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkCrossProbeCenter->SetValue(true);
sbSizer3->Add( m_checkCrossProbeCenter, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="15" />
<FileVersion major="1" minor="16" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
@ -14,6 +14,7 @@
<property name="file">panel_display_options_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">PanelDisplayOptions</property>
@ -25,6 +26,7 @@
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Panel" expanded="1">
@ -46,6 +48,7 @@
<property name="size">-1,-1</property>
<property name="subclass">RESETTABLE_PANEL; widgets/resettable_panel.h; Not forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
@ -608,6 +611,70 @@
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">1</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Cross-probe on selection</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_checkCrossProbeOnSelection</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Oct 26 2018)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -45,6 +45,7 @@ class PANEL_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
wxCheckBox* m_OptDisplayPadNoConn;
wxRadioBox* m_OptDisplayTracksClearance;
wxCheckBox* m_OptDisplayPadClearence;
wxCheckBox* m_checkCrossProbeOnSelection;
wxCheckBox* m_checkCrossProbeCenter;
wxCheckBox* m_checkCrossProbeZoom;
wxCheckBox* m_checkCrossProbeAutoHighlight;
@ -53,6 +54,7 @@ class PANEL_DISPLAY_OPTIONS_BASE : public RESETTABLE_PANEL
public:
PANEL_DISPLAY_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_DISPLAY_OPTIONS_BASE();
};

View File

@ -4,7 +4,7 @@
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2013-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2013-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -175,10 +175,10 @@ END_EVENT_TABLE()
PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ), wxDefaultPosition,
wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, PCB_EDIT_FRAME_NAME ),
m_exportNetlistAction( nullptr ),
m_findDialog( nullptr )
PCB_BASE_EDIT_FRAME( aKiway, aParent, FRAME_PCB_EDITOR, _( "PCB Editor" ),
wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE,
PCB_EDIT_FRAME_NAME ),
m_exportNetlistAction( nullptr ), m_findDialog( nullptr )
{
m_maximizeByDefault = true;
m_showBorderAndTitleBlock = true; // true to display sheet references
@ -187,6 +187,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_SelLayerBox = nullptr;
m_show_layer_manager_tools = true;
m_supportsAutoSave = true;
m_syncingSchToPcbSelection = false;
// We don't know what state board was in when it was last saved, so we have to
// assume dirty

View File

@ -121,6 +121,11 @@ public:
void KiwayMailIn( KIWAY_EXPRESS& aEvent ) override;
/**
* Used to find items by selection synchronization spec string.
*/
std::vector<BOARD_ITEM*> FindItemsFromSyncSelection( std::string syncStr );
/**
* Show the Find dialog.
*/
@ -782,6 +787,8 @@ public:
bool m_ZoneFillsDirty; // Board has been modified since last zone fill.
bool m_syncingSchToPcbSelection; // Recursion guard when synchronizing selection from schematic
private:
friend struct PCB::IFACE;
friend class APPEARANCE_CONTROLS;

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -866,7 +866,10 @@ int BOARD_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
int BOARD_INSPECTION_TOOL::CrossProbePcbToSch( const TOOL_EVENT& aEvent )
{
// Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ...
if( m_probingSchToPcb )
if( m_probingSchToPcb || m_frame->m_syncingSchToPcbSelection )
return 0;
if( !frame()->Settings().m_CrossProbing.on_selection )
return 0;
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -1240,6 +1240,12 @@ TOOL_ACTION PCB_ACTIONS::selectConnection( "pcbnew.InteractiveSelection.SelectCo
_( "Selects a connection or expands an existing selection to junctions, pads, or entire connections" ),
BITMAPS::add_tracks );
TOOL_ACTION PCB_ACTIONS::syncSelection( "pcbnew.InteractiveSelection.SyncSelection",
AS_GLOBAL );
TOOL_ACTION PCB_ACTIONS::syncSelectionWithNets( "pcbnew.InteractiveSelection.SyncSelectionWithNets",
AS_GLOBAL );
TOOL_ACTION PCB_ACTIONS::selectNet( "pcbnew.InteractiveSelection.SelectNet",
AS_GLOBAL, 0, "",
_( "Select All Tracks in Net" ),

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
@ -66,6 +66,12 @@ public:
static TOOL_ACTION selectItems;
static TOOL_ACTION unselectItems;
/// Sets selection to specified items, zooms to fit, if enabled
static TOOL_ACTION syncSelection;
/// Sets selection to specified items with connected nets, zooms to fit, if enabled
static TOOL_ACTION syncSelectionWithNets;
/// Run a selection menu to select from a list of items
static TOOL_ACTION selectionMenu;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2017 CERN
* Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
@ -25,6 +25,7 @@
*/
#include <limits>
#include <cmath>
#include <functional>
using namespace std::placeholders;
#include <core/kicad_algo.h>
@ -62,6 +63,7 @@ using namespace std::placeholders;
#include <wx/event.h>
#include <wx/timer.h>
#include <wx/log.h>
#include <profile.h>
class SELECT_MENU : public ACTION_MENU
@ -1100,34 +1102,49 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
for( const EDA_ITEM* item : m_selection.GetItems() )
{
if( dynamic_cast<const BOARD_CONNECTED_ITEM*>( item ) )
if( item->Type() == PCB_FOOTPRINT_T || BOARD_CONNECTED_ITEM::ClassOf( item ) )
initialCount++;
}
if( initialCount == 0 )
selectCursor( true, connectedItemFilter );
m_frame->SetStatusText( _( "Select/Expand Connection..." ) );
for( STOP_CONDITION stopCondition : { STOP_AT_JUNCTION, STOP_AT_PAD, STOP_NEVER } )
{
// copy the selection, since we're going to iterate and modify
std::deque<EDA_ITEM*> selectedItems = m_selection.GetItems();
for( EDA_ITEM* item : selectedItems )
item->ClearTempFlags();
std::vector<BOARD_CONNECTED_ITEM*> startItems;
for( EDA_ITEM* item : selectedItems )
{
PCB_TRACK* trackItem = dynamic_cast<PCB_TRACK*>( item );
if( item->Type() == PCB_FOOTPRINT_T )
{
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
// Track items marked SKIP_STRUCT have already been visited
if( trackItem && !( trackItem->GetFlags() & SKIP_STRUCT ) )
selectConnectedTracks( *trackItem, stopCondition );
for( PAD* pad : footprint->Pads() )
{
startItems.push_back( pad );
}
}
else if( BOARD_CONNECTED_ITEM::ClassOf( item ) )
{
startItems.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
}
}
selectAllConnectedTracks( startItems, stopCondition );
if( m_selection.GetItems().size() > initialCount )
break;
}
m_frame->SetStatusText( wxEmptyString );
// Inform other potentially interested tools
if( m_selection.Size() > 0 )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
@ -1136,165 +1153,220 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
}
void PCB_SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem,
STOP_CONDITION aStopCondition )
void PCB_SELECTION_TOOL::selectAllConnectedTracks(
const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems, STOP_CONDITION aStopCondition )
{
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
constexpr PCB_LAYER_ID ALL_LAYERS = UNDEFINED_LAYER;
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
const LSET allCuMask = LSET::AllCuMask();
PROF_TIMER refreshTimer;
double refreshIntervalMs = 500; // Refresh display with this interval to indicate progress
int lastSelectionSize = m_selection.GetSize();
auto connectivity = board()->GetConnectivity();
auto connectedItems = connectivity->GetConnectedItems( &aStartItem, types, true );
std::map<VECTOR2I, std::vector<PCB_TRACK*>> trackMap;
std::map<VECTOR2I, PCB_VIA*> viaMap;
std::map<VECTOR2I, PAD*> padMap;
std::set<PAD*> startPadSet;
std::vector<BOARD_CONNECTED_ITEM*> cleanupItems;
std::vector<std::pair<VECTOR2I, LSET>> activePts;
// Build maps of connected items
for( BOARD_CONNECTED_ITEM* item : connectedItems )
for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
{
switch( item->Type() )
// Track starting pads
if( startItem->Type() == PCB_PAD_T )
startPadSet.insert( static_cast<PAD*>( startItem ) );
}
for( BOARD_CONNECTED_ITEM* startItem : aStartItems )
{
if( startItem->HasFlag( SKIP_STRUCT ) ) // Skip already visited items
continue;
std::vector<BOARD_CONNECTED_ITEM*> connectedItems =
connectivity->GetConnectedItems( startItem, types, true );
// Build maps of connected items
for( BOARD_CONNECTED_ITEM* item : connectedItems )
{
switch( item->Type() )
{
case PCB_ARC_T:
case PCB_TRACE_T:
{
PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
trackMap[track->GetStart()].push_back( track );
trackMap[track->GetEnd()].push_back( track );
break;
}
case PCB_VIA_T:
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
viaMap[via->GetStart()] = via;
break;
}
case PCB_PAD_T:
{
PAD* pad = static_cast<PAD*>( item );
padMap[pad->GetPosition()] = pad;
break;
}
default: break;
}
}
// Set up the initial active points
switch( startItem->Type() )
{
case PCB_ARC_T:
case PCB_TRACE_T:
{
PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
trackMap[ track->GetStart() ].push_back( track );
trackMap[ track->GetEnd() ].push_back( track );
PCB_TRACK* track = static_cast<PCB_TRACK*>( startItem );
activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
break;
}
case PCB_VIA_T:
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
viaMap[ via->GetStart() ] = via;
activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
break;
}
case PCB_PAD_T:
{
PAD* pad = static_cast<PAD*>( item );
padMap[ pad->GetPosition() ] = pad;
activePts.push_back( { startItem->GetPosition(), startItem->GetLayerSet() } );
break;
default: break;
}
default:
break;
}
bool expand = true;
int failSafe = 0;
item->ClearFlags( TEMP_SELECTED );
}
std::vector<std::pair<VECTOR2I, PCB_LAYER_ID>> activePts;
// Set up the initial active points
switch( aStartItem.Type() )
{
case PCB_ARC_T:
case PCB_TRACE_T:
{
PCB_TRACK* track = static_cast<PCB_TRACK*>( &aStartItem );
activePts.push_back( { track->GetStart(), track->GetLayer() } );
activePts.push_back( { track->GetEnd(), track->GetLayer() } );
}
break;
case PCB_VIA_T:
activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
break;
case PCB_PAD_T:
activePts.push_back( { aStartItem.GetPosition(), ALL_LAYERS } );
break;
default:
break;
}
bool expand = true;
int failSafe = 0;
// Iterative push from all active points
while( expand && failSafe++ < 100000 )
{
expand = false;
for( int i = activePts.size() - 1; i >= 0; --i )
// Iterative push from all active points
while( expand && failSafe++ < 100000 )
{
VECTOR2I pt = activePts[i].first;
PCB_LAYER_ID layer = activePts[i].second;
size_t pt_count = 0;
expand = false;
for( PCB_TRACK* track : trackMap[pt] )
for( int i = activePts.size() - 1; i >= 0; --i )
{
if( layer == ALL_LAYERS || layer == track->GetLayer() )
pt_count++;
}
VECTOR2I pt = activePts[i].first;
LSET layerSetCu = activePts[i].second & allCuMask;
if( aStopCondition == STOP_AT_JUNCTION )
{
if( pt_count > 2
|| ( viaMap.count( pt ) && layer != ALL_LAYERS )
|| ( padMap.count( pt ) && layer != ALL_LAYERS ) )
auto viaIt = viaMap.find( pt );
auto padIt = padMap.find( pt );
bool gotVia = ( viaIt != viaMap.end() )
&& ( layerSetCu & ( viaIt->second->GetLayerSet() ) ).any();
bool gotPad = ( padIt != padMap.end() )
&& ( layerSetCu & ( padIt->second->GetLayerSet() ) ).any();
bool gotNonStartPad =
gotPad && ( startPadSet.find( padIt->second ) == startPadSet.end() );
if( aStopCondition == STOP_AT_JUNCTION )
{
activePts.erase( activePts.begin() + i );
continue;
size_t pt_count = 0;
for( PCB_TRACK* track : trackMap[pt] )
{
if( layerSetCu.Contains( track->GetLayer() ) )
pt_count++;
}
if( pt_count > 2 || gotVia || gotNonStartPad )
{
activePts.erase( activePts.begin() + i );
continue;
}
}
}
else if( aStopCondition == STOP_AT_PAD )
{
if( padMap.count( pt ) )
else if( aStopCondition == STOP_AT_PAD )
{
activePts.erase( activePts.begin() + i );
continue;
if( gotNonStartPad )
{
activePts.erase( activePts.begin() + i );
continue;
}
}
}
if( padMap.count( pt ) )
{
PAD* pad = padMap[ pt ];
if( !( pad->GetFlags() & TEMP_SELECTED ) )
if( gotPad )
{
pad->SetFlags( TEMP_SELECTED );
activePts.push_back( { pad->GetPosition(), ALL_LAYERS } );
expand = true;
PAD* pad = padIt->second;
if( !pad->HasFlag( SKIP_STRUCT ) )
{
pad->SetFlags( SKIP_STRUCT );
cleanupItems.push_back( pad );
activePts.push_back( { pad->GetPosition(), pad->GetLayerSet() } );
expand = true;
}
}
}
for( PCB_TRACK* track : trackMap[ pt ] )
{
if( layer != ALL_LAYERS && track->GetLayer() != layer )
continue;
if( !track->IsSelected() )
for( PCB_TRACK* track : trackMap[pt] )
{
select( track );
if( !layerSetCu.Contains( track->GetLayer() ) )
continue;
if( track->GetStart() == pt )
activePts.push_back( { track->GetEnd(), track->GetLayer() } );
else
activePts.push_back( { track->GetStart(), track->GetLayer() } );
if( !track->IsSelected() )
select( track );
expand = true;
if( !track->HasFlag( SKIP_STRUCT ) )
{
track->SetFlags( SKIP_STRUCT );
cleanupItems.push_back( track );
if( track->GetStart() == pt )
activePts.push_back( { track->GetEnd(), track->GetLayerSet() } );
else
activePts.push_back( { track->GetStart(), track->GetLayerSet() } );
expand = true;
}
}
}
if( viaMap.count( pt ) )
{
PCB_VIA* via = viaMap[ pt ];
if( !via->IsSelected() )
if( viaMap.count( pt ) )
{
select( via );
activePts.push_back( { via->GetPosition(), ALL_LAYERS } );
expand = true;
PCB_VIA* via = viaMap[pt];
if( !via->IsSelected() )
select( via );
if( !via->HasFlag( SKIP_STRUCT ) )
{
via->SetFlags( SKIP_STRUCT );
cleanupItems.push_back( via );
activePts.push_back( { via->GetPosition(), via->GetLayerSet() } );
expand = true;
}
}
activePts.erase( activePts.begin() + i );
}
activePts.erase( activePts.begin() + i );
// Refresh display for the feel of progress
if( refreshTimer.msecs() >= refreshIntervalMs )
{
if( m_selection.Size() != lastSelectionSize )
{
m_frame->GetCanvas()->ForceRefresh();
lastSelectionSize = m_selection.Size();
}
refreshTimer.Start();
}
}
}
for( BOARD_CONNECTED_ITEM* item : cleanupItems )
{
item->ClearFlags( SKIP_STRUCT );
}
}
@ -1348,7 +1420,7 @@ int PCB_SELECTION_TOOL::selectNet( const TOOL_EVENT& aEvent )
void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
{
std::list<FOOTPRINT*> footprintList;
std::vector<BOARD_ITEM*> footprints;
// store all footprints that are on that sheet path
for( FOOTPRINT* footprint : board()->Footprints() )
@ -1356,52 +1428,88 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
if( footprint == nullptr )
continue;
wxString footprint_path = footprint->GetPath().AsString().BeforeLast('/');
wxString footprint_path = footprint->GetPath().AsString().BeforeLast( '/' );
if( aSheetPath.IsEmpty() )
aSheetPath += '/';
if( footprint_path == aSheetPath )
footprintList.push_back( footprint );
footprints.push_back( footprint );
}
// Generate a list of all pads, and of all nets they belong to.
std::list<int> netcodeList;
std::list<PAD*> padList;
for( FOOTPRINT* footprint : footprintList )
for( BOARD_ITEM* i : footprints )
{
for( PAD* pad : footprint->Pads() )
if( i != nullptr )
select( i );
}
selectConnections( footprints );
}
void PCB_SELECTION_TOOL::selectConnections( const std::vector<BOARD_ITEM*>& aItems )
{
// Generate a list of all pads, and of all nets they belong to.
std::list<int> netcodeList;
std::vector<BOARD_CONNECTED_ITEM*> padList;
for( BOARD_ITEM* item : aItems )
{
switch( item->Type() )
{
case PCB_FOOTPRINT_T:
{
for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
{
if( pad->IsConnected() )
{
netcodeList.push_back( pad->GetNetCode() );
padList.push_back( pad );
}
}
break;
}
case PCB_PAD_T:
{
PAD* pad = static_cast<PAD*>( item );
if( pad->IsConnected() )
{
netcodeList.push_back( pad->GetNetCode() );
padList.push_back( pad );
}
break;
}
default: break;
}
}
// Sort for binary search
std::sort( padList.begin(), padList.end() );
// remove all duplicates
netcodeList.sort();
netcodeList.unique();
for( PAD* pad : padList )
selectConnectedTracks( *pad, STOP_NEVER );
selectAllConnectedTracks( padList, STOP_AT_PAD );
// now we need to find all footprints that are connected to each of these nets then we need
// to determine if these footprints are in the list of footprints belonging to this sheet
std::list<int> removeCodeList;
// to determine if these footprints are in the list of footprints
std::vector<int> removeCodeList;
constexpr KICAD_T padType[] = { PCB_PAD_T, EOT };
for( int netCode : netcodeList )
{
for( BOARD_CONNECTED_ITEM* mitem : board()->GetConnectivity()->GetNetItems( netCode,
padType ) )
for( BOARD_CONNECTED_ITEM* mitem :
board()->GetConnectivity()->GetNetItems( netCode, padType ) )
{
if( mitem->Type() == PCB_PAD_T && !alg::contains( footprintList, mitem->GetParent() ) )
if( mitem->Type() == PCB_PAD_T
&& !std::binary_search( padList.begin(), padList.end(), mitem ) )
{
// if we cannot find the footprint of the pad in the footprintList then we can
// assume that that footprint is not located in the same schematic, therefore
// if we cannot find the pad in the padList then we can
// assume that that pad should not be used, therefore
// invalidate this netcode.
removeCodeList.push_back( netCode );
break;
@ -1409,31 +1517,21 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
}
}
// remove all duplicates
removeCodeList.sort();
removeCodeList.unique();
for( int removeCode : removeCodeList )
{
netcodeList.remove( removeCode );
}
std::list<BOARD_CONNECTED_ITEM*> localConnectionList;
constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
std::vector<BOARD_CONNECTED_ITEM*> localConnectionList;
constexpr KICAD_T trackViaType[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, EOT };
for( int netCode : netcodeList )
{
for( BOARD_CONNECTED_ITEM* item : board()->GetConnectivity()->GetNetItems( netCode,
trackViaType ) )
for( BOARD_CONNECTED_ITEM* item :
board()->GetConnectivity()->GetNetItems( netCode, trackViaType ) )
localConnectionList.push_back( item );
}
for( BOARD_ITEM* i : footprintList )
{
if( i != nullptr )
select( i );
}
for( BOARD_CONNECTED_ITEM* i : localConnectionList )
{
if( i != nullptr )
@ -1442,27 +1540,55 @@ void PCB_SELECTION_TOOL::selectAllItemsOnSheet( wxString& aSheetPath )
}
void PCB_SELECTION_TOOL::zoomFitSelection()
int PCB_SELECTION_TOOL::syncSelection( const TOOL_EVENT& aEvent )
{
// Should recalculate the view to zoom in on the selection.
auto selectionBox = m_selection.GetBoundingBox();
auto view = getView();
std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
screenSize.x = std::max( 10.0, screenSize.x );
screenSize.y = std::max( 10.0, screenSize.y );
if( items )
doSyncSelection( *items, false );
if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
return 0;
}
int PCB_SELECTION_TOOL::syncSelectionWithNets( const TOOL_EVENT& aEvent )
{
std::vector<BOARD_ITEM*>* items = aEvent.Parameter<std::vector<BOARD_ITEM*>*>();
if( items )
doSyncSelection( *items, true );
return 0;
}
void PCB_SELECTION_TOOL::doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets )
{
ClearSelection( true /*quiet mode*/ );
// Perform individual selection of each item before processing the event.
for( BOARD_ITEM* item : aItems )
select( item );
if( aWithNets )
selectConnections( aItems );
EDA_RECT bbox = m_selection.GetBoundingBox();
if( m_frame->Settings().m_CrossProbing.center_on_items )
{
VECTOR2D vsize = selectionBox.GetSize();
double scale = view->GetScale() / std::max( fabs( vsize.x / screenSize.x ),
fabs( vsize.y / screenSize.y ) );
view->SetScale( scale );
view->SetCenter( selectionBox.Centre() );
view->Add( &m_selection );
if( m_frame->Settings().m_CrossProbing.zoom_to_fit )
zoomFitCrossProbeBBox( bbox );
m_frame->FocusOnLocation( bbox.Centre() );
}
view()->UpdateAllLayersColor();
m_frame->GetCanvas()->ForceRefresh();
if( m_selection.Size() > 0 )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
}
@ -1519,6 +1645,158 @@ int PCB_SELECTION_TOOL::selectSameSheet( const TOOL_EVENT& aEvent )
}
void PCB_SELECTION_TOOL::zoomFitSelection()
{
// Should recalculate the view to zoom in on the selection.
auto selectionBox = m_selection.GetBoundingBox();
auto view = getView();
VECTOR2D screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
screenSize.x = std::max( 10.0, screenSize.x );
screenSize.y = std::max( 10.0, screenSize.y );
if( selectionBox.GetWidth() != 0 || selectionBox.GetHeight() != 0 )
{
VECTOR2D vsize = selectionBox.GetSize();
double scale = view->GetScale()
/ std::max( fabs( vsize.x / screenSize.x ), fabs( vsize.y / screenSize.y ) );
view->SetScale( scale );
view->SetCenter( selectionBox.Centre() );
view->Add( &m_selection );
}
m_frame->GetCanvas()->ForceRefresh();
}
void PCB_SELECTION_TOOL::zoomFitCrossProbeBBox( EDA_RECT bbox )
{
// Should recalculate the view to zoom in on the bbox.
auto view = getView();
if( bbox.GetWidth() == 0 && bbox.GetHeight() != 0 )
return;
//#define DEFAULT_PCBNEW_CODE // Un-comment for normal full zoom KiCad algorithm
#ifdef DEFAULT_PCBNEW_CODE
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
auto screenSize = view->ToWorld( GetCanvas()->GetClientSize(), false );
// The "fabs" on x ensures the right answer when the view is flipped
screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
// Try not to zoom on every cross-probe; it gets very noisy
if( crossProbingSettings.zoom_to_fit && ( ratio < 0.5 || ratio > 1.0 ) )
view->SetScale( view->GetScale() / ratio );
#endif // DEFAULT_PCBNEW_CODE
#ifndef DEFAULT_PCBNEW_CODE // Do the scaled zoom
auto bbSize = bbox.Inflate( bbox.GetWidth() * 0.2f ).GetSize();
auto screenSize = view->ToWorld( m_frame->GetCanvas()->GetClientSize(), false );
// This code tries to come up with a zoom factor that doesn't simply zoom in
// to the cross probed component, but instead shows a reasonable amount of the
// circuit around it to provide context. This reduces or eliminates the need
// to manually change the zoom because it's too close.
// Using the default text height as a constant to compare against, use the
// height of the bounding box of visible items for a footprint to figure out
// if this is a big footprint (like a processor) or a small footprint (like a resistor).
// This ratio is not useful by itself as a scaling factor. It must be "bent" to
// provide good scaling at varying component sizes. Bigger components need less
// scaling than small ones.
double currTextHeight = Millimeter2iu( DEFAULT_TEXT_SIZE );
double compRatio = bbSize.y / currTextHeight; // Ratio of component to text height
// This will end up as the scaling factor we apply to "ratio".
double compRatioBent = 1.0;
// This is similar to the original KiCad code that scaled the zoom to make sure
// components were visible on screen. It's simply a ratio of screen size to
// component size, and its job is to zoom in to make the component fullscreen.
// Earlier in the code the component BBox is given a 20% margin to add some
// breathing room. We compare the height of this enlarged component bbox to the
// default text height. If a component will end up with the sides clipped, we
// adjust later to make sure it fits on screen.
//
// The "fabs" on x ensures the right answer when the view is flipped
screenSize.x = std::max( 10.0, fabs( screenSize.x ) );
screenSize.y = std::max( 10.0, screenSize.y );
double ratio = std::max( -1.0, fabs( bbSize.y / screenSize.y ) );
// Original KiCad code for how much to scale the zoom
double kicadRatio =
std::max( fabs( bbSize.x / screenSize.x ), fabs( bbSize.y / screenSize.y ) );
// LUT to scale zoom ratio to provide reasonable schematic context. Must work
// with footprints of varying sizes (e.g. 0402 package and 200 pin BGA).
// "first" is used as the input and "second" as the output
//
// "first" = compRatio (footprint height / default text height)
// "second" = Amount to scale ratio by
std::vector<std::pair<double, double>> lut{
{ 1, 8 }, { 1.5, 5 }, { 3, 3 }, { 4.5, 2.5 }, { 8, 2.0 },
{ 12, 1.7 }, { 16, 1.5 }, { 24, 1.3 }, { 32, 1.0 },
};
std::vector<std::pair<double, double>>::iterator it;
compRatioBent = lut.back().second; // Large component default
if( compRatio >= lut.front().first )
{
// Use LUT to do linear interpolation of "compRatio" within "first", then
// use that result to linearly interpolate "second" which gives the scaling
// factor needed.
for( it = lut.begin(); it < lut.end() - 1; it++ )
{
if( it->first <= compRatio && next( it )->first >= compRatio )
{
double diffx = compRatio - it->first;
double diffn = next( it )->first - it->first;
compRatioBent = it->second + ( next( it )->second - it->second ) * diffx / diffn;
break; // We have our interpolated value
}
}
}
else
{
compRatioBent = lut.front().second; // Small component default
}
// If the width of the part we're probing is bigger than what the screen width will be
// after the zoom, then punt and use the KiCad zoom algorithm since it guarantees the
// part's width will be encompassed within the screen. This will apply to parts that
// are much wider than they are tall.
if( bbSize.x > screenSize.x * ratio * compRatioBent )
{
// Use standard KiCad zoom algorithm for parts too wide to fit screen/
ratio = kicadRatio;
compRatioBent = 1.0; // Reset so we don't modify the "KiCad" ratio
wxLogTrace( "CROSS_PROBE_SCALE",
"Part TOO WIDE for screen. Using normal KiCad zoom ratio: %1.5f", ratio );
}
// Now that "compRatioBent" holds our final scaling factor we apply it to the original
// fullscreen zoom ratio to arrive at the final ratio itself.
ratio *= compRatioBent;
bool alwaysZoom = false; // DEBUG - allows us to minimize zooming or not
// Try not to zoom on every cross-probe; it gets very noisy
if( ( ratio < 0.5 || ratio > 1.0 ) || alwaysZoom )
view->SetScale( view->GetScale() / ratio );
#endif // ifndef DEFAULT_PCBNEW_CODE
}
void PCB_SELECTION_TOOL::FindItem( BOARD_ITEM* aItem )
{
bool cleared = false;
@ -2772,6 +3050,9 @@ void PCB_SELECTION_TOOL::setTransitions()
Go( &PCB_SELECTION_TOOL::expandConnection, PCB_ACTIONS::selectConnection.MakeEvent() );
Go( &PCB_SELECTION_TOOL::selectNet, PCB_ACTIONS::selectNet.MakeEvent() );
Go( &PCB_SELECTION_TOOL::selectNet, PCB_ACTIONS::deselectNet.MakeEvent() );
Go( &PCB_SELECTION_TOOL::syncSelection, PCB_ACTIONS::syncSelection.MakeEvent() );
Go( &PCB_SELECTION_TOOL::syncSelectionWithNets,
PCB_ACTIONS::syncSelectionWithNets.MakeEvent() );
Go( &PCB_SELECTION_TOOL::selectSameSheet, PCB_ACTIONS::selectSameSheet.MakeEvent() );
Go( &PCB_SELECTION_TOOL::selectSheetContents,
PCB_ACTIONS::selectOnSheetFromEeschema.MakeEvent() );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2017 CERN
* Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.TXT for contributors.
* Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.TXT for contributors.
*
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* @author Maciej Suminski <maciej.suminski@cern.ch>
@ -181,6 +181,9 @@ public:
///< Zoom the screen to center and fit the current selection.
void zoomFitSelection();
///< Zoom the screen to fit the bounding box for cross probing/selection sync.
void zoomFitCrossProbeBBox( EDA_RECT bbox );
BOARD* GetBoard() const
{
return board();
@ -304,7 +307,8 @@ private:
*
* @param aStopCondition where to stop selecting more items
*/
void selectConnectedTracks( BOARD_CONNECTED_ITEM& aSourceItem, STOP_CONDITION aStopCondition );
void selectAllConnectedTracks( const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems,
STOP_CONDITION aStopCondition );
/**
* Select all items with the given net code.
@ -314,6 +318,11 @@ private:
*/
void selectAllItemsOnNet( int aNetCode, bool aSelect = true );
/*
* Select tracks and vias connected to specified board items.
*/
void selectConnections( const std::vector<BOARD_ITEM*>& aItems );
/**
* Select all items with the given sheet timestamp/UUID name (the sheet path).
*
@ -328,6 +337,12 @@ private:
///< (same sheet path).
int selectSameSheet( const TOOL_EVENT& aEvent );
///< Set selection to items passed by parameter and connected nets (optionally).
///< Zooms to fit, if enabled
int syncSelection( const TOOL_EVENT& aEvent );
int syncSelectionWithNets( const TOOL_EVENT& aEvent );
void doSyncSelection( const std::vector<BOARD_ITEM*>& aItems, bool aWithNets );
///< Invoke filter dialog and modify current selection
int filterSelection( const TOOL_EVENT& aEvent );

View File

@ -435,8 +435,8 @@ int PCB_SELECTION_TOOL::expandConnection( const TOOL_EVENT& aEvent )
}
void PCB_SELECTION_TOOL::selectConnectedTracks( BOARD_CONNECTED_ITEM& aStartItem,
STOP_CONDITION aStopCondition )
void PCB_SELECTION_TOOL::selectAllConnectedTracks(
const std::vector<BOARD_CONNECTED_ITEM*>& aStartItems, STOP_CONDITION aStopCondition )
{
}