2012-06-08 09:56:42 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2018-06-02 16:14:34 +00:00
|
|
|
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2012-06-08 09:56:42 +00:00
|
|
|
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
|
|
|
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
|
|
|
|
*
|
2020-02-20 12:11:04 +00:00
|
|
|
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
|
2012-06-08 09:56:42 +00:00
|
|
|
*
|
|
|
|
* 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 Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
#include <algorithm>
|
2017-07-17 12:55:31 +00:00
|
|
|
#include <iterator>
|
2018-01-29 15:39:40 +00:00
|
|
|
#include <pcb_base_frame.h>
|
2013-04-25 16:29:35 +00:00
|
|
|
#include <reporter.h>
|
2020-10-14 00:23:36 +00:00
|
|
|
#include <page_layout/ws_proxy_view_item.h>
|
2020-08-11 19:37:07 +00:00
|
|
|
#include <board_commit.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <class_module.h>
|
|
|
|
#include <class_track.h>
|
|
|
|
#include <class_zone.h>
|
|
|
|
#include <class_marker_pcb.h>
|
2017-10-31 15:38:10 +00:00
|
|
|
#include <class_pcb_target.h>
|
2020-10-24 12:53:11 +00:00
|
|
|
#include <core/kicad_algo.h>
|
2018-10-12 06:17:15 +00:00
|
|
|
#include <connectivity/connectivity_data.h>
|
2020-10-15 22:39:33 +00:00
|
|
|
#include <kicad_string.h>
|
2019-08-20 11:58:14 +00:00
|
|
|
#include <pgm_base.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <pcbnew_settings.h>
|
2020-05-31 21:42:04 +00:00
|
|
|
#include <project.h>
|
|
|
|
#include <project/net_settings.h>
|
|
|
|
#include <project/project_file.h>
|
|
|
|
#include <project/project_local_settings.h>
|
2020-06-16 18:15:14 +00:00
|
|
|
#include <ratsnest/ratsnest_data.h>
|
|
|
|
#include <ratsnest/ratsnest_viewitem.h>
|
2020-08-11 19:37:07 +00:00
|
|
|
#include <tool/selection_conditions.h>
|
2020-10-23 06:48:52 +00:00
|
|
|
#include <convert_drawsegment_list_to_polygon.h>
|
2007-12-01 05:37:44 +00:00
|
|
|
|
2011-09-30 18:15:37 +00:00
|
|
|
/* This is an odd place for this, but CvPcb won't link if it is
|
2009-05-28 08:42:24 +00:00
|
|
|
* in class_board_item.cpp like I first tried it.
|
|
|
|
*/
|
|
|
|
wxPoint BOARD_ITEM::ZeroOffset( 0, 0 );
|
2008-04-01 05:21:50 +00:00
|
|
|
|
2011-09-14 20:04:58 +00:00
|
|
|
|
++PCBNew
* Removed Pcb_Frame argument from BOARD() constructor, since it precludes
having a BOARD being edited by more than one editor, it was a bad design.
And this meant removing m_PcbFrame from BOARD.
* removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame
* Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp
* added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance
* a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed,
such as dialog_mask_clearance, dialog_drc, etc.
* Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it
with build_version.h's #define BOARD_FILE_VERSION, although there may be a
better place for this constant.
* Made the public functions in PARAM_CFG_ARRAY be type const.
void SaveParam(..) const and void ReadParam(..) const
* PARAM_CFG_BASE now has virtual destructor since we have various way of
destroying the derived class and boost::ptr_vector must be told about this.
* Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use
an automatic PARAM_CFG_ARRAY which is on the stack.\
* PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array,
since it has to access the current BOARD and the BOARD can change.
Remember BOARD_DESIGN_SETTINGS are now in the BOARD.
* Made the m_BoundingBox member private, this was a brutally hard task,
and indicative of the lack of commitment to accessors and object oriented
design on the part of KiCad developers. We must do better.
Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox().
* Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
|
|
|
BOARD::BOARD() :
|
2020-03-26 11:02:59 +00:00
|
|
|
BOARD_ITEM_CONTAINER( (BOARD_ITEM*) NULL, PCB_T ),
|
2020-02-27 17:46:49 +00:00
|
|
|
m_boardUse( BOARD_USE::NORMAL ),
|
2020-03-26 11:02:59 +00:00
|
|
|
m_paper( PAGE_INFO::A4 ),
|
2020-05-31 21:42:04 +00:00
|
|
|
m_project( nullptr ),
|
|
|
|
m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
|
2020-03-26 11:02:59 +00:00
|
|
|
m_NetInfo( this ),
|
2020-05-31 21:42:04 +00:00
|
|
|
m_LegacyDesignSettingsLoaded( false ),
|
|
|
|
m_LegacyNetclassesLoaded( false )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
++PCBNew
* Removed Pcb_Frame argument from BOARD() constructor, since it precludes
having a BOARD being edited by more than one editor, it was a bad design.
And this meant removing m_PcbFrame from BOARD.
* removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame
* Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp
* added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance
* a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed,
such as dialog_mask_clearance, dialog_drc, etc.
* Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it
with build_version.h's #define BOARD_FILE_VERSION, although there may be a
better place for this constant.
* Made the public functions in PARAM_CFG_ARRAY be type const.
void SaveParam(..) const and void ReadParam(..) const
* PARAM_CFG_BASE now has virtual destructor since we have various way of
destroying the derived class and boost::ptr_vector must be told about this.
* Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use
an automatic PARAM_CFG_ARRAY which is on the stack.\
* PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array,
since it has to access the current BOARD and the BOARD can change.
Remember BOARD_DESIGN_SETTINGS are now in the BOARD.
* Made the m_BoundingBox member private, this was a brutally hard task,
and indicative of the lack of commitment to accessors and object oriented
design on the part of KiCad developers. We must do better.
Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox().
* Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
|
|
|
// we have not loaded a board yet, assume latest until then.
|
2012-04-07 18:05:56 +00:00
|
|
|
m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION;
|
++PCBNew
* Removed Pcb_Frame argument from BOARD() constructor, since it precludes
having a BOARD being edited by more than one editor, it was a bad design.
And this meant removing m_PcbFrame from BOARD.
* removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame
* Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp
* added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance
* a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed,
such as dialog_mask_clearance, dialog_drc, etc.
* Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it
with build_version.h's #define BOARD_FILE_VERSION, although there may be a
better place for this constant.
* Made the public functions in PARAM_CFG_ARRAY be type const.
void SaveParam(..) const and void ReadParam(..) const
* PARAM_CFG_BASE now has virtual destructor since we have various way of
destroying the derived class and boost::ptr_vector must be told about this.
* Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use
an automatic PARAM_CFG_ARRAY which is on the stack.\
* PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array,
since it has to access the current BOARD and the BOARD can change.
Remember BOARD_DESIGN_SETTINGS are now in the BOARD.
* Made the m_BoundingBox member private, this was a brutally hard task,
and indicative of the lack of commitment to accessors and object oriented
design on the part of KiCad developers. We must do better.
Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox().
* Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
|
2008-03-04 04:22:27 +00:00
|
|
|
{
|
2014-06-29 13:05:51 +00:00
|
|
|
m_Layer[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) );
|
2012-06-16 22:49:24 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
if( IsCopperLayer( layer ) )
|
|
|
|
m_Layer[layer].m_type = LT_SIGNAL;
|
2012-06-16 22:49:24 +00:00
|
|
|
else
|
2014-06-24 16:17:18 +00:00
|
|
|
m_Layer[layer].m_type = LT_UNDEFINED;
|
2008-03-04 04:22:27 +00:00
|
|
|
}
|
2009-09-10 15:22:26 +00:00
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
|
|
|
|
|
2015-03-13 16:48:42 +00:00
|
|
|
// Initialize default netclass.
|
2020-05-31 21:42:04 +00:00
|
|
|
NETCLASS* defaultClass = bds.GetDefault();
|
2014-05-13 09:22:51 +00:00
|
|
|
defaultClass->SetDescription( _( "This is the default net class." ) );
|
2013-11-25 15:50:03 +00:00
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
bds.UseCustomTrackViaSize( false );
|
2014-05-13 09:22:51 +00:00
|
|
|
|
2014-03-03 16:15:41 +00:00
|
|
|
// Initialize ratsnest
|
2017-03-22 13:43:10 +00:00
|
|
|
m_connectivity.reset( new CONNECTIVITY_DATA() );
|
2020-05-31 21:42:04 +00:00
|
|
|
|
|
|
|
// Set flag bits on these that will only be cleared if these are loaded from a legacy file
|
|
|
|
m_LegacyVisibleLayers.reset().set( Rescue );
|
|
|
|
m_LegacyVisibleItems.reset().set( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) );
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-04 20:05:54 +00:00
|
|
|
|
2007-08-30 22:20:52 +00:00
|
|
|
BOARD::~BOARD()
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2019-08-11 20:30:33 +00:00
|
|
|
// Clean up the owned elements
|
2007-11-27 22:49:35 +00:00
|
|
|
DeleteMARKERs();
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : m_zones )
|
|
|
|
delete zone;
|
|
|
|
|
|
|
|
m_zones.clear();
|
|
|
|
|
|
|
|
for( MODULE* m : m_modules )
|
2019-08-11 20:30:33 +00:00
|
|
|
delete m;
|
|
|
|
|
|
|
|
m_modules.clear();
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( TRACK* t : m_tracks )
|
2019-08-11 20:30:33 +00:00
|
|
|
delete t;
|
|
|
|
|
|
|
|
m_tracks.clear();
|
|
|
|
|
2020-09-16 01:03:27 +00:00
|
|
|
for( BOARD_ITEM* d : m_drawings )
|
2019-08-11 20:30:33 +00:00
|
|
|
delete d;
|
|
|
|
|
|
|
|
m_drawings.clear();
|
|
|
|
|
2020-09-16 01:03:27 +00:00
|
|
|
for( PCB_GROUP* g : m_groups )
|
|
|
|
delete g;
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
m_groups.clear();
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2017-08-17 15:55:05 +00:00
|
|
|
void BOARD::BuildConnectivity()
|
|
|
|
{
|
|
|
|
GetConnectivity()->Build( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
void BOARD::SetProject( PROJECT* aProject )
|
|
|
|
{
|
|
|
|
m_project = aProject;
|
|
|
|
|
|
|
|
if( aProject )
|
|
|
|
{
|
|
|
|
PROJECT_FILE& project = aProject->GetProjectFile();
|
|
|
|
|
|
|
|
// Link the design settings object to the project file
|
2020-07-03 22:30:23 +00:00
|
|
|
project.m_BoardSettings = &GetDesignSettings();
|
2020-05-31 21:42:04 +00:00
|
|
|
|
|
|
|
// Set parent, which also will load the values from JSON stored in the project
|
|
|
|
project.m_BoardSettings->SetParent( &project );
|
|
|
|
|
2020-07-03 22:30:23 +00:00
|
|
|
// The DesignSettings' netclasses pointer will be pointing to its internal netclasses
|
|
|
|
// list at this point. If we loaded anything into it from a legacy board file then we
|
|
|
|
// want to transfer it over to the project netclasses list.
|
2020-05-31 21:42:04 +00:00
|
|
|
if( m_LegacyNetclassesLoaded )
|
2020-07-03 22:30:23 +00:00
|
|
|
project.NetSettings().m_NetClasses = GetDesignSettings().GetNetClasses();
|
2020-05-31 21:42:04 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
// Now update the DesignSettings' netclass pointer to point into the project.
|
2020-05-31 21:42:04 +00:00
|
|
|
GetDesignSettings().SetNetClasses( &project.NetSettings().m_NetClasses );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::ClearProject()
|
|
|
|
{
|
|
|
|
if( !m_project )
|
|
|
|
return;
|
|
|
|
|
|
|
|
PROJECT_FILE& project = m_project->GetProjectFile();
|
|
|
|
|
|
|
|
// Owned by the BOARD
|
|
|
|
if( project.m_BoardSettings )
|
|
|
|
{
|
|
|
|
project.ReleaseNestedSettings( project.m_BoardSettings );
|
|
|
|
project.m_BoardSettings = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetDesignSettings().SetParent( nullptr );
|
|
|
|
m_project = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-29 11:33:44 +00:00
|
|
|
std::vector<MARKER_PCB*> BOARD::ResolveDRCExclusions()
|
|
|
|
{
|
|
|
|
for( MARKER_PCB* marker : GetBoard()->Markers() )
|
|
|
|
{
|
|
|
|
auto i = m_designSettings->m_DrcExclusions.find( marker->Serialize() );
|
|
|
|
|
|
|
|
if( i != m_designSettings->m_DrcExclusions.end() )
|
|
|
|
{
|
|
|
|
marker->SetExcluded( true );
|
|
|
|
m_designSettings->m_DrcExclusions.erase( i );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<MARKER_PCB*> markers;
|
|
|
|
|
|
|
|
for( const wxString& exclusionData : m_designSettings->m_DrcExclusions )
|
|
|
|
{
|
|
|
|
MARKER_PCB* marker = MARKER_PCB::Deserialize( exclusionData );
|
|
|
|
|
|
|
|
if( marker )
|
|
|
|
{
|
|
|
|
marker->SetExcluded( true );
|
|
|
|
markers.push_back( marker );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_designSettings->m_DrcExclusions.clear();
|
|
|
|
|
|
|
|
return markers;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-19 18:21:24 +00:00
|
|
|
bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const
|
|
|
|
{
|
|
|
|
if( m_properties.count( *token ) )
|
|
|
|
{
|
|
|
|
*token = m_properties.at( *token );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-02 17:52:19 +00:00
|
|
|
wxPoint BOARD::GetPosition() const
|
2012-06-01 07:39:32 +00:00
|
|
|
{
|
2013-08-29 10:06:06 +00:00
|
|
|
return ZeroOffset;
|
|
|
|
}
|
2012-06-01 07:39:32 +00:00
|
|
|
|
2017-12-17 17:43:43 +00:00
|
|
|
|
2013-08-29 10:06:06 +00:00
|
|
|
void BOARD::SetPosition( const wxPoint& aPos )
|
|
|
|
{
|
|
|
|
wxLogWarning( wxT( "This should not be called on the BOARD object") );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::Move( const wxPoint& aMoveVector ) // overload
|
|
|
|
{
|
2013-11-02 00:24:38 +00:00
|
|
|
// @todo : anything like this elsewhere? maybe put into GENERAL_COLLECTOR class.
|
|
|
|
static const KICAD_T top_level_board_stuff[] = {
|
|
|
|
PCB_MARKER_T,
|
|
|
|
PCB_TEXT_T,
|
2020-10-04 14:19:33 +00:00
|
|
|
PCB_SHAPE_T,
|
2020-09-12 20:09:40 +00:00
|
|
|
PCB_DIM_ALIGNED_T,
|
|
|
|
PCB_DIM_LEADER_T,
|
2013-11-02 00:24:38 +00:00
|
|
|
PCB_TARGET_T,
|
|
|
|
PCB_VIA_T,
|
|
|
|
PCB_TRACE_T,
|
2019-05-17 00:13:21 +00:00
|
|
|
PCB_ARC_T,
|
2014-09-10 15:18:42 +00:00
|
|
|
// PCB_PAD_T, Can't be at board level
|
2020-10-04 14:19:33 +00:00
|
|
|
// PCB_FP_TEXT_T, Can't be at board level
|
2013-11-02 00:24:38 +00:00
|
|
|
PCB_MODULE_T,
|
2013-11-02 19:49:46 +00:00
|
|
|
PCB_ZONE_AREA_T,
|
2013-11-02 00:24:38 +00:00
|
|
|
EOT
|
|
|
|
};
|
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
|
|
|
|
{
|
|
|
|
BOARD_ITEM* brd_item = (BOARD_ITEM*) item;
|
|
|
|
|
|
|
|
// aMoveVector was snapshotted, don't need "data".
|
|
|
|
brd_item->Move( aMoveVector );
|
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
return SEARCH_RESULT::CONTINUE;
|
2016-07-12 19:05:54 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Visit( inspector, NULL, top_level_board_stuff );
|
2012-06-01 07:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-19 20:34:09 +00:00
|
|
|
TRACKS BOARD::TracksInNet( int aNetCode )
|
|
|
|
{
|
|
|
|
TRACKS ret;
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
INSPECTOR_FUNC inspector = [aNetCode, &ret]( EDA_ITEM* item, void* testData )
|
|
|
|
{
|
|
|
|
TRACK* t = (TRACK*) item;
|
2016-07-19 20:34:09 +00:00
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
if( t->GetNetCode() == aNetCode )
|
|
|
|
ret.push_back( t );
|
2016-07-19 20:34:09 +00:00
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
return SEARCH_RESULT::CONTINUE;
|
|
|
|
};
|
2016-07-19 20:34:09 +00:00
|
|
|
|
|
|
|
// visit this BOARD's TRACKs and VIAs with above TRACK INSPECTOR which
|
|
|
|
// appends all in aNetCode to ret.
|
2020-08-26 23:52:12 +00:00
|
|
|
Visit( inspector, NULL, GENERAL_COLLECTOR::Tracks );
|
2016-07-19 20:34:09 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
bool BOARD::SetLayerDescr( PCB_LAYER_ID aIndex, const LAYER& aLayer )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2019-01-06 16:43:12 +00:00
|
|
|
if( unsigned( aIndex ) < arrayDim( m_Layer ) )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
|
|
|
m_Layer[ aIndex ] = aLayer;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-15 13:31:47 +00:00
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
const PCB_LAYER_ID BOARD::GetLayerID( const wxString& aLayerName ) const
|
2015-02-15 13:31:47 +00:00
|
|
|
{
|
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
// Check the BOARD physical layer names.
|
2017-03-13 03:19:33 +00:00
|
|
|
for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
|
2015-02-15 13:31:47 +00:00
|
|
|
{
|
2020-09-22 21:50:59 +00:00
|
|
|
if ( ( m_Layer[ layer ].m_name == aLayerName )
|
|
|
|
|| ( m_Layer[ layer ].m_userName == aLayerName ) )
|
2015-02-15 13:31:47 +00:00
|
|
|
return ToLAYER_ID( layer );
|
|
|
|
}
|
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
// Otherwise fall back to the system standard layer names for virtual layers.
|
2017-03-13 03:19:33 +00:00
|
|
|
for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
|
2015-02-15 13:31:47 +00:00
|
|
|
{
|
2015-06-04 12:54:07 +00:00
|
|
|
if( GetStandardLayerName( ToLAYER_ID( layer ) ) == aLayerName )
|
2015-02-15 13:31:47 +00:00
|
|
|
return ToLAYER_ID( layer );
|
|
|
|
}
|
|
|
|
|
|
|
|
return UNDEFINED_LAYER;
|
|
|
|
}
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
|
2008-01-23 01:50:16 +00:00
|
|
|
{
|
2012-06-16 22:49:24 +00:00
|
|
|
// All layer names are stored in the BOARD.
|
2013-04-09 16:00:46 +00:00
|
|
|
if( IsLayerEnabled( aLayer ) )
|
2008-03-04 04:22:27 +00:00
|
|
|
{
|
2020-09-22 21:50:59 +00:00
|
|
|
// Standard names were set in BOARD::BOARD() but they may be over-ridden by
|
|
|
|
// BOARD::SetLayerName(). For copper layers, return the user defined layer name,
|
|
|
|
// if it was set. Otherwise return the Standard English layer name.
|
|
|
|
if( !m_Layer[aLayer].m_userName.IsEmpty() )
|
|
|
|
return m_Layer[aLayer].m_userName;
|
2008-03-04 04:22:27 +00:00
|
|
|
}
|
|
|
|
|
2013-04-09 16:00:46 +00:00
|
|
|
return GetStandardLayerName( aLayer );
|
2009-12-07 03:46:13 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
bool BOARD::SetLayerName( PCB_LAYER_ID aLayer, const wxString& aLayerName )
|
2008-03-01 13:15:41 +00:00
|
|
|
{
|
2020-09-22 21:50:59 +00:00
|
|
|
wxCHECK( !aLayerName.IsEmpty(), false );
|
2009-10-18 23:22:26 +00:00
|
|
|
|
|
|
|
// no quote chars in the name allowed
|
|
|
|
if( aLayerName.Find( wxChar( '"' ) ) != wxNOT_FOUND )
|
|
|
|
return false;
|
2008-03-04 04:22:27 +00:00
|
|
|
|
2013-04-09 16:00:46 +00:00
|
|
|
if( IsLayerEnabled( aLayer ) )
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-09-22 21:50:59 +00:00
|
|
|
m_Layer[aLayer].m_userName = aLayerName;
|
2008-03-04 04:22:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2008-03-01 13:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
LAYER_T BOARD::GetLayerType( PCB_LAYER_ID aLayer ) const
|
2008-03-01 13:15:41 +00:00
|
|
|
{
|
2013-04-09 16:00:46 +00:00
|
|
|
if( !IsCopperLayer( aLayer ) )
|
2009-10-18 23:22:26 +00:00
|
|
|
return LT_SIGNAL;
|
|
|
|
|
|
|
|
//@@IMB: The original test was broken due to the discontinuity
|
|
|
|
// in the layer sequence.
|
2013-04-09 16:00:46 +00:00
|
|
|
if( IsLayerEnabled( aLayer ) )
|
2014-06-24 16:17:18 +00:00
|
|
|
return m_Layer[aLayer].m_type;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2008-03-01 13:15:41 +00:00
|
|
|
return LT_SIGNAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
bool BOARD::SetLayerType( PCB_LAYER_ID aLayer, LAYER_T aLayerType )
|
2008-03-01 13:15:41 +00:00
|
|
|
{
|
2013-04-09 16:00:46 +00:00
|
|
|
if( !IsCopperLayer( aLayer ) )
|
2009-10-18 23:22:26 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
//@@IMB: The original test was broken due to the discontinuity
|
|
|
|
// in the layer sequence.
|
2013-04-09 16:00:46 +00:00
|
|
|
if( IsLayerEnabled( aLayer ) )
|
2008-03-04 04:22:27 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
m_Layer[aLayer].m_type = aLayerType;
|
2008-03-04 04:22:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2008-03-04 04:22:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char* LAYER::ShowType( LAYER_T aType )
|
|
|
|
{
|
|
|
|
switch( aType )
|
|
|
|
{
|
|
|
|
default:
|
2020-08-26 23:52:12 +00:00
|
|
|
case LT_SIGNAL: return "signal";
|
|
|
|
case LT_POWER: return "power";
|
|
|
|
case LT_MIXED: return "mixed";
|
|
|
|
case LT_JUMPER: return "jumper";
|
2008-03-04 04:22:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LAYER_T LAYER::ParseType( const char* aType )
|
|
|
|
{
|
|
|
|
if( strcmp( aType, "signal" ) == 0 )
|
|
|
|
return LT_SIGNAL;
|
|
|
|
else if( strcmp( aType, "power" ) == 0 )
|
|
|
|
return LT_POWER;
|
|
|
|
else if( strcmp( aType, "mixed" ) == 0 )
|
|
|
|
return LT_MIXED;
|
|
|
|
else if( strcmp( aType, "jumper" ) == 0 )
|
|
|
|
return LT_JUMPER;
|
|
|
|
else
|
2012-06-09 17:00:13 +00:00
|
|
|
return LT_UNDEFINED;
|
2008-03-01 13:15:41 +00:00
|
|
|
}
|
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2008-01-23 01:50:16 +00:00
|
|
|
int BOARD::GetCopperLayerCount() const
|
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
return GetDesignSettings().GetCopperLayerCount();
|
2008-01-23 01:50:16 +00:00
|
|
|
}
|
2010-01-31 20:01:46 +00:00
|
|
|
|
2012-06-09 17:00:13 +00:00
|
|
|
|
2010-01-21 07:41:30 +00:00
|
|
|
void BOARD::SetCopperLayerCount( int aCount )
|
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
GetDesignSettings().SetCopperLayerCount( aCount );
|
2010-01-21 07:41:30 +00:00
|
|
|
}
|
2008-01-23 01:50:16 +00:00
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
LSET BOARD::GetEnabledLayers() const
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
return GetDesignSettings().GetEnabledLayers();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BOARD::IsLayerVisible( PCB_LAYER_ID aLayer ) const
|
|
|
|
{
|
|
|
|
// If there is no project, assume layer is visible always
|
|
|
|
return GetDesignSettings().IsLayerEnabled( aLayer )
|
|
|
|
&& ( !m_project || m_project->GetLocalSettings().m_VisibleLayers[aLayer] );
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
LSET BOARD::GetVisibleLayers() const
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
return m_project ? m_project->GetLocalSettings().m_VisibleLayers : LSET::AllLayersMask();
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
void BOARD::SetEnabledLayers( LSET aLayerSet )
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
GetDesignSettings().SetEnabledLayers( aLayerSet );
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
void BOARD::SetVisibleLayers( LSET aLayerSet )
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
if( m_project )
|
|
|
|
m_project->GetLocalSettings().m_VisibleLayers = aLayerSet;
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
void BOARD::SetVisibleElements( const GAL_SET& aSet )
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2012-03-12 00:40:48 +00:00
|
|
|
// Call SetElementVisibility for each item
|
|
|
|
// to ensure specific calculations that can be needed by some items,
|
|
|
|
// just changing the visibility flags could be not sufficient.
|
2020-05-31 21:42:04 +00:00
|
|
|
for( size_t i = 0; i < aSet.size(); i++ )
|
|
|
|
SetElementVisibility( GAL_LAYER_ID_START + static_cast<int>( i ), aSet[i] );
|
2010-01-30 14:46:26 +00:00
|
|
|
}
|
|
|
|
|
2012-02-06 05:44:19 +00:00
|
|
|
|
|
|
|
void BOARD::SetVisibleAlls()
|
2010-01-30 14:46:26 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
SetVisibleLayers( LSET().set() );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2012-02-06 05:44:19 +00:00
|
|
|
// Call SetElementVisibility for each item,
|
|
|
|
// to ensure specific calculations that can be needed by some items
|
2017-03-13 03:19:33 +00:00
|
|
|
for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
|
2010-01-30 14:46:26 +00:00
|
|
|
SetElementVisibility( ii, true );
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 15:43:38 +00:00
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
GAL_SET BOARD::GetVisibleElements() const
|
2009-10-18 23:22:26 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
return m_project ? m_project->GetLocalSettings().m_VisibleItems : GAL_SET().set();
|
2009-10-18 23:22:26 +00:00
|
|
|
}
|
|
|
|
|
2010-01-21 20:53:01 +00:00
|
|
|
|
2017-10-05 18:11:55 +00:00
|
|
|
bool BOARD::IsElementVisible( GAL_LAYER_ID aLayer ) const
|
2010-01-25 06:45:34 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
return !m_project || m_project->GetLocalSettings().m_VisibleItems[aLayer - GAL_LAYER_ID_START];
|
2010-01-25 06:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-05 18:11:55 +00:00
|
|
|
void BOARD::SetElementVisibility( GAL_LAYER_ID aLayer, bool isEnabled )
|
2010-01-25 06:45:34 +00:00
|
|
|
{
|
2020-05-31 21:42:04 +00:00
|
|
|
if( m_project )
|
|
|
|
m_project->GetLocalSettings().m_VisibleItems.set( aLayer - GAL_LAYER_ID_START, isEnabled );
|
2012-02-06 05:44:19 +00:00
|
|
|
|
2017-10-05 18:11:55 +00:00
|
|
|
switch( aLayer )
|
2010-01-25 06:45:34 +00:00
|
|
|
{
|
2017-03-13 03:19:33 +00:00
|
|
|
case LAYER_RATSNEST:
|
2017-06-23 17:22:44 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
// because we have a tool to show/hide ratsnest relative to a pad or a footprint
|
2010-01-30 14:46:26 +00:00
|
|
|
// so the hide/show option is a per item selection
|
2017-04-24 20:26:11 +00:00
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( TRACK* track : Tracks() )
|
2017-04-24 20:26:11 +00:00
|
|
|
track->SetLocalRatsnestVisible( isEnabled );
|
2017-06-23 17:22:44 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2017-06-23 17:22:44 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2017-04-24 20:26:11 +00:00
|
|
|
pad->SetLocalRatsnestVisible( isEnabled );
|
2017-06-23 17:22:44 +00:00
|
|
|
}
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
2017-04-24 20:26:11 +00:00
|
|
|
zone->SetLocalRatsnestVisible( isEnabled );
|
2010-01-30 14:46:26 +00:00
|
|
|
|
2017-04-24 20:26:11 +00:00
|
|
|
break;
|
2017-06-23 17:22:44 +00:00
|
|
|
}
|
|
|
|
|
2010-01-25 06:45:34 +00:00
|
|
|
default:
|
2012-02-06 05:44:19 +00:00
|
|
|
;
|
2010-01-25 06:45:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-05 18:11:55 +00:00
|
|
|
bool BOARD::IsModuleLayerVisible( PCB_LAYER_ID aLayer )
|
2010-01-31 20:01:46 +00:00
|
|
|
{
|
2017-10-05 18:11:55 +00:00
|
|
|
switch( aLayer )
|
2013-04-07 11:55:18 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
case F_Cu:
|
2017-03-13 03:19:33 +00:00
|
|
|
return IsElementVisible( LAYER_MOD_FR );
|
2013-04-07 11:55:18 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
case B_Cu:
|
2017-03-13 03:19:33 +00:00
|
|
|
return IsElementVisible( LAYER_MOD_BK );
|
2013-04-07 11:55:18 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
wxFAIL_MSG( wxT( "BOARD::IsModuleLayerVisible() param error: bad layer" ) );
|
2010-01-31 20:01:46 +00:00
|
|
|
return true;
|
2013-04-07 11:55:18 +00:00
|
|
|
}
|
2010-01-31 20:01:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-13 15:31:54 +00:00
|
|
|
void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
|
2007-11-27 22:49:35 +00:00
|
|
|
{
|
2009-05-28 08:42:24 +00:00
|
|
|
if( aBoardItem == NULL )
|
2008-02-12 01:02:53 +00:00
|
|
|
{
|
2009-05-28 08:42:24 +00:00
|
|
|
wxFAIL_MSG( wxT( "BOARD::Add() param error: aBoardItem NULL" ) );
|
2008-02-12 01:02:53 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
switch( aBoardItem->Type() )
|
|
|
|
{
|
2016-01-29 10:29:56 +00:00
|
|
|
case PCB_NETINFO_T:
|
|
|
|
m_NetInfo.AppendNet( (NETINFO_ITEM*) aBoardItem );
|
2016-09-05 10:19:30 +00:00
|
|
|
break;
|
2016-01-29 10:29:56 +00:00
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
// this one uses a vector
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MARKER_T:
|
2009-08-01 19:26:05 +00:00
|
|
|
m_markers.push_back( (MARKER_PCB*) aBoardItem );
|
2007-11-27 22:49:35 +00:00
|
|
|
break;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
// this one uses a vector
|
|
|
|
case PCB_GROUP_T:
|
2020-08-12 11:23:30 +00:00
|
|
|
m_groups.push_back( (PCB_GROUP*) aBoardItem );
|
2020-08-11 19:37:07 +00:00
|
|
|
break;
|
|
|
|
|
2008-01-06 17:19:36 +00:00
|
|
|
// this one uses a vector
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_ZONE_AREA_T:
|
2020-08-26 23:52:12 +00:00
|
|
|
m_zones.push_back( (ZONE_CONTAINER*) aBoardItem );
|
2008-01-06 17:19:36 +00:00
|
|
|
break;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TRACE_T:
|
|
|
|
case PCB_VIA_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
case PCB_ARC_T:
|
2019-06-29 02:23:00 +00:00
|
|
|
|
|
|
|
// N.B. This inserts a small memory leak as we lose the
|
|
|
|
if( !IsCopperLayer( aBoardItem->GetLayer() ) )
|
|
|
|
{
|
|
|
|
wxFAIL_MSG( wxT( "BOARD::Add() Cannot place Track on non-copper layer" ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2019-05-31 02:30:28 +00:00
|
|
|
m_tracks.push_back( static_cast<TRACK*>( aBoardItem ) );
|
2015-06-18 19:23:01 +00:00
|
|
|
else
|
2019-05-31 02:30:28 +00:00
|
|
|
m_tracks.push_front( static_cast<TRACK*>( aBoardItem ) );
|
2015-06-18 19:23:01 +00:00
|
|
|
|
2011-08-09 14:13:01 +00:00
|
|
|
break;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MODULE_T:
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2019-05-31 00:15:57 +00:00
|
|
|
m_modules.push_back( (MODULE*) aBoardItem );
|
2008-12-04 04:28:11 +00:00
|
|
|
else
|
2019-05-31 00:15:57 +00:00
|
|
|
m_modules.push_front( (MODULE*) aBoardItem );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
break;
|
|
|
|
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_ALIGNED_T:
|
2020-09-17 00:54:58 +00:00
|
|
|
case PCB_DIM_CENTER_T:
|
|
|
|
case PCB_DIM_ORTHOGONAL_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_LEADER_T:
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_SHAPE_T:
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TEXT_T:
|
2011-09-07 19:41:04 +00:00
|
|
|
case PCB_TARGET_T:
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2019-05-30 13:33:59 +00:00
|
|
|
m_drawings.push_back( aBoardItem );
|
2008-12-04 04:28:11 +00:00
|
|
|
else
|
2019-05-30 13:33:59 +00:00
|
|
|
m_drawings.push_front( aBoardItem );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2008-12-04 04:28:11 +00:00
|
|
|
break;
|
|
|
|
|
2009-05-28 08:42:24 +00:00
|
|
|
// other types may use linked list
|
2007-11-27 22:49:35 +00:00
|
|
|
default:
|
2011-08-09 14:13:01 +00:00
|
|
|
{
|
|
|
|
wxString msg;
|
2011-09-07 19:41:04 +00:00
|
|
|
msg.Printf( wxT( "BOARD::Add() needs work: BOARD_ITEM type (%d) not handled" ),
|
|
|
|
aBoardItem->Type() );
|
2011-08-09 14:13:01 +00:00
|
|
|
wxFAIL_MSG( msg );
|
2016-05-30 08:44:05 +00:00
|
|
|
return;
|
2011-08-09 14:13:01 +00:00
|
|
|
}
|
|
|
|
break;
|
2007-11-27 22:49:35 +00:00
|
|
|
}
|
2014-01-30 14:46:39 +00:00
|
|
|
|
2016-05-30 08:44:05 +00:00
|
|
|
aBoardItem->SetParent( this );
|
2019-06-15 19:42:17 +00:00
|
|
|
aBoardItem->ClearEditFlags();
|
2017-03-22 13:43:10 +00:00
|
|
|
m_connectivity->Add( aBoardItem );
|
2020-04-12 19:29:16 +00:00
|
|
|
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardItemAdded, *this, aBoardItem );
|
2007-11-27 22:49:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-13 15:31:54 +00:00
|
|
|
void BOARD::Remove( BOARD_ITEM* aBoardItem )
|
2007-11-27 22:49:35 +00:00
|
|
|
{
|
2011-09-14 20:04:58 +00:00
|
|
|
// find these calls and fix them! Don't send me no stinking' NULL.
|
2008-12-06 21:20:50 +00:00
|
|
|
wxASSERT( aBoardItem );
|
2007-12-29 19:15:58 +00:00
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
switch( aBoardItem->Type() )
|
|
|
|
{
|
2016-01-29 10:29:56 +00:00
|
|
|
case PCB_NETINFO_T:
|
2016-01-29 14:43:40 +00:00
|
|
|
{
|
|
|
|
NETINFO_ITEM* item = (NETINFO_ITEM*) aBoardItem;
|
|
|
|
m_NetInfo.RemoveNet( item );
|
2016-01-29 10:29:56 +00:00
|
|
|
break;
|
2016-01-29 14:43:40 +00:00
|
|
|
}
|
2016-05-13 15:31:54 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MARKER_T:
|
2020-08-26 23:52:12 +00:00
|
|
|
m_markers.erase( std::remove_if( m_markers.begin(), m_markers.end(),
|
|
|
|
[aBoardItem]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2007-11-27 22:49:35 +00:00
|
|
|
break;
|
2007-12-29 19:15:58 +00:00
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
m_groups.erase( std::remove_if( m_groups.begin(), m_groups.end(),
|
2020-08-26 23:52:12 +00:00
|
|
|
[aBoardItem]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2020-08-11 19:37:07 +00:00
|
|
|
break;
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
case PCB_ZONE_AREA_T:
|
|
|
|
m_zones.erase( std::remove_if( m_zones.begin(), m_zones.end(),
|
|
|
|
[aBoardItem]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2007-12-29 19:15:58 +00:00
|
|
|
break;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MODULE_T:
|
2019-05-31 00:15:57 +00:00
|
|
|
m_modules.erase( std::remove_if( m_modules.begin(), m_modules.end(),
|
2020-02-28 00:05:40 +00:00
|
|
|
[aBoardItem]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2008-12-06 08:21:54 +00:00
|
|
|
break;
|
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
case PCB_ARC_T:
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_VIA_T:
|
2019-05-31 02:30:28 +00:00
|
|
|
m_tracks.erase( std::remove_if( m_tracks.begin(), m_tracks.end(),
|
2020-02-28 00:05:40 +00:00
|
|
|
[aBoardItem]( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2008-12-06 08:21:54 +00:00
|
|
|
break;
|
|
|
|
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_ALIGNED_T:
|
2020-09-17 00:54:58 +00:00
|
|
|
case PCB_DIM_CENTER_T:
|
|
|
|
case PCB_DIM_ORTHOGONAL_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_LEADER_T:
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_SHAPE_T:
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TEXT_T:
|
2011-09-07 19:41:04 +00:00
|
|
|
case PCB_TARGET_T:
|
2020-02-28 00:05:40 +00:00
|
|
|
m_drawings.erase( std::remove_if( m_drawings.begin(), m_drawings.end(),
|
|
|
|
[aBoardItem](BOARD_ITEM* aItem)
|
|
|
|
{
|
|
|
|
return aItem == aBoardItem;
|
|
|
|
} ) );
|
2008-12-06 08:21:54 +00:00
|
|
|
break;
|
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
// other types may use linked list
|
|
|
|
default:
|
2009-05-28 08:42:24 +00:00
|
|
|
wxFAIL_MSG( wxT( "BOARD::Remove() needs more ::Type() support" ) );
|
2007-11-27 22:49:35 +00:00
|
|
|
}
|
|
|
|
|
2017-03-22 13:43:10 +00:00
|
|
|
m_connectivity->Remove( aBoardItem );
|
2020-04-12 19:29:16 +00:00
|
|
|
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aBoardItem );
|
2007-12-03 05:14:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-20 14:11:39 +00:00
|
|
|
wxString BOARD::GetSelectMenuText( EDA_UNITS aUnits ) const
|
2018-11-28 12:11:43 +00:00
|
|
|
{
|
|
|
|
return wxString::Format( _( "PCB" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
void BOARD::DeleteMARKERs()
|
|
|
|
{
|
2009-08-01 19:26:05 +00:00
|
|
|
// the vector does not know how to delete the MARKER_PCB, it holds pointers
|
2018-09-17 09:54:49 +00:00
|
|
|
for( MARKER_PCB* marker : m_markers )
|
|
|
|
delete marker;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
m_markers.clear();
|
|
|
|
}
|
|
|
|
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2020-08-04 00:41:56 +00:00
|
|
|
void BOARD::DeleteMARKERs( bool aWarningsAndErrors, bool aExclusions )
|
|
|
|
{
|
|
|
|
// Deleting lots of items from a vector can be very slow. Copy remaining items instead.
|
|
|
|
MARKERS remaining;
|
|
|
|
|
|
|
|
for( MARKER_PCB* marker : m_markers )
|
|
|
|
{
|
|
|
|
if( ( marker->IsExcluded() && aExclusions )
|
|
|
|
|| ( !marker->IsExcluded() && aWarningsAndErrors ) )
|
|
|
|
{
|
|
|
|
delete marker;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
remaining.push_back( marker );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_markers = remaining;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-25 17:37:03 +00:00
|
|
|
BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
|
2018-01-28 09:35:33 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( aID == niluuid )
|
|
|
|
return nullptr;
|
|
|
|
|
2018-01-28 09:35:33 +00:00
|
|
|
for( TRACK* track : Tracks() )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( track->m_Uuid == aID )
|
2018-01-28 09:35:33 +00:00
|
|
|
return track;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2018-01-28 09:35:33 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->m_Uuid == aID )
|
|
|
|
return footprint;
|
2018-01-28 09:35:33 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( pad->m_Uuid == aID )
|
2018-01-28 09:35:33 +00:00
|
|
|
return pad;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->Reference().m_Uuid == aID )
|
|
|
|
return &footprint->Reference();
|
2018-08-23 22:41:57 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->Value().m_Uuid == aID )
|
|
|
|
return &footprint->Value();
|
2018-08-23 22:41:57 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( drawing->m_Uuid == aID )
|
2018-08-23 22:41:57 +00:00
|
|
|
return drawing;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2020-10-03 11:16:29 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* zone : footprint->Zones() )
|
2020-10-03 11:16:29 +00:00
|
|
|
{
|
|
|
|
if( zone->m_Uuid == aID )
|
|
|
|
return zone;
|
|
|
|
}
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( PCB_GROUP* group : footprint->Groups() )
|
2020-10-03 11:16:29 +00:00
|
|
|
{
|
|
|
|
if( group->m_Uuid == aID )
|
|
|
|
return group;
|
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( zone->m_Uuid == aID )
|
2018-01-28 09:35:33 +00:00
|
|
|
return zone;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
|
2018-08-23 22:41:57 +00:00
|
|
|
for( BOARD_ITEM* drawing : Drawings() )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( drawing->m_Uuid == aID )
|
2018-08-23 22:41:57 +00:00
|
|
|
return drawing;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2018-01-28 09:35:33 +00:00
|
|
|
|
2020-02-24 17:23:53 +00:00
|
|
|
for( MARKER_PCB* marker : m_markers )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-02-24 17:23:53 +00:00
|
|
|
if( marker->m_Uuid == aID )
|
|
|
|
return marker;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2020-02-24 17:23:53 +00:00
|
|
|
|
2020-08-12 11:23:30 +00:00
|
|
|
for( PCB_GROUP* group : m_groups )
|
2020-08-26 23:52:12 +00:00
|
|
|
{
|
2020-08-11 19:37:07 +00:00
|
|
|
if( group->m_Uuid == aID )
|
|
|
|
return group;
|
2020-08-26 23:52:12 +00:00
|
|
|
}
|
2020-08-11 19:37:07 +00:00
|
|
|
|
2020-05-18 00:20:16 +00:00
|
|
|
if( m_Uuid == aID )
|
2020-09-25 17:37:03 +00:00
|
|
|
return const_cast<BOARD*>( this );
|
2020-05-18 00:20:16 +00:00
|
|
|
|
2018-01-28 09:35:33 +00:00
|
|
|
// Not found; weak reference has been deleted.
|
2020-07-06 23:55:38 +00:00
|
|
|
return DELETED_BOARD_ITEM::GetInstance();
|
2018-01-28 09:35:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-24 13:36:10 +00:00
|
|
|
void BOARD::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
|
|
|
|
{
|
2020-09-17 23:35:19 +00:00
|
|
|
// the board itself
|
|
|
|
aMap[ this->m_Uuid ] = this;
|
|
|
|
|
2020-04-24 13:36:10 +00:00
|
|
|
for( TRACK* track : Tracks() )
|
|
|
|
aMap[ track->m_Uuid ] = track;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2020-04-24 13:36:10 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
aMap[ footprint->m_Uuid ] = footprint;
|
2020-04-24 13:36:10 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2020-04-24 13:36:10 +00:00
|
|
|
aMap[ pad->m_Uuid ] = pad;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
aMap[ footprint->Reference().m_Uuid ] = &footprint->Reference();
|
|
|
|
aMap[ footprint->Value().m_Uuid ] = &footprint->Value();
|
2020-04-24 13:36:10 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
|
2020-04-24 13:36:10 +00:00
|
|
|
aMap[ drawing->m_Uuid ] = drawing;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
|
|
|
aMap[ zone->m_Uuid ] = zone;
|
|
|
|
|
|
|
|
for( BOARD_ITEM* drawing : Drawings() )
|
|
|
|
aMap[ drawing->m_Uuid ] = drawing;
|
|
|
|
|
|
|
|
for( MARKER_PCB* marker : m_markers )
|
|
|
|
aMap[ marker->m_Uuid ] = marker;
|
2020-08-11 19:37:07 +00:00
|
|
|
|
2020-08-12 11:23:30 +00:00
|
|
|
for( PCB_GROUP* group : m_groups )
|
2020-08-11 19:37:07 +00:00
|
|
|
aMap[ group->m_Uuid ] = group;
|
2020-04-24 13:36:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-19 19:41:54 +00:00
|
|
|
wxString BOARD::ConvertCrossReferencesToKIIDs( const wxString& aSource )
|
|
|
|
{
|
|
|
|
wxString newbuf;
|
|
|
|
size_t sourceLen = aSource.length();
|
|
|
|
|
|
|
|
for( size_t i = 0; i < sourceLen; ++i )
|
|
|
|
{
|
|
|
|
if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
|
|
|
|
{
|
|
|
|
wxString token;
|
|
|
|
bool isCrossRef = false;
|
|
|
|
|
|
|
|
for( i = i + 2; i < sourceLen; ++i )
|
|
|
|
{
|
|
|
|
if( aSource[i] == '}' )
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( aSource[i] == ':' )
|
|
|
|
isCrossRef = true;
|
|
|
|
|
|
|
|
token.append( aSource[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( isCrossRef )
|
|
|
|
{
|
|
|
|
wxString remainder;
|
|
|
|
wxString ref = token.BeforeFirst( ':', &remainder );
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2020-09-19 19:41:54 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->GetReference().CmpNoCase( ref ) == 0 )
|
2020-09-19 19:41:54 +00:00
|
|
|
{
|
|
|
|
wxString test( remainder );
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->ResolveTextVar( &test ) )
|
|
|
|
token = footprint->m_Uuid.AsString() + ":" + remainder;
|
2020-09-19 19:41:54 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newbuf.append( "${" + token + "}" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newbuf.append( aSource[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString BOARD::ConvertKIIDsToCrossReferences( const wxString& aSource )
|
|
|
|
{
|
|
|
|
wxString newbuf;
|
|
|
|
size_t sourceLen = aSource.length();
|
|
|
|
|
|
|
|
for( size_t i = 0; i < sourceLen; ++i )
|
|
|
|
{
|
|
|
|
if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
|
|
|
|
{
|
|
|
|
wxString token;
|
|
|
|
bool isCrossRef = false;
|
|
|
|
|
|
|
|
for( i = i + 2; i < sourceLen; ++i )
|
|
|
|
{
|
|
|
|
if( aSource[i] == '}' )
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( aSource[i] == ':' )
|
|
|
|
isCrossRef = true;
|
|
|
|
|
|
|
|
token.append( aSource[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( isCrossRef )
|
|
|
|
{
|
|
|
|
wxString remainder;
|
|
|
|
wxString ref = token.BeforeFirst( ':', &remainder );
|
|
|
|
BOARD_ITEM* refItem = GetItem( KIID( ref ) );
|
|
|
|
|
|
|
|
if( refItem && refItem->Type() == PCB_MODULE_T )
|
|
|
|
token = static_cast<MODULE*>( refItem )->GetReference() + ":" + remainder;
|
|
|
|
}
|
|
|
|
|
|
|
|
newbuf.append( "${" + token + "}" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newbuf.append( aSource[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return newbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-27 11:03:35 +00:00
|
|
|
unsigned BOARD::GetNodesCount( int aNet ) const
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2018-05-12 17:07:04 +00:00
|
|
|
unsigned retval = 0;
|
2020-08-26 23:52:12 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2018-05-12 17:07:04 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2018-06-04 05:55:03 +00:00
|
|
|
{
|
|
|
|
if( ( aNet == -1 && pad->GetNetCode() > 0 ) || aNet == pad->GetNetCode() )
|
2018-05-12 17:07:04 +00:00
|
|
|
retval++;
|
2018-06-04 05:55:03 +00:00
|
|
|
}
|
2018-05-12 17:07:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
2017-06-23 16:39:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned BOARD::GetUnconnectedNetCount() const
|
|
|
|
{
|
|
|
|
return m_connectivity->GetUnconnectedCount();
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-23 04:31:26 +00:00
|
|
|
EDA_RECT BOARD::ComputeBoundingBox( bool aBoardEdgesOnly ) const
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2011-09-01 21:39:38 +00:00
|
|
|
EDA_RECT area;
|
2019-08-20 11:58:14 +00:00
|
|
|
LSET visible = GetVisibleLayers();
|
2019-10-05 08:39:12 +00:00
|
|
|
bool showInvisibleText = IsElementVisible( LAYER_MOD_TEXT_INVISIBLE )
|
|
|
|
&& PgmOrNull() && !PgmOrNull()->m_Printing;
|
2007-08-04 20:05:54 +00:00
|
|
|
|
2020-10-13 17:46:48 +00:00
|
|
|
// Check shapes, dimensions, texts, and fiducials
|
2020-08-26 23:52:12 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2007-08-04 20:05:54 +00:00
|
|
|
{
|
2020-10-13 17:46:48 +00:00
|
|
|
if( aBoardEdgesOnly && ( item->GetLayer() != Edge_Cuts || item->Type() != PCB_SHAPE_T ) )
|
2011-09-01 21:39:38 +00:00
|
|
|
continue;
|
2011-02-25 16:23:24 +00:00
|
|
|
|
2019-08-16 05:58:45 +00:00
|
|
|
if( ( item->GetLayerSet() & visible ).any() )
|
2011-09-01 21:39:38 +00:00
|
|
|
area.Merge( item->GetBoundingBox() );
|
2011-02-25 16:23:24 +00:00
|
|
|
}
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
// Check footprints
|
|
|
|
for( MODULE* footprint : m_modules )
|
2011-02-25 16:23:24 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
if( !( footprint->GetLayerSet() & visible ).any() )
|
2019-08-16 05:58:45 +00:00
|
|
|
continue;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-08-16 05:58:45 +00:00
|
|
|
if( aBoardEdgesOnly )
|
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( const BOARD_ITEM* edge : footprint->GraphicalItems() )
|
2019-08-16 05:58:45 +00:00
|
|
|
{
|
2020-10-13 17:46:48 +00:00
|
|
|
if( edge->GetLayer() == Edge_Cuts && edge->Type() == PCB_FP_SHAPE_T )
|
2019-08-16 05:58:45 +00:00
|
|
|
area.Merge( edge->GetBoundingBox() );
|
|
|
|
}
|
2011-02-25 16:23:24 +00:00
|
|
|
}
|
2019-08-16 05:58:45 +00:00
|
|
|
else
|
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
area.Merge( footprint->GetBoundingBox( showInvisibleText ) );
|
2019-08-16 05:58:45 +00:00
|
|
|
}
|
|
|
|
}
|
2011-02-25 16:23:24 +00:00
|
|
|
|
2019-08-16 05:58:45 +00:00
|
|
|
if( !aBoardEdgesOnly )
|
|
|
|
{
|
2011-02-25 16:23:24 +00:00
|
|
|
// Check tracks
|
2020-08-26 23:52:12 +00:00
|
|
|
for( TRACK* track : m_tracks )
|
2007-08-04 20:05:54 +00:00
|
|
|
{
|
2019-08-16 05:58:45 +00:00
|
|
|
if( ( track->GetLayerSet() & visible ).any() )
|
2011-09-01 21:39:38 +00:00
|
|
|
area.Merge( track->GetBoundingBox() );
|
2007-08-04 20:05:54 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 09:19:16 +00:00
|
|
|
// Check zones
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* aZone : m_zones )
|
2011-02-25 16:23:24 +00:00
|
|
|
{
|
2019-08-16 05:58:45 +00:00
|
|
|
if( ( aZone->GetLayerSet() & visible ).any() )
|
2011-09-01 21:39:38 +00:00
|
|
|
area.Merge( aZone->GetBoundingBox() );
|
2011-02-25 16:23:24 +00:00
|
|
|
}
|
2007-08-04 20:05:54 +00:00
|
|
|
}
|
|
|
|
|
++PCBNew
* Removed Pcb_Frame argument from BOARD() constructor, since it precludes
having a BOARD being edited by more than one editor, it was a bad design.
And this meant removing m_PcbFrame from BOARD.
* removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame
* Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp
* added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance
* a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed,
such as dialog_mask_clearance, dialog_drc, etc.
* Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it
with build_version.h's #define BOARD_FILE_VERSION, although there may be a
better place for this constant.
* Made the public functions in PARAM_CFG_ARRAY be type const.
void SaveParam(..) const and void ReadParam(..) const
* PARAM_CFG_BASE now has virtual destructor since we have various way of
destroying the derived class and boost::ptr_vector must be told about this.
* Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use
an automatic PARAM_CFG_ARRAY which is on the stack.\
* PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array,
since it has to access the current BOARD and the BOARD can change.
Remember BOARD_DESIGN_SETTINGS are now in the BOARD.
* Made the m_BoundingBox member private, this was a brutally hard task,
and indicative of the lack of commitment to accessors and object oriented
design on the part of KiCad developers. We must do better.
Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox().
* Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
2011-12-05 06:15:33 +00:00
|
|
|
return area;
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
2007-08-06 02:02:39 +00:00
|
|
|
|
|
|
|
|
2020-04-24 13:36:10 +00:00
|
|
|
void BOARD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
|
2007-08-20 19:33:15 +00:00
|
|
|
{
|
2009-05-28 08:42:24 +00:00
|
|
|
wxString txt;
|
2013-01-12 17:32:24 +00:00
|
|
|
int viasCount = 0;
|
|
|
|
int trackSegmentsCount = 0;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( TRACK* item : m_tracks )
|
2007-08-20 19:33:15 +00:00
|
|
|
{
|
2011-10-01 19:24:27 +00:00
|
|
|
if( item->Type() == PCB_VIA_T )
|
2009-05-28 08:42:24 +00:00
|
|
|
viasCount++;
|
2010-09-18 08:29:40 +00:00
|
|
|
else
|
|
|
|
trackSegmentsCount++;
|
2007-08-20 19:33:15 +00:00
|
|
|
}
|
|
|
|
|
2011-12-10 05:33:24 +00:00
|
|
|
txt.Printf( wxT( "%d" ), GetPadCount() );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Pads" ), txt, DARKGREEN );
|
2009-05-28 08:42:24 +00:00
|
|
|
|
|
|
|
txt.Printf( wxT( "%d" ), viasCount );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Vias" ), txt, DARKGREEN );
|
2007-08-20 19:33:15 +00:00
|
|
|
|
2010-09-18 08:29:40 +00:00
|
|
|
txt.Printf( wxT( "%d" ), trackSegmentsCount );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Track Segments" ), txt, DARKGREEN );
|
2010-09-18 08:29:40 +00:00
|
|
|
|
2009-05-28 08:42:24 +00:00
|
|
|
txt.Printf( wxT( "%d" ), GetNodesCount() );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Nodes" ), txt, DARKCYAN );
|
2007-08-20 19:33:15 +00:00
|
|
|
|
2018-09-21 12:41:28 +00:00
|
|
|
txt.Printf( wxT( "%d" ), m_NetInfo.GetNetCount() - 1 /* Don't include "No Net" in count */ );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Nets" ), txt, RED );
|
2007-08-20 19:33:15 +00:00
|
|
|
|
2017-03-22 13:51:07 +00:00
|
|
|
txt.Printf( wxT( "%d" ), GetConnectivity()->GetUnconnectedCount() );
|
2019-12-05 15:41:21 +00:00
|
|
|
aList.emplace_back( _( "Unrouted" ), txt, BLUE );
|
2007-08-20 19:33:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
|
2007-08-06 02:02:39 +00:00
|
|
|
{
|
2009-05-28 08:42:24 +00:00
|
|
|
KICAD_T stype;
|
2019-12-28 00:55:11 +00:00
|
|
|
SEARCH_RESULT result = SEARCH_RESULT::CONTINUE;
|
2020-08-26 23:52:12 +00:00
|
|
|
const KICAD_T* p = scanTypes;
|
|
|
|
bool done = false;
|
2007-08-24 03:40:04 +00:00
|
|
|
|
2007-08-30 22:20:52 +00:00
|
|
|
#if 0 && defined(DEBUG)
|
2009-05-28 08:42:24 +00:00
|
|
|
std::cout << GetClass().mb_str() << ' ';
|
2008-02-12 01:02:53 +00:00
|
|
|
#endif
|
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
while( !done )
|
2007-08-06 02:02:39 +00:00
|
|
|
{
|
2007-08-24 03:40:04 +00:00
|
|
|
stype = *p;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
switch( stype )
|
|
|
|
{
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_T:
|
2016-07-12 19:05:54 +00:00
|
|
|
result = inspector( this, testData ); // inspect me
|
2007-08-24 03:40:04 +00:00
|
|
|
// skip over any types handled in the above call.
|
|
|
|
++p;
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Instances of the requested KICAD_T live in a list, either one
|
2020-10-21 03:48:06 +00:00
|
|
|
* that I manage, or that my footprints manage. If it's a type managed
|
2009-05-28 08:42:24 +00:00
|
|
|
* by class MODULE, then simply pass it on to each module's
|
|
|
|
* MODULE::Visit() function by way of the
|
|
|
|
* IterateForward( m_Modules, ... ) call.
|
|
|
|
*/
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MODULE_T:
|
|
|
|
case PCB_PAD_T:
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
|
|
|
case PCB_FP_SHAPE_T:
|
|
|
|
case PCB_FP_ZONE_AREA_T:
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
// this calls MODULE::Visit() on each module.
|
2019-05-31 00:15:57 +00:00
|
|
|
result = IterateForward<MODULE*>( m_modules, inspector, testData, p );
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
// skip over any types handled in the above call.
|
2009-05-28 08:42:24 +00:00
|
|
|
for( ; ; )
|
2007-08-24 03:40:04 +00:00
|
|
|
{
|
|
|
|
switch( stype = *++p )
|
|
|
|
{
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MODULE_T:
|
|
|
|
case PCB_PAD_T:
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
|
|
|
case PCB_FP_SHAPE_T:
|
|
|
|
case PCB_FP_ZONE_AREA_T:
|
2007-08-24 03:40:04 +00:00
|
|
|
continue;
|
2009-05-28 08:42:24 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
;
|
2007-08-24 03:40:04 +00:00
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_SHAPE_T:
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TEXT_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_ALIGNED_T:
|
2020-09-17 00:54:58 +00:00
|
|
|
case PCB_DIM_CENTER_T:
|
|
|
|
case PCB_DIM_ORTHOGONAL_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_LEADER_T:
|
2011-09-07 19:41:04 +00:00
|
|
|
case PCB_TARGET_T:
|
2019-05-30 13:33:59 +00:00
|
|
|
result = IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, p );
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
// skip over any types handled in the above call.
|
2009-05-28 08:42:24 +00:00
|
|
|
for( ; ; )
|
2007-08-24 03:40:04 +00:00
|
|
|
{
|
|
|
|
switch( stype = *++p )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_SHAPE_T:
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TEXT_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_ALIGNED_T:
|
2020-09-17 00:54:58 +00:00
|
|
|
case PCB_DIM_CENTER_T:
|
|
|
|
case PCB_DIM_ORTHOGONAL_T:
|
2020-09-12 20:09:40 +00:00
|
|
|
case PCB_DIM_LEADER_T:
|
2011-09-07 19:41:04 +00:00
|
|
|
case PCB_TARGET_T:
|
2007-08-24 03:40:04 +00:00
|
|
|
continue;
|
2009-05-28 08:42:24 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
;
|
2007-08-24 03:40:04 +00:00
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2007-08-30 22:20:52 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_VIA_T:
|
2019-05-31 02:30:28 +00:00
|
|
|
result = IterateForward<TRACK*>( m_tracks, inspector, testData, p );
|
2007-08-30 22:20:52 +00:00
|
|
|
++p;
|
|
|
|
break;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
case PCB_ARC_T:
|
2019-05-31 02:30:28 +00:00
|
|
|
result = IterateForward<TRACK*>( m_tracks, inspector, testData, p );
|
2007-08-30 22:20:52 +00:00
|
|
|
++p;
|
|
|
|
break;
|
2007-11-27 22:49:35 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_MARKER_T:
|
2020-08-26 23:52:12 +00:00
|
|
|
for( MARKER_PCB* marker : m_markers )
|
2007-11-27 22:49:35 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
result = marker->Visit( inspector, testData, p );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-11-27 22:49:35 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-11-27 22:49:35 +00:00
|
|
|
++p;
|
|
|
|
break;
|
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_ZONE_AREA_T:
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : m_zones)
|
2007-12-29 19:15:58 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
result = zone->Visit( inspector, testData, p );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-12-29 19:15:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2007-12-29 19:15:58 +00:00
|
|
|
++p;
|
|
|
|
break;
|
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
case PCB_GROUP_T:
|
2020-08-12 11:23:30 +00:00
|
|
|
result = IterateForward<PCB_GROUP*>( m_groups, inspector, testData, p );
|
2020-08-11 19:37:07 +00:00
|
|
|
++p;
|
|
|
|
break;
|
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
default: // catch EOT or ANY OTHER type here and return.
|
|
|
|
done = true;
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2007-08-06 02:02:39 +00:00
|
|
|
}
|
2007-08-09 21:15:08 +00:00
|
|
|
|
2008-02-12 01:02:53 +00:00
|
|
|
return result;
|
2007-08-06 02:02:39 +00:00
|
|
|
}
|
2007-08-06 20:26:59 +00:00
|
|
|
|
|
|
|
|
2010-12-29 17:47:32 +00:00
|
|
|
NETINFO_ITEM* BOARD::FindNet( int aNetcode ) const
|
2007-08-20 19:33:15 +00:00
|
|
|
{
|
2011-12-10 05:33:24 +00:00
|
|
|
// the first valid netcode is 1 and the last is m_NetInfo.GetCount()-1.
|
2015-01-23 11:24:32 +00:00
|
|
|
// zero is reserved for "no connection" and is not actually a net.
|
2009-07-20 05:18:47 +00:00
|
|
|
// NULL is returned for non valid netcodes
|
2009-08-10 02:22:56 +00:00
|
|
|
|
2018-09-21 12:41:28 +00:00
|
|
|
wxASSERT( m_NetInfo.GetNetCount() > 0 );
|
2015-01-23 11:24:32 +00:00
|
|
|
|
|
|
|
if( aNetcode == NETINFO_LIST::UNCONNECTED && m_NetInfo.GetNetCount() == 0 )
|
2020-02-22 13:19:43 +00:00
|
|
|
return NETINFO_LIST::OrphanedItem();
|
2014-11-24 15:34:47 +00:00
|
|
|
else
|
|
|
|
return m_NetInfo.GetNetItem( aNetcode );
|
2007-08-20 19:33:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-28 08:42:24 +00:00
|
|
|
NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
|
2008-01-06 20:58:27 +00:00
|
|
|
{
|
2014-01-10 17:04:07 +00:00
|
|
|
return m_NetInfo.GetNetItem( aNetname );
|
2008-01-06 20:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-07 17:10:12 +00:00
|
|
|
MODULE* BOARD::FindModuleByReference( const wxString& aReference ) const
|
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : m_modules )
|
2020-09-04 09:32:20 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
if( aReference == footprint->GetReference() )
|
|
|
|
return footprint;
|
2020-09-04 09:32:20 +00:00
|
|
|
}
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2020-09-04 09:32:20 +00:00
|
|
|
return nullptr;
|
2008-02-07 17:10:12 +00:00
|
|
|
}
|
2008-01-06 20:58:27 +00:00
|
|
|
|
|
|
|
|
2020-02-21 22:20:42 +00:00
|
|
|
MODULE* BOARD::FindModuleByPath( const KIID_PATH& aPath ) const
|
2013-04-25 16:29:35 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : m_modules )
|
2013-04-25 16:29:35 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->GetPath() == aPath )
|
|
|
|
return footprint;
|
2013-04-25 16:29:35 +00:00
|
|
|
}
|
|
|
|
|
2020-02-20 12:11:04 +00:00
|
|
|
return nullptr;
|
2013-04-25 16:29:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-02 16:14:34 +00:00
|
|
|
// The pad count for each netcode, stored in a buffer for a fast access.
|
|
|
|
// This is needed by the sort function sortNetsByNodes()
|
|
|
|
static std::vector<int> padCountListByNet;
|
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
2018-06-02 16:14:34 +00:00
|
|
|
// Sort nets by decreasing pad count.
|
|
|
|
// For same pad count, sort by alphabetic names
|
2014-08-29 06:47:05 +00:00
|
|
|
static bool sortNetsByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
|
2007-12-17 20:18:04 +00:00
|
|
|
{
|
2018-06-02 16:14:34 +00:00
|
|
|
int countA = padCountListByNet[a->GetNet()];
|
|
|
|
int countB = padCountListByNet[b->GetNet()];
|
2014-08-29 06:47:05 +00:00
|
|
|
|
2017-03-22 13:51:07 +00:00
|
|
|
if( countA == countB )
|
|
|
|
return a->GetNetname() < b->GetNetname();
|
|
|
|
else
|
|
|
|
return countB < countA;
|
2007-12-17 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
2014-08-29 06:47:05 +00:00
|
|
|
// Sort nets by alphabetic names
|
|
|
|
static bool sortNetsByNames( const NETINFO_ITEM* a, const NETINFO_ITEM* b )
|
|
|
|
{
|
|
|
|
return a->GetNetname() < b->GetNetname();
|
|
|
|
}
|
2007-12-17 20:18:04 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
int BOARD::SortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount )
|
2007-12-17 20:18:04 +00:00
|
|
|
{
|
2011-12-10 05:33:24 +00:00
|
|
|
if( m_NetInfo.GetNetCount() == 0 )
|
2009-05-28 08:42:24 +00:00
|
|
|
return 0;
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2009-08-10 02:22:56 +00:00
|
|
|
// Build the list
|
2009-05-28 08:42:24 +00:00
|
|
|
std::vector <NETINFO_ITEM*> netBuffer;
|
2009-08-10 02:22:56 +00:00
|
|
|
|
2011-12-10 05:33:24 +00:00
|
|
|
netBuffer.reserve( m_NetInfo.GetNetCount() );
|
2018-11-03 17:13:16 +00:00
|
|
|
int max_netcode = 0;
|
2009-08-10 02:22:56 +00:00
|
|
|
|
2018-09-17 09:54:49 +00:00
|
|
|
for( NETINFO_ITEM* net : m_NetInfo )
|
2008-02-12 01:02:53 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
int netcode = net->GetNet();
|
2018-11-03 17:13:16 +00:00
|
|
|
|
2018-10-19 11:28:34 +00:00
|
|
|
if( netcode > 0 && net->IsCurrent() )
|
2018-11-03 17:13:16 +00:00
|
|
|
{
|
2018-09-17 09:54:49 +00:00
|
|
|
netBuffer.push_back( net );
|
2018-11-03 17:13:16 +00:00
|
|
|
max_netcode = std::max( netcode, max_netcode);
|
|
|
|
}
|
2008-02-12 01:02:53 +00:00
|
|
|
}
|
|
|
|
|
2009-08-10 02:22:56 +00:00
|
|
|
// sort the list
|
2009-05-28 08:42:24 +00:00
|
|
|
if( aSortbyPadsCount )
|
2018-06-02 16:14:34 +00:00
|
|
|
{
|
|
|
|
// Build the pad count by net:
|
|
|
|
padCountListByNet.clear();
|
|
|
|
std::vector<D_PAD*> pads = GetPads();
|
|
|
|
|
2018-11-03 17:13:16 +00:00
|
|
|
padCountListByNet.assign( max_netcode + 1, 0 );
|
2018-06-02 16:14:34 +00:00
|
|
|
|
|
|
|
for( D_PAD* pad : pads )
|
2020-01-15 19:40:20 +00:00
|
|
|
{
|
|
|
|
int netCode = pad->GetNetCode();
|
|
|
|
|
|
|
|
if( netCode >= 0 )
|
|
|
|
padCountListByNet[ netCode ]++;
|
|
|
|
}
|
2018-06-02 16:14:34 +00:00
|
|
|
|
2014-08-29 06:47:05 +00:00
|
|
|
sort( netBuffer.begin(), netBuffer.end(), sortNetsByNodes );
|
2018-06-02 16:14:34 +00:00
|
|
|
}
|
2014-08-29 06:47:05 +00:00
|
|
|
else
|
2018-11-03 17:13:16 +00:00
|
|
|
{
|
2014-08-29 06:47:05 +00:00
|
|
|
sort( netBuffer.begin(), netBuffer.end(), sortNetsByNames );
|
2018-11-03 17:13:16 +00:00
|
|
|
}
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2018-09-17 09:54:49 +00:00
|
|
|
for( NETINFO_ITEM* net : netBuffer )
|
2018-10-19 11:28:34 +00:00
|
|
|
aNames.Add( UnescapeString( net->GetNetname() ) );
|
2008-02-12 01:02:53 +00:00
|
|
|
|
2009-05-24 18:28:36 +00:00
|
|
|
return netBuffer.size();
|
2007-12-17 20:18:04 +00:00
|
|
|
}
|
2007-10-30 21:30:58 +00:00
|
|
|
|
2009-05-28 08:42:24 +00:00
|
|
|
|
2020-07-06 10:51:04 +00:00
|
|
|
std::vector<wxString> BOARD::GetNetClassAssignmentCandidates()
|
2020-05-31 21:42:04 +00:00
|
|
|
{
|
2020-07-06 10:51:04 +00:00
|
|
|
std::vector<wxString> names;
|
2020-05-31 21:42:04 +00:00
|
|
|
|
2020-07-06 10:51:04 +00:00
|
|
|
for( NETINFO_ITEM* net : m_NetInfo )
|
2020-05-31 21:42:04 +00:00
|
|
|
{
|
2020-08-24 01:46:01 +00:00
|
|
|
if( !net->GetNetname().IsEmpty() )
|
|
|
|
names.emplace_back( net->GetNetname() );
|
2020-05-31 21:42:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-06 10:51:04 +00:00
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-19 18:21:24 +00:00
|
|
|
void BOARD::SynchronizeProperties()
|
|
|
|
{
|
|
|
|
if( m_project )
|
|
|
|
SetProperties( m_project->GetTextVars() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-06 10:51:04 +00:00
|
|
|
void BOARD::SynchronizeNetsAndNetClasses()
|
|
|
|
{
|
2020-09-26 18:45:16 +00:00
|
|
|
if( !m_project )
|
|
|
|
return;
|
|
|
|
|
|
|
|
NET_SETTINGS* netSettings = m_project->GetProjectFile().m_NetSettings.get();
|
|
|
|
NETCLASSES& netClasses = netSettings->m_NetClasses;
|
|
|
|
NETCLASSPTR defaultNetClass = netClasses.GetDefault();
|
|
|
|
|
|
|
|
for( NETINFO_ITEM* net : m_NetInfo )
|
2020-05-31 21:42:04 +00:00
|
|
|
{
|
2020-09-26 18:45:16 +00:00
|
|
|
const wxString& netname = net->GetNetname();
|
2020-10-07 14:40:12 +00:00
|
|
|
const wxString& netclassName = netSettings->GetNetclassName( netname );
|
2020-05-31 21:42:04 +00:00
|
|
|
|
2020-10-07 14:40:12 +00:00
|
|
|
net->SetClass( netClasses.Find( netclassName ) );
|
2020-05-31 21:42:04 +00:00
|
|
|
}
|
|
|
|
|
2020-09-26 18:45:16 +00:00
|
|
|
BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
|
|
|
|
|
|
|
|
// Set initial values for custom track width & via size to match the default
|
|
|
|
// netclass settings
|
|
|
|
bds.UseCustomTrackViaSize( false );
|
|
|
|
bds.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() );
|
|
|
|
bds.SetCustomViaSize( defaultNetClass->GetViaDiameter() );
|
|
|
|
bds.SetCustomViaDrill( defaultNetClass->GetViaDrill() );
|
|
|
|
bds.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() );
|
|
|
|
bds.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() );
|
|
|
|
bds.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() );
|
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
int BOARD::SetAreasNetCodesFromNetNames()
|
2009-05-24 18:28:36 +00:00
|
|
|
{
|
|
|
|
int error_count = 0;
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
2009-05-24 18:28:36 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
if( !zone->IsOnCopperLayer() )
|
2009-05-24 18:28:36 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
|
2009-05-24 18:28:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
if( zone->GetNetCode() != 0 ) // i.e. if this zone is connected to a net
|
2009-05-24 18:28:36 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
const NETINFO_ITEM* net = zone->GetNet();
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2009-05-24 18:28:36 +00:00
|
|
|
if( net )
|
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
zone->SetNetCode( net->GetNet() );
|
2009-05-24 18:28:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error_count++;
|
2011-09-14 20:04:58 +00:00
|
|
|
|
|
|
|
// keep Net Name and set m_NetCode to -1 : error flag.
|
2020-08-26 23:52:12 +00:00
|
|
|
zone->SetNetCode( -1 );
|
2009-05-24 18:28:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return error_count;
|
|
|
|
}
|
2008-11-27 10:12:46 +00:00
|
|
|
|
2007-10-30 21:30:58 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
D_PAD* BOARD::GetPad( const wxPoint& aPosition, LSET aLayerSet )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2016-07-12 19:05:54 +00:00
|
|
|
if( !aLayerSet.any() )
|
|
|
|
aLayerSet = LSET::AllCuMask();
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : m_modules )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2018-05-22 22:37:24 +00:00
|
|
|
D_PAD* pad = NULL;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint->HitTest( aPosition ) )
|
|
|
|
pad = footprint->GetPad( aPosition, aLayerSet );
|
2014-06-24 16:17:18 +00:00
|
|
|
|
|
|
|
if( pad )
|
|
|
|
return pad;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
return NULL;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-25 17:13:33 +00:00
|
|
|
D_PAD* BOARD::GetPad( TRACK* aTrace, ENDPOINT_T aEndPoint )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2014-06-24 16:17:18 +00:00
|
|
|
const wxPoint& aPosition = aTrace->GetEndPoint( aEndPoint );
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
LSET lset( aTrace->GetLayer() );
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2018-05-22 22:37:24 +00:00
|
|
|
return GetPad( aPosition, lset );
|
2017-07-17 12:55:31 +00:00
|
|
|
}
|
|
|
|
|
2017-09-18 09:25:32 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
D_PAD* BOARD::GetPadFast( const wxPoint& aPosition, LSET aLayerSet )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2017-03-22 13:51:07 +00:00
|
|
|
{
|
2012-02-19 04:02:19 +00:00
|
|
|
if( pad->GetPosition() != aPosition )
|
2011-09-15 17:58:35 +00:00
|
|
|
continue;
|
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
// Pad found, it must be on the correct layer
|
2016-07-12 19:05:54 +00:00
|
|
|
if( ( pad->GetLayerSet() & aLayerSet ).any() )
|
2011-09-15 17:58:35 +00:00
|
|
|
return pad;
|
2018-01-28 09:35:33 +00:00
|
|
|
}
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
2017-03-22 13:51:07 +00:00
|
|
|
return nullptr;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
D_PAD* BOARD::GetPad( std::vector<D_PAD*>& aPadList, const wxPoint& aPosition, LSET aLayerSet )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2016-07-12 19:05:54 +00:00
|
|
|
// Search aPadList for aPosition
|
2011-10-31 13:44:13 +00:00
|
|
|
// aPadList is sorted by X then Y values, and a fast binary search is used
|
|
|
|
int idxmax = aPadList.size()-1;
|
|
|
|
|
|
|
|
int delta = aPadList.size();
|
2011-12-28 15:14:46 +00:00
|
|
|
|
|
|
|
int idx = 0; // Starting index is the beginning of list
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
while( delta )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2011-12-28 15:14:46 +00:00
|
|
|
// Calculate half size of remaining interval to test.
|
|
|
|
// Ensure the computed value is not truncated (too small)
|
2011-10-31 13:44:13 +00:00
|
|
|
if( (delta & 1) && ( delta > 1 ) )
|
|
|
|
delta++;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
delta /= 2;
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
D_PAD* pad = aPadList[idx];
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( pad->GetPosition() == aPosition ) // candidate found
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2011-10-31 13:44:13 +00:00
|
|
|
// The pad must match the layer mask:
|
2016-07-12 19:05:54 +00:00
|
|
|
if( ( aLayerSet & pad->GetLayerSet() ).any() )
|
2011-10-31 13:44:13 +00:00
|
|
|
return pad;
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
// More than one pad can be at aPosition
|
|
|
|
// search for a pad at aPosition that matched this mask
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
// search next
|
|
|
|
for( int ii = idx+1; ii <= idxmax; ii++ )
|
|
|
|
{
|
|
|
|
pad = aPadList[ii];
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( pad->GetPosition() != aPosition )
|
2011-10-31 13:44:13 +00:00
|
|
|
break;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
if( ( aLayerSet & pad->GetLayerSet() ).any() )
|
2011-10-31 13:44:13 +00:00
|
|
|
return pad;
|
|
|
|
}
|
|
|
|
// search previous
|
|
|
|
for( int ii = idx-1 ;ii >=0; ii-- )
|
|
|
|
{
|
|
|
|
pad = aPadList[ii];
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( pad->GetPosition() != aPosition )
|
2011-10-31 13:44:13 +00:00
|
|
|
break;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
if( ( aLayerSet & pad->GetLayerSet() ).any() )
|
2011-10-31 13:44:13 +00:00
|
|
|
return pad;
|
|
|
|
}
|
2011-09-15 17:58:35 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
// Not found:
|
|
|
|
return 0;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
2016-07-12 19:05:54 +00:00
|
|
|
if( pad->GetPosition().x == aPosition.x ) // Must search considering Y coordinate
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2016-07-12 19:05:54 +00:00
|
|
|
if( pad->GetPosition().y < aPosition.y ) // Must search after this item
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2011-10-31 13:44:13 +00:00
|
|
|
idx += delta;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
if( idx > idxmax )
|
|
|
|
idx = idxmax;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
2011-10-31 13:44:13 +00:00
|
|
|
else // Must search before this item
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2011-10-31 13:44:13 +00:00
|
|
|
idx -= delta;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
if( idx < 0 )
|
|
|
|
idx = 0;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-19 04:02:19 +00:00
|
|
|
else if( pad->GetPosition().x < aPosition.x ) // Must search after this item
|
2011-10-31 13:44:13 +00:00
|
|
|
{
|
|
|
|
idx += delta;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
if( idx > idxmax )
|
|
|
|
idx = idxmax;
|
|
|
|
}
|
|
|
|
else // Must search before this item
|
|
|
|
{
|
|
|
|
idx -= delta;
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
if( idx < 0 )
|
|
|
|
idx = 0;
|
|
|
|
}
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-10-31 13:44:13 +00:00
|
|
|
/**
|
2020-09-22 21:50:59 +00:00
|
|
|
* Used by #GetSortedPadListByXCoord to sort a pad list by X coordinate value.
|
|
|
|
*
|
2012-09-11 07:33:17 +00:00
|
|
|
* This function is used to build ordered pads lists
|
2011-10-31 13:44:13 +00:00
|
|
|
*/
|
2012-09-11 07:33:17 +00:00
|
|
|
bool sortPadsByXthenYCoord( D_PAD* const & ref, D_PAD* const & comp )
|
2011-10-31 13:44:13 +00:00
|
|
|
{
|
2012-02-19 04:02:19 +00:00
|
|
|
if( ref->GetPosition().x == comp->GetPosition().x )
|
|
|
|
return ref->GetPosition().y < comp->GetPosition().y;
|
|
|
|
return ref->GetPosition().x < comp->GetPosition().x;
|
2011-10-31 13:44:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-17 21:21:03 +00:00
|
|
|
void BOARD::GetSortedPadListByXthenYCoord( std::vector<D_PAD*>& aVector, int aNetCode )
|
2011-09-17 01:22:26 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2011-12-17 21:21:03 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads( ) )
|
2011-12-17 21:21:03 +00:00
|
|
|
{
|
2017-03-22 13:51:07 +00:00
|
|
|
if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
|
|
|
|
aVector.push_back( pad );
|
2011-12-17 21:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-17 01:22:26 +00:00
|
|
|
|
2017-03-22 13:51:07 +00:00
|
|
|
std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
|
2011-09-17 01:22:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-23 23:02:40 +00:00
|
|
|
void BOARD::PadDelete( D_PAD* aPad )
|
|
|
|
{
|
2018-07-26 14:20:16 +00:00
|
|
|
GetConnectivity()->Remove( aPad );
|
2020-04-12 19:29:16 +00:00
|
|
|
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aPad );
|
|
|
|
|
2015-09-23 23:02:40 +00:00
|
|
|
aPad->DeleteStructure();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
std::tuple<int, double, double> BOARD::GetTrackLength( const TRACK& aTrack ) const
|
2011-09-16 14:13:02 +00:00
|
|
|
{
|
2019-05-31 02:30:28 +00:00
|
|
|
int count = 0;
|
|
|
|
double length = 0.0;
|
|
|
|
double package_length = 0.0;
|
2011-09-16 14:13:02 +00:00
|
|
|
|
2019-05-17 00:13:21 +00:00
|
|
|
constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
|
2019-05-31 02:30:28 +00:00
|
|
|
auto connectivity = GetBoard()->GetConnectivity();
|
2011-09-16 15:54:50 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_CONNECTED_ITEM* item : connectivity->GetConnectedItems(
|
2019-05-31 02:30:28 +00:00
|
|
|
static_cast<const BOARD_CONNECTED_ITEM*>( &aTrack ), types ) )
|
2011-09-16 15:54:50 +00:00
|
|
|
{
|
2019-05-31 02:30:28 +00:00
|
|
|
count++;
|
2011-09-16 15:54:50 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( TRACK* track = dyn_cast<TRACK*>( item ) )
|
2011-09-16 15:54:50 +00:00
|
|
|
{
|
2019-05-31 02:30:28 +00:00
|
|
|
bool inPad = false;
|
2011-09-16 15:54:50 +00:00
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
for( auto pad_it : connectivity->GetConnectedPads( item ) )
|
2011-09-16 15:54:50 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
D_PAD* pad = static_cast<D_PAD*>( pad_it );
|
2011-09-16 15:54:50 +00:00
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
if( pad->HitTest( track->GetStart(), track->GetWidth() / 2 )
|
|
|
|
&& pad->HitTest( track->GetEnd(), track->GetWidth() / 2 ) )
|
|
|
|
{
|
|
|
|
inPad = true;
|
|
|
|
break;
|
|
|
|
}
|
2011-09-16 15:54:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
if( !inPad )
|
|
|
|
length += track->GetLength();
|
2011-09-16 15:54:50 +00:00
|
|
|
}
|
2020-10-14 23:37:26 +00:00
|
|
|
else if( D_PAD* pad = dyn_cast<D_PAD*>( item ) )
|
|
|
|
{
|
2019-05-31 02:30:28 +00:00
|
|
|
package_length += pad->GetPadToDieLength();
|
2020-10-14 23:37:26 +00:00
|
|
|
}
|
2011-09-16 15:54:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
return std::make_tuple( count, length, package_length );
|
2011-09-16 15:54:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
MODULE* BOARD::GetFootprint( const wxPoint& aPosition, PCB_LAYER_ID aActiveLayer,
|
2011-09-16 18:54:04 +00:00
|
|
|
bool aVisibleOnly, bool aIgnoreLocked )
|
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
MODULE* footprint = NULL;
|
|
|
|
MODULE* alt_footprint = NULL;
|
|
|
|
int min_dim = 0x7FFFFFFF;
|
|
|
|
int alt_min_dim = 0x7FFFFFFF;
|
2013-04-07 11:55:18 +00:00
|
|
|
bool current_layer_back = IsBackLayer( aActiveLayer );
|
2011-09-16 18:54:04 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* candidate : m_modules )
|
2011-09-16 18:54:04 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
// is the ref point within the footprint's bounds?
|
|
|
|
if( !candidate->HitTest( aPosition ) )
|
2011-09-16 18:54:04 +00:00
|
|
|
continue;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
// if caller wants to ignore locked footprints, and this one is locked, skip it.
|
|
|
|
if( aIgnoreLocked && candidate->IsLocked() )
|
2011-09-16 18:54:04 +00:00
|
|
|
continue;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
PCB_LAYER_ID layer = candidate->GetLayer();
|
2011-09-16 18:54:04 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
// Filter non visible footprints if requested
|
2014-06-24 16:17:18 +00:00
|
|
|
if( !aVisibleOnly || IsModuleLayerVisible( layer ) )
|
2013-04-07 11:55:18 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
EDA_RECT bb = candidate->GetFootprintRect();
|
2014-06-24 16:17:18 +00:00
|
|
|
|
2013-04-07 11:55:18 +00:00
|
|
|
int offx = bb.GetX() + bb.GetWidth() / 2;
|
|
|
|
int offy = bb.GetY() + bb.GetHeight() / 2;
|
2011-09-16 18:54:04 +00:00
|
|
|
|
2013-04-07 11:55:18 +00:00
|
|
|
// off x & offy point to the middle of the box.
|
|
|
|
int dist = ( aPosition.x - offx ) * ( aPosition.x - offx ) +
|
|
|
|
( aPosition.y - offy ) * ( aPosition.y - offy );
|
2011-09-16 18:54:04 +00:00
|
|
|
|
2013-04-07 11:55:18 +00:00
|
|
|
if( current_layer_back == IsBackLayer( layer ) )
|
2011-09-16 18:54:04 +00:00
|
|
|
{
|
2013-04-07 11:55:18 +00:00
|
|
|
if( dist <= min_dim )
|
|
|
|
{
|
|
|
|
// better footprint shown on the active side
|
2020-10-14 23:37:26 +00:00
|
|
|
footprint = candidate;
|
2013-04-07 11:55:18 +00:00
|
|
|
min_dim = dist;
|
|
|
|
}
|
2011-09-16 18:54:04 +00:00
|
|
|
}
|
2013-04-07 11:55:18 +00:00
|
|
|
else if( aVisibleOnly && IsModuleLayerVisible( layer ) )
|
2011-09-16 18:54:04 +00:00
|
|
|
{
|
2013-04-07 11:55:18 +00:00
|
|
|
if( dist <= alt_min_dim )
|
|
|
|
{
|
|
|
|
// better footprint shown on the other side
|
2020-10-14 23:37:26 +00:00
|
|
|
alt_footprint = candidate;
|
2013-04-07 11:55:18 +00:00
|
|
|
alt_min_dim = dist;
|
|
|
|
}
|
2011-09-16 18:54:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( footprint )
|
|
|
|
return footprint;
|
2011-09-16 18:54:04 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
if( alt_footprint)
|
|
|
|
return alt_footprint;
|
2011-09-16 18:54:04 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-20 14:11:39 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
std::list<ZONE_CONTAINER*> BOARD::GetZoneList( bool aIncludeZonesInFootprints )
|
|
|
|
{
|
|
|
|
std::list<ZONE_CONTAINER*> zones;
|
|
|
|
|
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
|
|
|
zones.push_back( zone );
|
|
|
|
|
|
|
|
if( aIncludeZonesInFootprints )
|
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : m_modules )
|
2020-09-22 21:50:59 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE_ZONE_CONTAINER* zone : footprint->Zones() )
|
2020-09-22 21:50:59 +00:00
|
|
|
zones.push_back( zone );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return zones;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-20 14:11:39 +00:00
|
|
|
ZONE_CONTAINER* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode, PCB_LAYER_ID aLayer,
|
2020-08-07 14:04:34 +00:00
|
|
|
wxPoint aStartPointPosition, ZONE_BORDER_DISPLAY_STYLE aHatch )
|
2013-03-20 14:50:12 +00:00
|
|
|
{
|
2020-08-26 23:52:12 +00:00
|
|
|
ZONE_CONTAINER* new_area = new ZONE_CONTAINER( this );
|
|
|
|
|
|
|
|
new_area->SetNetCode( aNetcode );
|
|
|
|
new_area->SetLayer( aLayer );
|
|
|
|
|
|
|
|
m_zones.push_back( new_area );
|
|
|
|
|
|
|
|
new_area->SetHatchStyle( (ZONE_BORDER_DISPLAY_STYLE) aHatch );
|
|
|
|
|
|
|
|
// Add the first corner to the new zone
|
|
|
|
new_area->AppendCorner( aStartPointPosition, -1 );
|
2013-03-20 14:50:12 +00:00
|
|
|
|
|
|
|
if( aNewZonesList )
|
|
|
|
{
|
2020-08-26 18:04:32 +00:00
|
|
|
ITEM_PICKER picker( nullptr, new_area, UNDO_REDO::NEWITEM );
|
2013-03-20 14:50:12 +00:00
|
|
|
aNewZonesList->PushItem( picker );
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_area;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::RemoveArea( PICKED_ITEMS_LIST* aDeletedList, ZONE_CONTAINER* area_to_remove )
|
|
|
|
{
|
|
|
|
if( area_to_remove == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( aDeletedList )
|
|
|
|
{
|
2020-08-26 18:04:32 +00:00
|
|
|
ITEM_PICKER picker( nullptr, area_to_remove, UNDO_REDO::DELETED );
|
2013-03-20 14:50:12 +00:00
|
|
|
aDeletedList->PushItem( picker );
|
|
|
|
Remove( area_to_remove ); // remove from zone list, but does not delete it
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Delete( area_to_remove );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAINER* aCurrArea )
|
|
|
|
{
|
|
|
|
// mark all areas as unmodified except this one, if modified
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : m_zones )
|
2018-09-17 09:54:49 +00:00
|
|
|
zone->SetLocalFlags( 0 );
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2013-03-26 09:58:40 +00:00
|
|
|
aCurrArea->SetLocalFlags( 1 );
|
2013-03-20 14:50:12 +00:00
|
|
|
|
2017-03-07 12:06:00 +00:00
|
|
|
if( aCurrArea->Outline()->IsSelfIntersecting() )
|
2013-03-20 14:50:12 +00:00
|
|
|
{
|
2020-08-07 14:04:34 +00:00
|
|
|
aCurrArea->UnHatchBorder();
|
2017-03-07 12:06:00 +00:00
|
|
|
|
|
|
|
// Normalize copied area and store resulting number of polygons
|
|
|
|
int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines();
|
2013-03-20 14:50:12 +00:00
|
|
|
|
|
|
|
// If clipping has created some polygons, we must add these new copper areas.
|
|
|
|
if( n_poly > 1 )
|
|
|
|
{
|
|
|
|
ZONE_CONTAINER* NewArea;
|
|
|
|
|
2017-03-07 12:06:00 +00:00
|
|
|
// Move the newly created polygons to new areas, removing them from the current area
|
2013-03-20 14:50:12 +00:00
|
|
|
for( int ip = 1; ip < n_poly; ip++ )
|
|
|
|
{
|
2017-03-07 12:06:00 +00:00
|
|
|
// Create new copper area and copy poly into it
|
|
|
|
SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) );
|
2014-02-25 10:40:34 +00:00
|
|
|
NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(),
|
2017-03-07 12:06:00 +00:00
|
|
|
wxPoint(0, 0), aCurrArea->GetHatchStyle() );
|
2013-03-20 14:50:12 +00:00
|
|
|
|
|
|
|
// remove the poly that was automatically created for the new area
|
|
|
|
// and replace it with a poly from NormalizeAreaOutlines
|
|
|
|
delete NewArea->Outline();
|
|
|
|
NewArea->SetOutline( new_p );
|
2020-08-07 14:04:34 +00:00
|
|
|
NewArea->HatchBorder();
|
2013-03-26 09:58:40 +00:00
|
|
|
NewArea->SetLocalFlags( 1 );
|
2013-03-20 14:50:12 +00:00
|
|
|
}
|
|
|
|
|
2017-03-07 12:06:00 +00:00
|
|
|
SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
|
|
|
|
delete aCurrArea->Outline();
|
|
|
|
aCurrArea->SetOutline( new_p );
|
|
|
|
}
|
2013-03-20 14:50:12 +00:00
|
|
|
}
|
|
|
|
|
2020-08-07 14:04:34 +00:00
|
|
|
aCurrArea->HatchBorder();
|
2013-03-20 14:50:12 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-17 20:07:27 +00:00
|
|
|
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText,
|
2020-10-22 20:27:46 +00:00
|
|
|
std::vector<wxPoint>* aDiscontinuities,
|
|
|
|
std::vector<wxPoint>* aIntersections )
|
2013-10-04 08:42:09 +00:00
|
|
|
{
|
2020-10-22 20:27:46 +00:00
|
|
|
bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
|
|
|
|
aErrorText, aDiscontinuities, aIntersections );
|
2017-03-23 07:56:52 +00:00
|
|
|
|
|
|
|
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
|
|
|
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
|
|
|
|
|
|
|
return success;
|
2013-10-04 08:42:09 +00:00
|
|
|
}
|
2017-03-22 13:43:10 +00:00
|
|
|
|
2017-03-22 13:51:07 +00:00
|
|
|
|
2020-10-27 11:03:35 +00:00
|
|
|
const std::vector<D_PAD*> BOARD::GetPads() const
|
2017-03-22 13:51:07 +00:00
|
|
|
{
|
2018-09-17 09:54:49 +00:00
|
|
|
std::vector<D_PAD*> allPads;
|
2017-03-22 13:51:07 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2018-09-17 09:54:49 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2018-09-17 09:54:49 +00:00
|
|
|
allPads.push_back( pad );
|
2017-03-22 13:51:07 +00:00
|
|
|
}
|
|
|
|
|
2018-09-17 09:54:49 +00:00
|
|
|
return allPads;
|
2017-03-22 13:51:07 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 11:40:20 +00:00
|
|
|
|
2020-10-27 11:03:35 +00:00
|
|
|
unsigned BOARD::GetPadCount() const
|
2017-03-22 13:51:07 +00:00
|
|
|
{
|
2018-05-12 17:07:04 +00:00
|
|
|
unsigned retval = 0;
|
2018-09-17 09:54:49 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
|
|
|
retval += footprint->Pads().size();
|
2018-05-12 17:07:04 +00:00
|
|
|
|
|
|
|
return retval;
|
2017-03-22 13:51:07 +00:00
|
|
|
}
|
|
|
|
|
2017-06-30 11:40:20 +00:00
|
|
|
|
2018-09-23 17:21:16 +00:00
|
|
|
const std::vector<BOARD_CONNECTED_ITEM*> BOARD::AllConnectedItems()
|
|
|
|
{
|
|
|
|
std::vector<BOARD_CONNECTED_ITEM*> items;
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( TRACK* track : Tracks() )
|
2018-09-23 17:21:16 +00:00
|
|
|
items.push_back( track );
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( MODULE* footprint : Modules() )
|
2018-09-23 17:21:16 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
for( D_PAD* pad : footprint->Pads() )
|
2018-09-23 17:21:16 +00:00
|
|
|
items.push_back( pad );
|
|
|
|
}
|
|
|
|
|
2020-08-26 23:52:12 +00:00
|
|
|
for( ZONE_CONTAINER* zone : Zones() )
|
2018-09-23 17:21:16 +00:00
|
|
|
items.push_back( zone );
|
|
|
|
|
|
|
|
return items;
|
|
|
|
}
|
|
|
|
|
2019-10-17 17:03:43 +00:00
|
|
|
|
|
|
|
void BOARD::ClearAllNetCodes()
|
|
|
|
{
|
2020-03-10 23:05:34 +00:00
|
|
|
for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
|
2019-10-17 17:03:43 +00:00
|
|
|
item->SetNetCode( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::MapNets( const BOARD* aDestBoard )
|
|
|
|
{
|
2020-03-10 23:05:34 +00:00
|
|
|
for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
|
2019-10-17 17:03:43 +00:00
|
|
|
{
|
|
|
|
NETINFO_ITEM* netInfo = aDestBoard->FindNet( item->GetNetname() );
|
|
|
|
|
|
|
|
if( netInfo )
|
2020-07-31 15:46:45 +00:00
|
|
|
item->SetNet( netInfo );
|
2019-10-17 17:03:43 +00:00
|
|
|
else
|
|
|
|
item->SetNetCode( 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-23 17:21:16 +00:00
|
|
|
void BOARD::SanitizeNetcodes()
|
|
|
|
{
|
2019-10-17 17:03:43 +00:00
|
|
|
for ( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
|
2018-09-23 17:21:16 +00:00
|
|
|
{
|
|
|
|
if( FindNet( item->GetNetCode() ) == nullptr )
|
|
|
|
item->SetNetCode( NETINFO_LIST::ORPHANED );
|
|
|
|
}
|
|
|
|
}
|
2020-04-12 19:29:16 +00:00
|
|
|
|
2020-04-21 06:31:44 +00:00
|
|
|
|
2020-04-12 19:29:16 +00:00
|
|
|
void BOARD::AddListener( BOARD_LISTENER* aListener )
|
|
|
|
{
|
2020-09-26 13:42:40 +00:00
|
|
|
if( !alg::contains( m_listeners, aListener ) )
|
2020-04-12 19:29:16 +00:00
|
|
|
m_listeners.push_back( aListener );
|
|
|
|
}
|
|
|
|
|
2020-04-21 06:31:44 +00:00
|
|
|
|
2020-04-12 19:29:16 +00:00
|
|
|
void BOARD::RemoveListener( BOARD_LISTENER* aListener )
|
|
|
|
{
|
|
|
|
auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
|
|
|
|
|
|
|
|
if( i != m_listeners.end() )
|
|
|
|
{
|
|
|
|
std::iter_swap( i, m_listeners.end() - 1 );
|
|
|
|
m_listeners.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 06:31:44 +00:00
|
|
|
|
2020-04-12 19:29:16 +00:00
|
|
|
void BOARD::OnItemChanged( BOARD_ITEM* aItem )
|
|
|
|
{
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardItemChanged, *this, aItem );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::ResetNetHighLight()
|
|
|
|
{
|
|
|
|
m_highLight.Clear();
|
|
|
|
m_highLightPrevious.Clear();
|
|
|
|
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-24 17:30:23 +00:00
|
|
|
void BOARD::SetHighLightNet( int aNetCode, bool aMulti )
|
2020-04-12 19:29:16 +00:00
|
|
|
{
|
2020-05-24 17:30:23 +00:00
|
|
|
if( !m_highLight.m_netCodes.count( aNetCode ) )
|
2020-04-12 19:29:16 +00:00
|
|
|
{
|
2020-05-24 17:30:23 +00:00
|
|
|
if( !aMulti )
|
|
|
|
m_highLight.m_netCodes.clear();
|
|
|
|
|
|
|
|
m_highLight.m_netCodes.insert( aNetCode );
|
2020-04-12 19:29:16 +00:00
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BOARD::HighLightON( bool aValue )
|
|
|
|
{
|
|
|
|
if( m_highLight.m_highLightOn != aValue )
|
|
|
|
{
|
|
|
|
m_highLight.m_highLightOn = aValue;
|
|
|
|
InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
|
|
|
|
}
|
|
|
|
}
|
2020-08-11 19:37:07 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
wxString BOARD::GroupsSanityCheck( bool repair )
|
|
|
|
{
|
|
|
|
if( repair )
|
|
|
|
{
|
|
|
|
while( GroupsSanityCheckInternal( repair ) != wxEmptyString );
|
|
|
|
return wxEmptyString;
|
|
|
|
}
|
|
|
|
return GroupsSanityCheckInternal( repair );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString BOARD::GroupsSanityCheckInternal( bool repair )
|
|
|
|
{
|
|
|
|
// Cycle detection
|
|
|
|
//
|
|
|
|
// Each group has at most one parent group.
|
|
|
|
// So we start at group 0 and traverse the parent chain, marking groups seen along the way.
|
|
|
|
// If we ever see a group that we've already marked, that's a cycle.
|
|
|
|
// If we reach the end of the chain, we know all groups in that chain are not part of any cycle.
|
|
|
|
//
|
|
|
|
// Algorithm below is linear in the # of groups because each group is visited only once.
|
|
|
|
// There may be extra time taken due to the container access calls and iterators.
|
|
|
|
//
|
|
|
|
// Groups we know are cycle free
|
2020-09-25 17:37:03 +00:00
|
|
|
std::unordered_set<PCB_GROUP*> knownCycleFreeGroups;
|
2020-08-11 19:37:07 +00:00
|
|
|
// Groups in the current chain we're exploring.
|
2020-09-25 17:37:03 +00:00
|
|
|
std::unordered_set<PCB_GROUP*> currentChainGroups;
|
2020-08-11 19:37:07 +00:00
|
|
|
// Groups we haven't checked yet.
|
2020-09-25 17:37:03 +00:00
|
|
|
std::unordered_set<PCB_GROUP*> toCheckGroups;
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
// Initialize set of groups to check that could participate in a cycle.
|
2020-09-25 17:37:03 +00:00
|
|
|
for( PCB_GROUP* group : Groups() )
|
|
|
|
toCheckGroups.insert( group);
|
2020-08-11 19:37:07 +00:00
|
|
|
|
|
|
|
while( !toCheckGroups.empty() )
|
|
|
|
{
|
|
|
|
currentChainGroups.clear();
|
2020-09-25 17:37:03 +00:00
|
|
|
PCB_GROUP* group = *toCheckGroups.begin();
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
while( true )
|
|
|
|
{
|
2020-09-25 17:37:03 +00:00
|
|
|
if( currentChainGroups.find( group ) != currentChainGroups.end() )
|
2020-08-11 19:37:07 +00:00
|
|
|
{
|
|
|
|
if( repair )
|
2020-09-25 17:37:03 +00:00
|
|
|
Remove( group );
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-08-16 19:10:18 +00:00
|
|
|
return "Cycle detected in group membership";
|
2020-08-11 19:37:07 +00:00
|
|
|
}
|
2020-09-25 17:37:03 +00:00
|
|
|
else if( knownCycleFreeGroups.find( group ) != knownCycleFreeGroups.end() )
|
2020-08-11 19:37:07 +00:00
|
|
|
{
|
|
|
|
// Parent is a group we know does not lead to a cycle
|
|
|
|
break;
|
|
|
|
}
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-09-25 17:37:03 +00:00
|
|
|
currentChainGroups.insert( group );
|
2020-08-11 19:37:07 +00:00
|
|
|
// We haven't visited currIdx yet, so it must be in toCheckGroups
|
2020-09-25 17:37:03 +00:00
|
|
|
toCheckGroups.erase( group );
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-09-25 17:37:03 +00:00
|
|
|
group = group->GetParentGroup();
|
|
|
|
|
|
|
|
if( !group )
|
2020-08-11 19:37:07 +00:00
|
|
|
{
|
|
|
|
// end of chain and no cycles found in this chain
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-09-22 21:50:59 +00:00
|
|
|
// No cycles found in chain, so add it to set of groups we know don't participate
|
|
|
|
// in a cycle.
|
2020-08-11 19:37:07 +00:00
|
|
|
knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() );
|
|
|
|
}
|
2020-09-02 01:09:34 +00:00
|
|
|
|
2020-08-11 19:37:07 +00:00
|
|
|
// Success
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOARD::GroupLegalOpsField BOARD::GroupLegalOps( const PCBNEW_SELECTION& selection ) const
|
|
|
|
{
|
|
|
|
GroupLegalOpsField legalOps = { false, false, false, false, false, false };
|
|
|
|
|
|
|
|
std::unordered_set<const BOARD_ITEM*> allMembers;
|
2020-09-25 17:37:03 +00:00
|
|
|
|
2020-08-12 11:23:30 +00:00
|
|
|
for( const PCB_GROUP* grp : m_groups )
|
2020-08-11 19:37:07 +00:00
|
|
|
{
|
|
|
|
for( const BOARD_ITEM* member : grp->GetItems() )
|
2020-09-25 17:37:03 +00:00
|
|
|
allMembers.insert( member );
|
2020-08-11 19:37:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool hasGroup = ( SELECTION_CONDITIONS::HasType( PCB_GROUP_T ) )( selection );
|
|
|
|
// All elements of selection are groups, and no element is a descendant group of any other.
|
|
|
|
bool onlyGroups = ( SELECTION_CONDITIONS::OnlyType( PCB_GROUP_T ) )( selection );
|
|
|
|
// Any elements of the selections are already members of groups
|
|
|
|
bool anyGrouped = false;
|
|
|
|
// Any elements of the selections, except the first group, are already members of groups.
|
|
|
|
bool anyGroupedExceptFirst = false;
|
|
|
|
// All elements of the selections are already members of groups
|
|
|
|
bool allGrouped = true;
|
|
|
|
bool seenFirstGroup = false;
|
|
|
|
|
|
|
|
if( onlyGroups )
|
|
|
|
{
|
|
|
|
// Check that no groups are descendant subgroups of another group in the selection
|
|
|
|
for( EDA_ITEM* item : selection )
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
const PCB_GROUP* group = static_cast<const PCB_GROUP*>( item );
|
|
|
|
std::unordered_set<const PCB_GROUP*> subgroupos;
|
|
|
|
std::queue<const PCB_GROUP*> toCheck;
|
2020-08-11 19:37:07 +00:00
|
|
|
toCheck.push( group );
|
|
|
|
|
|
|
|
while( !toCheck.empty() )
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
const PCB_GROUP* candidate = toCheck.front();
|
2020-08-11 19:37:07 +00:00
|
|
|
toCheck.pop();
|
|
|
|
|
|
|
|
for( const BOARD_ITEM* aChild : candidate->GetItems() )
|
|
|
|
{
|
|
|
|
if( aChild->Type() == PCB_GROUP_T )
|
|
|
|
{
|
2020-08-12 11:23:30 +00:00
|
|
|
const PCB_GROUP* childGroup = static_cast<const PCB_GROUP*>( aChild );
|
2020-08-11 19:37:07 +00:00
|
|
|
subgroupos.insert( childGroup );
|
|
|
|
toCheck.push( childGroup );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( EDA_ITEM* otherItem : selection )
|
|
|
|
{
|
|
|
|
if( otherItem != item
|
2020-08-12 11:23:30 +00:00
|
|
|
&& subgroupos.find( static_cast<PCB_GROUP*>( otherItem ) ) != subgroupos.end() )
|
2020-08-11 19:37:07 +00:00
|
|
|
{
|
|
|
|
// otherItem is a descendant subgroup of item
|
|
|
|
onlyGroups = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for( EDA_ITEM* item : selection )
|
|
|
|
{
|
|
|
|
BOARD_ITEM* board_item = static_cast<BOARD_ITEM*>( item );
|
|
|
|
bool isFirstGroup = !seenFirstGroup && board_item->Type() == PCB_GROUP_T;
|
|
|
|
|
|
|
|
if( isFirstGroup )
|
|
|
|
{
|
|
|
|
seenFirstGroup = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( allMembers.find( board_item ) == allMembers.end() )
|
|
|
|
{
|
|
|
|
allGrouped = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
anyGrouped = true;
|
|
|
|
|
|
|
|
if( !isFirstGroup )
|
|
|
|
anyGroupedExceptFirst = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
legalOps.create = !anyGrouped;
|
|
|
|
legalOps.merge = hasGroup && !anyGroupedExceptFirst && ( selection.Size() > 1 );
|
|
|
|
legalOps.ungroup = onlyGroups;
|
|
|
|
legalOps.removeItems = allGrouped;
|
|
|
|
legalOps.flatten = onlyGroups;
|
|
|
|
legalOps.enter = onlyGroups && selection.Size() == 1;
|
|
|
|
return legalOps;
|
|
|
|
}
|