2011-10-13 19:56:32 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2017-03-20 12:05:38 +00:00
|
|
|
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2015-02-18 09:13:17 +00:00
|
|
|
* Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
2018-03-07 19:18:45 +00:00
|
|
|
* Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com>
|
2021-01-09 15:46:00 +00:00
|
|
|
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
2011-10-13 19:56:32 +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
|
|
|
|
*/
|
|
|
|
|
2020-11-18 01:21:04 +00:00
|
|
|
#include <core/mirror.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <confirm.h>
|
2019-01-29 10:50:50 +00:00
|
|
|
#include <refdes_utils.h>
|
2017-02-20 12:20:39 +00:00
|
|
|
#include <bitmaps.h>
|
2020-06-27 21:27:49 +00:00
|
|
|
#include <unordered_set>
|
2021-07-29 09:56:22 +00:00
|
|
|
#include <string_utils.h>
|
2018-01-29 20:58:58 +00:00
|
|
|
#include <pcb_edit_frame.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <board.h>
|
2021-06-06 19:03:10 +00:00
|
|
|
#include <board_design_settings.h>
|
2020-10-04 23:34:59 +00:00
|
|
|
#include <fp_shape.h>
|
2021-03-20 15:35:37 +00:00
|
|
|
#include <macros.h>
|
2021-06-06 19:03:10 +00:00
|
|
|
#include <pad.h>
|
2020-11-27 18:04:30 +00:00
|
|
|
#include <pcb_text.h>
|
2020-11-29 14:00:39 +00:00
|
|
|
#include <pcb_marker.h>
|
2020-12-01 14:03:34 +00:00
|
|
|
#include <pcb_group.h>
|
2021-06-11 21:07:02 +00:00
|
|
|
#include <pcb_track.h>
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <footprint.h>
|
2021-06-03 18:05:43 +00:00
|
|
|
#include <zone.h>
|
2016-12-02 17:58:12 +00:00
|
|
|
#include <view/view.h>
|
2020-08-26 22:04:16 +00:00
|
|
|
#include <geometry/shape_null.h>
|
2020-10-16 15:51:24 +00:00
|
|
|
#include <i18n_utility.h>
|
2021-07-14 20:03:32 +00:00
|
|
|
#include <convert_shape_list_to_polygon.h>
|
2020-11-29 14:00:39 +00:00
|
|
|
#include <geometry/convex_hull.h>
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT::FOOTPRINT( BOARD* parent ) :
|
2020-11-13 12:21:02 +00:00
|
|
|
BOARD_ITEM_CONTAINER((BOARD_ITEM*) parent, PCB_FOOTPRINT_T ),
|
2021-02-26 13:49:40 +00:00
|
|
|
m_boundingBoxCacheTimeStamp( 0 ),
|
|
|
|
m_visibleBBoxCacheTimeStamp( 0 ),
|
|
|
|
m_textExcludedBBoxCacheTimeStamp( 0 ),
|
|
|
|
m_hullCacheTimeStamp( 0 ),
|
2021-04-22 21:20:34 +00:00
|
|
|
m_initial_comments( nullptr )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
m_attributes = 0;
|
2020-11-14 14:29:11 +00:00
|
|
|
m_layer = F_Cu;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = 0;
|
2020-11-13 11:17:15 +00:00
|
|
|
m_fpStatus = FP_PADS_are_LOCKED;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_arflag = 0;
|
|
|
|
m_rot90Cost = m_rot180Cost = 0;
|
|
|
|
m_link = 0;
|
|
|
|
m_lastEditTime = 0;
|
|
|
|
m_localClearance = 0;
|
|
|
|
m_localSolderMaskMargin = 0;
|
|
|
|
m_localSolderPasteMargin = 0;
|
|
|
|
m_localSolderPasteMarginRatio = 0.0;
|
|
|
|
m_zoneConnection = ZONE_CONNECTION::INHERITED; // Use zone setting by default
|
|
|
|
m_thermalWidth = 0; // Use zone setting by default
|
|
|
|
m_thermalGap = 0; // Use zone setting by default
|
2008-11-24 06:53:43 +00:00
|
|
|
|
2014-09-10 15:18:42 +00:00
|
|
|
// These are special and mandatory text fields
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference = new FP_TEXT( this, FP_TEXT::TEXT_is_REFERENCE );
|
|
|
|
m_value = new FP_TEXT( this, FP_TEXT::TEXT_is_VALUE );
|
2008-11-24 06:53:43 +00:00
|
|
|
|
2016-07-19 17:35:25 +00:00
|
|
|
m_3D_Drawings.clear();
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
|
2020-10-14 23:37:26 +00:00
|
|
|
BOARD_ITEM_CONTAINER( aFootprint )
|
2012-01-14 19:50:32 +00:00
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
m_pos = aFootprint.m_pos;
|
|
|
|
m_fpid = aFootprint.m_fpid;
|
|
|
|
m_attributes = aFootprint.m_attributes;
|
2020-11-13 11:17:15 +00:00
|
|
|
m_fpStatus = aFootprint.m_fpStatus;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = aFootprint.m_orient;
|
|
|
|
m_rot90Cost = aFootprint.m_rot90Cost;
|
|
|
|
m_rot180Cost = aFootprint.m_rot180Cost;
|
|
|
|
m_lastEditTime = aFootprint.m_lastEditTime;
|
|
|
|
m_link = aFootprint.m_link;
|
|
|
|
m_path = aFootprint.m_path;
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedBoundingBox = aFootprint.m_cachedBoundingBox;
|
|
|
|
m_boundingBoxCacheTimeStamp = aFootprint.m_boundingBoxCacheTimeStamp;
|
|
|
|
m_cachedVisibleBBox = aFootprint.m_cachedVisibleBBox;
|
|
|
|
m_visibleBBoxCacheTimeStamp = aFootprint.m_visibleBBoxCacheTimeStamp;
|
|
|
|
m_cachedTextExcludedBBox = aFootprint.m_cachedTextExcludedBBox;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = aFootprint.m_textExcludedBBoxCacheTimeStamp;
|
|
|
|
m_cachedHull = aFootprint.m_cachedHull;
|
|
|
|
m_hullCacheTimeStamp = aFootprint.m_hullCacheTimeStamp;
|
|
|
|
|
|
|
|
m_localClearance = aFootprint.m_localClearance;
|
|
|
|
m_localSolderMaskMargin = aFootprint.m_localSolderMaskMargin;
|
|
|
|
m_localSolderPasteMargin = aFootprint.m_localSolderPasteMargin;
|
|
|
|
m_localSolderPasteMarginRatio = aFootprint.m_localSolderPasteMarginRatio;
|
|
|
|
m_zoneConnection = aFootprint.m_zoneConnection;
|
|
|
|
m_thermalWidth = aFootprint.m_thermalWidth;
|
|
|
|
m_thermalGap = aFootprint.m_thermalGap;
|
2012-01-14 19:50:32 +00:00
|
|
|
|
2020-12-08 23:43:39 +00:00
|
|
|
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
|
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
// Copy reference and value.
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference = new FP_TEXT( *aFootprint.m_reference );
|
|
|
|
m_reference->SetParent( this );
|
2020-12-08 23:43:39 +00:00
|
|
|
ptrMap[ aFootprint.m_reference ] = m_reference;
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_value = new FP_TEXT( *aFootprint.m_value );
|
|
|
|
m_value->SetParent( this );
|
2020-12-08 23:43:39 +00:00
|
|
|
ptrMap[ aFootprint.m_value ] = m_value;
|
2020-10-03 11:16:29 +00:00
|
|
|
|
|
|
|
// Copy pads
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : aFootprint.Pads() )
|
2020-10-03 11:16:29 +00:00
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
PAD* newPad = static_cast<PAD*>( pad->Clone() );
|
2020-10-03 11:16:29 +00:00
|
|
|
ptrMap[ pad ] = newPad;
|
2021-04-10 18:11:28 +00:00
|
|
|
Add( newPad, ADD_MODE::APPEND ); // Append to ensure indexes are identical
|
2020-10-03 11:16:29 +00:00
|
|
|
}
|
2012-01-14 19:50:32 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy zones
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* zone : aFootprint.Zones() )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
2020-11-11 23:05:59 +00:00
|
|
|
FP_ZONE* newZone = static_cast<FP_ZONE*>( zone->Clone() );
|
2020-10-03 11:16:29 +00:00
|
|
|
ptrMap[ zone ] = newZone;
|
2021-04-10 18:11:28 +00:00
|
|
|
Add( newZone, ADD_MODE::APPEND ); // Append to ensure indexes are identical
|
2019-10-29 10:09:54 +00:00
|
|
|
|
|
|
|
// Ensure the net info is OK and especially uses the net info list
|
|
|
|
// living in the current board
|
|
|
|
// Needed when copying a fp from fp editor that has its own board
|
|
|
|
// Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
|
2020-10-03 11:16:29 +00:00
|
|
|
newZone->SetNetCode( -1 );
|
2019-10-29 10:09:54 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy drawings
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* item : aFootprint.GraphicalItems() )
|
2012-01-14 19:50:32 +00:00
|
|
|
{
|
2020-10-03 11:16:29 +00:00
|
|
|
BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
|
|
|
|
ptrMap[ item ] = newItem;
|
2021-04-10 18:11:28 +00:00
|
|
|
Add( newItem, ADD_MODE::APPEND ); // Append to ensure indexes are identical
|
2020-10-03 11:16:29 +00:00
|
|
|
}
|
2012-01-14 19:50:32 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy groups
|
2020-10-14 23:37:26 +00:00
|
|
|
for( PCB_GROUP* group : aFootprint.Groups() )
|
2020-10-03 11:16:29 +00:00
|
|
|
{
|
|
|
|
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
|
2020-10-13 16:54:01 +00:00
|
|
|
ptrMap[ group ] = newGroup;
|
2021-04-10 18:11:28 +00:00
|
|
|
Add( newGroup, ADD_MODE::APPEND ); // Append to ensure indexes are identical
|
2020-10-13 16:54:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rebuild groups
|
2020-10-14 23:37:26 +00:00
|
|
|
for( PCB_GROUP* group : aFootprint.Groups() )
|
2020-10-13 16:54:01 +00:00
|
|
|
{
|
|
|
|
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( ptrMap[ group ] );
|
|
|
|
|
2020-12-20 18:59:07 +00:00
|
|
|
newGroup->GetItems().clear();
|
2020-10-03 11:16:29 +00:00
|
|
|
|
|
|
|
for( BOARD_ITEM* member : group->GetItems() )
|
2021-08-12 20:34:48 +00:00
|
|
|
{
|
|
|
|
if( ptrMap.count( member ) )
|
|
|
|
newGroup->AddItem( ptrMap[ member ] );
|
|
|
|
}
|
2012-01-14 19:50:32 +00:00
|
|
|
}
|
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
// Copy auxiliary data: 3D_Drawings info
|
2020-10-14 23:37:26 +00:00
|
|
|
m_3D_Drawings = aFootprint.m_3D_Drawings;
|
2012-01-14 19:50:32 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_doc = aFootprint.m_doc;
|
2020-12-04 12:14:07 +00:00
|
|
|
m_keywords = aFootprint.m_keywords;
|
2020-10-14 23:37:26 +00:00
|
|
|
m_properties = aFootprint.m_properties;
|
2013-01-02 21:49:56 +00:00
|
|
|
|
2015-03-23 08:28:12 +00:00
|
|
|
m_arflag = 0;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
m_initial_comments = aFootprint.m_initial_comments ?
|
|
|
|
new wxArrayString( *aFootprint.m_initial_comments ) : nullptr;
|
2012-01-14 19:50:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT::FOOTPRINT( FOOTPRINT&& aFootprint ) :
|
2020-10-14 23:37:26 +00:00
|
|
|
BOARD_ITEM_CONTAINER( aFootprint )
|
2020-07-16 01:11:51 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
*this = std::move( aFootprint );
|
2020-07-16 01:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT::~FOOTPRINT()
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2019-08-11 20:30:33 +00:00
|
|
|
// Clean up the owned elements
|
2020-11-13 02:57:11 +00:00
|
|
|
delete m_reference;
|
|
|
|
delete m_value;
|
2013-06-23 19:18:33 +00:00
|
|
|
delete m_initial_comments;
|
2019-08-11 20:30:33 +00:00
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* p : m_pads )
|
2019-08-11 20:30:33 +00:00
|
|
|
delete p;
|
|
|
|
|
|
|
|
m_pads.clear();
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
2020-10-03 11:16:29 +00:00
|
|
|
delete zone;
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2019-10-26 15:49:29 +00:00
|
|
|
m_fp_zones.clear();
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
for( PCB_GROUP* group : m_fp_groups )
|
|
|
|
delete group;
|
|
|
|
|
|
|
|
m_fp_groups.clear();
|
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* d : m_drawings )
|
2019-08-11 20:30:33 +00:00
|
|
|
delete d;
|
|
|
|
|
|
|
|
m_drawings.clear();
|
2007-06-05 12:10:51 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2021-08-04 10:37:52 +00:00
|
|
|
bool FOOTPRINT::FixUuids()
|
|
|
|
{
|
|
|
|
// replace null UUIDs if any by a valid uuid
|
|
|
|
std::vector< BOARD_ITEM* > item_list;
|
|
|
|
|
|
|
|
item_list.push_back( m_reference );
|
|
|
|
item_list.push_back( m_value );
|
|
|
|
|
|
|
|
for( PAD* pad : m_pads )
|
|
|
|
item_list.push_back( pad );
|
|
|
|
|
|
|
|
for( BOARD_ITEM* gr_item : m_drawings )
|
|
|
|
item_list.push_back( gr_item );
|
|
|
|
|
|
|
|
// Note: one cannot fix null UUIDs inside the group, but it should not happen
|
|
|
|
// because null uuids can be found in old footprints, therefore without group
|
|
|
|
for( PCB_GROUP* group : m_fp_groups )
|
|
|
|
item_list.push_back( group );
|
|
|
|
|
|
|
|
// Probably notneeded, because old fp do not have zones. But just in case.
|
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
|
|
|
item_list.push_back( zone );
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : item_list )
|
|
|
|
{
|
|
|
|
if( item->m_Uuid == niluuid )
|
|
|
|
{
|
|
|
|
const_cast<KIID&>( item->m_Uuid ) = KIID();
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT& FOOTPRINT::operator=( FOOTPRINT&& aOther )
|
2020-07-16 01:11:51 +00:00
|
|
|
{
|
|
|
|
BOARD_ITEM::operator=( aOther );
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_pos = aOther.m_pos;
|
2020-07-16 01:11:51 +00:00
|
|
|
m_fpid = aOther.m_fpid;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_attributes = aOther.m_attributes;
|
2020-11-13 11:17:15 +00:00
|
|
|
m_fpStatus = aOther.m_fpStatus;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = aOther.m_orient;
|
|
|
|
m_rot90Cost = aOther.m_rot90Cost;
|
|
|
|
m_rot180Cost = aOther.m_rot180Cost;
|
|
|
|
m_lastEditTime = aOther.m_lastEditTime;
|
|
|
|
m_link = aOther.m_link;
|
|
|
|
m_path = aOther.m_path;
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedBoundingBox = aOther.m_cachedBoundingBox;
|
|
|
|
m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
|
|
|
|
m_cachedVisibleBBox = aOther.m_cachedVisibleBBox;
|
|
|
|
m_visibleBBoxCacheTimeStamp = aOther.m_visibleBBoxCacheTimeStamp;
|
|
|
|
m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
|
|
|
|
m_cachedHull = aOther.m_cachedHull;
|
|
|
|
m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
|
|
|
|
|
|
|
|
m_localClearance = aOther.m_localClearance;
|
|
|
|
m_localSolderMaskMargin = aOther.m_localSolderMaskMargin;
|
|
|
|
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
|
|
|
|
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
|
|
|
|
m_zoneConnection = aOther.m_zoneConnection;
|
|
|
|
m_thermalWidth = aOther.m_thermalWidth;
|
|
|
|
m_thermalGap = aOther.m_thermalGap;
|
2020-07-16 01:11:51 +00:00
|
|
|
|
|
|
|
// Move reference and value
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference = aOther.m_reference;
|
|
|
|
m_reference->SetParent( this );
|
|
|
|
m_value = aOther.m_value;
|
|
|
|
m_value->SetParent( this );
|
2020-07-16 01:11:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Move the pads
|
|
|
|
m_pads.clear();
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : aOther.Pads() )
|
2020-07-16 01:11:51 +00:00
|
|
|
Add( pad );
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
aOther.Pads().clear();
|
|
|
|
|
2020-07-16 01:11:51 +00:00
|
|
|
// Move the zones
|
|
|
|
m_fp_zones.clear();
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* item : aOther.Zones() )
|
2020-07-16 01:11:51 +00:00
|
|
|
{
|
2020-10-03 11:16:29 +00:00
|
|
|
Add( item );
|
2020-07-16 01:11:51 +00:00
|
|
|
|
|
|
|
// Ensure the net info is OK and especially uses the net info list
|
|
|
|
// living in the current board
|
|
|
|
// Needed when copying a fp from fp editor that has its own board
|
|
|
|
// Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
|
|
|
|
item->SetNetCode( -1 );
|
|
|
|
}
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
aOther.Zones().clear();
|
|
|
|
|
2020-07-16 01:11:51 +00:00
|
|
|
// Move the drawings
|
|
|
|
m_drawings.clear();
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : aOther.GraphicalItems() )
|
2020-10-03 11:16:29 +00:00
|
|
|
Add( item );
|
2020-07-16 01:11:51 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
aOther.GraphicalItems().clear();
|
|
|
|
|
|
|
|
// Move the groups
|
|
|
|
m_fp_groups.clear();
|
|
|
|
|
|
|
|
for( PCB_GROUP* group : aOther.Groups() )
|
|
|
|
Add( group );
|
|
|
|
|
|
|
|
aOther.Groups().clear();
|
2020-07-16 01:11:51 +00:00
|
|
|
|
|
|
|
// Copy auxiliary data: 3D_Drawings info
|
|
|
|
m_3D_Drawings.clear();
|
|
|
|
m_3D_Drawings = aOther.m_3D_Drawings;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_doc = aOther.m_doc;
|
2020-12-20 18:50:45 +00:00
|
|
|
m_keywords = aOther.m_keywords;
|
2020-08-19 19:21:22 +00:00
|
|
|
m_properties = aOther.m_properties;
|
2020-07-16 01:11:51 +00:00
|
|
|
|
|
|
|
m_initial_comments = aOther.m_initial_comments;
|
|
|
|
|
|
|
|
// Clear the other item's containers since this is a move
|
|
|
|
aOther.Pads().clear();
|
|
|
|
aOther.Zones().clear();
|
|
|
|
aOther.GraphicalItems().clear();
|
2020-11-13 02:57:11 +00:00
|
|
|
aOther.m_value = nullptr;
|
|
|
|
aOther.m_reference = nullptr;
|
2020-07-16 01:11:51 +00:00
|
|
|
aOther.m_initial_comments = nullptr;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT& FOOTPRINT::operator=( const FOOTPRINT& aOther )
|
2016-05-31 08:27:52 +00:00
|
|
|
{
|
|
|
|
BOARD_ITEM::operator=( aOther );
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_pos = aOther.m_pos;
|
2016-05-31 08:27:52 +00:00
|
|
|
m_fpid = aOther.m_fpid;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_attributes = aOther.m_attributes;
|
2020-11-13 11:17:15 +00:00
|
|
|
m_fpStatus = aOther.m_fpStatus;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = aOther.m_orient;
|
|
|
|
m_rot90Cost = aOther.m_rot90Cost;
|
|
|
|
m_rot180Cost = aOther.m_rot180Cost;
|
|
|
|
m_lastEditTime = aOther.m_lastEditTime;
|
|
|
|
m_link = aOther.m_link;
|
|
|
|
m_path = aOther.m_path;
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedBoundingBox = aOther.m_cachedBoundingBox;
|
|
|
|
m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
|
|
|
|
m_cachedVisibleBBox = aOther.m_cachedVisibleBBox;
|
|
|
|
m_visibleBBoxCacheTimeStamp = aOther.m_visibleBBoxCacheTimeStamp;
|
|
|
|
m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
|
|
|
|
m_cachedHull = aOther.m_cachedHull;
|
|
|
|
m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
|
|
|
|
|
|
|
|
m_localClearance = aOther.m_localClearance;
|
|
|
|
m_localSolderMaskMargin = aOther.m_localSolderMaskMargin;
|
|
|
|
m_localSolderPasteMargin = aOther.m_localSolderPasteMargin;
|
|
|
|
m_localSolderPasteMarginRatio = aOther.m_localSolderPasteMarginRatio;
|
|
|
|
m_zoneConnection = aOther.m_zoneConnection;
|
|
|
|
m_thermalWidth = aOther.m_thermalWidth;
|
|
|
|
m_thermalGap = aOther.m_thermalGap;
|
2016-05-31 08:27:52 +00:00
|
|
|
|
|
|
|
// Copy reference and value
|
2020-11-13 02:57:11 +00:00
|
|
|
*m_reference = *aOther.m_reference;
|
|
|
|
m_reference->SetParent( this );
|
|
|
|
*m_value = *aOther.m_value;
|
|
|
|
m_value->SetParent( this );
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
|
|
|
|
|
|
|
|
// Copy pads
|
2019-06-01 23:23:36 +00:00
|
|
|
m_pads.clear();
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : aOther.Pads() )
|
2020-10-03 11:16:29 +00:00
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
PAD* newPad = new PAD( *pad );
|
2020-10-03 11:16:29 +00:00
|
|
|
ptrMap[ pad ] = newPad;
|
|
|
|
Add( newPad );
|
|
|
|
}
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy zones
|
2019-10-26 15:49:29 +00:00
|
|
|
m_fp_zones.clear();
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* zone : aOther.Zones() )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
2020-11-11 23:05:59 +00:00
|
|
|
FP_ZONE* newZone = static_cast<FP_ZONE*>( zone->Clone() );
|
2020-10-03 11:16:29 +00:00
|
|
|
ptrMap[ zone ] = newZone;
|
|
|
|
Add( newZone );
|
2019-10-29 10:09:54 +00:00
|
|
|
|
|
|
|
// Ensure the net info is OK and especially uses the net info list
|
|
|
|
// living in the current board
|
|
|
|
// Needed when copying a fp from fp editor that has its own board
|
|
|
|
// Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
|
2020-10-03 11:16:29 +00:00
|
|
|
newZone->SetNetCode( -1 );
|
2019-10-29 10:09:54 +00:00
|
|
|
}
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy drawings
|
2019-06-02 03:55:32 +00:00
|
|
|
m_drawings.clear();
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* item : aOther.GraphicalItems() )
|
2016-05-31 08:27:52 +00:00
|
|
|
{
|
2020-10-03 11:16:29 +00:00
|
|
|
BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
|
|
|
|
ptrMap[ item ] = newItem;
|
|
|
|
Add( newItem );
|
|
|
|
}
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Copy groups
|
|
|
|
m_fp_groups.clear();
|
|
|
|
|
|
|
|
for( PCB_GROUP* group : aOther.Groups() )
|
|
|
|
{
|
|
|
|
PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
|
2021-03-06 09:27:41 +00:00
|
|
|
newGroup->GetItems().clear();
|
2020-10-03 11:16:29 +00:00
|
|
|
|
|
|
|
for( BOARD_ITEM* member : group->GetItems() )
|
|
|
|
newGroup->AddItem( ptrMap[ member ] );
|
|
|
|
|
|
|
|
Add( newGroup );
|
2016-05-31 08:27:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Copy auxiliary data: 3D_Drawings info
|
|
|
|
m_3D_Drawings.clear();
|
|
|
|
m_3D_Drawings = aOther.m_3D_Drawings;
|
2020-11-13 02:57:11 +00:00
|
|
|
m_doc = aOther.m_doc;
|
2021-03-06 09:27:41 +00:00
|
|
|
m_keywords = aOther.m_keywords;
|
2020-08-19 19:21:22 +00:00
|
|
|
m_properties = aOther.m_properties;
|
2016-05-31 08:27:52 +00:00
|
|
|
|
2020-07-16 01:11:51 +00:00
|
|
|
m_initial_comments = aOther.m_initial_comments ?
|
|
|
|
new wxArrayString( *aOther.m_initial_comments ) : nullptr;
|
|
|
|
|
2016-05-31 08:27:52 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::GetContextualTextVars( wxArrayString* aVars ) const
|
2020-05-27 22:28:36 +00:00
|
|
|
{
|
|
|
|
aVars->push_back( wxT( "REFERENCE" ) );
|
|
|
|
aVars->push_back( wxT( "VALUE" ) );
|
|
|
|
aVars->push_back( wxT( "LAYER" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::ResolveTextVar( wxString* token, int aDepth ) const
|
2020-04-06 13:06:57 +00:00
|
|
|
{
|
|
|
|
if( token->IsSameAs( wxT( "REFERENCE" ) ) )
|
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
*token = m_reference->GetShownText( aDepth + 1 );
|
2020-04-06 13:06:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( token->IsSameAs( wxT( "VALUE" ) ) )
|
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
*token = m_value->GetShownText( aDepth + 1 );
|
2020-04-06 13:06:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if( token->IsSameAs( wxT( "LAYER" ) ) )
|
|
|
|
{
|
|
|
|
*token = GetLayerName();
|
|
|
|
return true;
|
|
|
|
}
|
2020-08-08 21:47:57 +00:00
|
|
|
else if( m_properties.count( *token ) )
|
|
|
|
{
|
|
|
|
*token = m_properties.at( *token );
|
|
|
|
return true;
|
|
|
|
}
|
2020-04-06 13:06:57 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::ClearAllNets()
|
2015-02-18 09:13:17 +00:00
|
|
|
{
|
|
|
|
// Force the ORPHANED dummy net info for all pads.
|
|
|
|
// ORPHANED dummy net does not depend on a board
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2016-01-29 10:29:56 +00:00
|
|
|
pad->SetNetCode( NETINFO_LIST::ORPHANED );
|
2015-02-18 09:13:17 +00:00
|
|
|
}
|
|
|
|
|
2010-07-20 10:30:40 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
|
2014-07-09 12:22:29 +00:00
|
|
|
{
|
|
|
|
switch( aBoardItem->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2019-12-10 13:49:28 +00:00
|
|
|
// Only user text can be added this way.
|
2021-08-18 13:34:32 +00:00
|
|
|
wxASSERT( static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS );
|
2020-04-24 23:44:09 +00:00
|
|
|
KI_FALLTHROUGH;
|
2014-07-09 12:22:29 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2019-06-02 03:55:32 +00:00
|
|
|
m_drawings.push_back( aBoardItem );
|
2014-07-09 12:22:29 +00:00
|
|
|
else
|
2019-06-02 03:55:32 +00:00
|
|
|
m_drawings.push_front( aBoardItem );
|
2014-07-09 12:22:29 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_PAD_T:
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2020-11-12 22:30:02 +00:00
|
|
|
m_pads.push_back( static_cast<PAD*>( aBoardItem ) );
|
2014-07-09 12:22:29 +00:00
|
|
|
else
|
2020-11-12 22:30:02 +00:00
|
|
|
m_pads.push_front( static_cast<PAD*>( aBoardItem ) );
|
2014-07-09 12:22:29 +00:00
|
|
|
break;
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
case PCB_FP_ZONE_T:
|
2019-12-28 00:55:11 +00:00
|
|
|
if( aMode == ADD_MODE::APPEND )
|
2020-11-11 23:05:59 +00:00
|
|
|
m_fp_zones.push_back( static_cast<FP_ZONE*>( aBoardItem ) );
|
2019-10-29 10:09:54 +00:00
|
|
|
else
|
2020-11-11 23:05:59 +00:00
|
|
|
m_fp_zones.insert( m_fp_zones.begin(), static_cast<FP_ZONE*>( aBoardItem ) );
|
2019-10-29 10:09:54 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
if( aMode == ADD_MODE::APPEND )
|
|
|
|
m_fp_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
|
|
|
|
else
|
|
|
|
m_fp_groups.insert( m_fp_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
|
|
|
|
break;
|
|
|
|
|
2014-07-09 12:22:29 +00:00
|
|
|
default:
|
2014-11-15 19:06:05 +00:00
|
|
|
{
|
|
|
|
wxString msg;
|
2020-11-13 21:07:01 +00:00
|
|
|
msg.Printf( wxT( "FOOTPRINT::Add() needs work: BOARD_ITEM type (%d) not handled" ),
|
2014-11-15 19:06:05 +00:00
|
|
|
aBoardItem->Type() );
|
|
|
|
wxFAIL_MSG( msg );
|
2014-07-09 12:22:29 +00:00
|
|
|
|
2014-11-15 19:06:05 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-07-09 12:22:29 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 19:50:42 +00:00
|
|
|
aBoardItem->ClearEditFlags();
|
2014-07-09 12:22:29 +00:00
|
|
|
aBoardItem->SetParent( this );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-07 23:29:30 +00:00
|
|
|
void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
|
2014-07-09 12:22:29 +00:00
|
|
|
{
|
|
|
|
switch( aBoardItem->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2019-12-10 13:49:28 +00:00
|
|
|
// Only user text can be removed this way.
|
2019-06-11 14:47:54 +00:00
|
|
|
wxCHECK_RET(
|
2020-10-04 23:34:59 +00:00
|
|
|
static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS,
|
2019-06-11 14:47:54 +00:00
|
|
|
"Please report this bug: Invalid remove operation on required text" );
|
2020-04-24 23:44:09 +00:00
|
|
|
KI_FALLTHROUGH;
|
2014-07-09 12:22:29 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2019-06-11 14:47:54 +00:00
|
|
|
for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
|
|
|
|
{
|
|
|
|
if( *it == aBoardItem )
|
|
|
|
{
|
|
|
|
m_drawings.erase( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-13 15:31:54 +00:00
|
|
|
break;
|
2014-07-09 12:22:29 +00:00
|
|
|
|
|
|
|
case PCB_PAD_T:
|
2019-06-11 14:47:54 +00:00
|
|
|
for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
|
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
if( *it == static_cast<PAD*>( aBoardItem ) )
|
2019-06-11 14:47:54 +00:00
|
|
|
{
|
|
|
|
m_pads.erase( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-13 15:31:54 +00:00
|
|
|
break;
|
2014-07-09 12:22:29 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
case PCB_FP_ZONE_T:
|
2019-10-26 15:49:29 +00:00
|
|
|
for( auto it = m_fp_zones.begin(); it != m_fp_zones.end(); ++it )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
2020-11-11 23:05:59 +00:00
|
|
|
if( *it == static_cast<FP_ZONE*>( aBoardItem ) )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
2019-10-26 15:49:29 +00:00
|
|
|
m_fp_zones.erase( it );
|
2019-10-29 10:09:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
for( auto it = m_fp_groups.begin(); it != m_fp_groups.end(); ++it )
|
|
|
|
{
|
|
|
|
if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
|
|
|
|
{
|
|
|
|
m_fp_groups.erase( it );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2014-07-09 12:22:29 +00:00
|
|
|
default:
|
2014-11-15 19:06:05 +00:00
|
|
|
{
|
|
|
|
wxString msg;
|
2020-11-13 21:07:01 +00:00
|
|
|
msg.Printf( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
|
2014-11-15 19:06:05 +00:00
|
|
|
aBoardItem->Type() );
|
|
|
|
wxFAIL_MSG( msg );
|
|
|
|
}
|
2014-07-09 12:22:29 +00:00
|
|
|
}
|
2021-02-02 11:57:48 +00:00
|
|
|
|
2021-02-15 13:49:40 +00:00
|
|
|
aBoardItem->SetFlags( STRUCT_DELETED );
|
|
|
|
|
|
|
|
PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
|
|
|
|
|
|
|
|
if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
|
|
|
|
parentGroup->RemoveItem( aBoardItem );
|
2018-09-13 21:29:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
double FOOTPRINT::GetArea( int aPadding ) const
|
2018-09-13 21:29:52 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
EDA_RECT bbox = GetBoundingBox( false, false );
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
|
|
|
|
double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
|
|
|
|
return w * h;
|
2010-12-10 19:47:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-24 16:15:54 +00:00
|
|
|
int FOOTPRINT::GetLikelyAttribute() const
|
|
|
|
{
|
|
|
|
int smd_count = 0;
|
|
|
|
int tht_count = 0;
|
|
|
|
|
|
|
|
for( PAD* pad : m_pads )
|
|
|
|
{
|
2021-11-29 16:44:45 +00:00
|
|
|
switch( pad->GetProperty() )
|
|
|
|
{
|
|
|
|
case PAD_PROP::FIDUCIAL_GLBL:
|
|
|
|
case PAD_PROP::FIDUCIAL_LOCAL:
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case PAD_PROP::HEATSINK:
|
|
|
|
case PAD_PROP::CASTELLATED:
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case PAD_PROP::NONE:
|
|
|
|
case PAD_PROP::BGA:
|
|
|
|
case PAD_PROP::TESTPOINT:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-09-24 16:15:54 +00:00
|
|
|
switch( pad->GetAttribute() )
|
|
|
|
{
|
|
|
|
case PAD_ATTRIB::PTH:
|
|
|
|
tht_count++;
|
|
|
|
break;
|
2021-11-29 16:44:45 +00:00
|
|
|
|
2021-09-24 16:15:54 +00:00
|
|
|
case PAD_ATTRIB::SMD:
|
|
|
|
smd_count++;
|
|
|
|
break;
|
2021-11-29 16:44:45 +00:00
|
|
|
|
2021-09-24 16:15:54 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( tht_count > 0 )
|
|
|
|
return FP_THROUGH_HOLE;
|
|
|
|
|
|
|
|
if( smd_count > 0 )
|
|
|
|
return FP_SMD;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString FOOTPRINT::GetTypeName() const
|
|
|
|
{
|
|
|
|
if( ( m_attributes & FP_SMD ) == FP_SMD )
|
|
|
|
return _( "SMD" );
|
|
|
|
|
|
|
|
if( ( m_attributes & FP_THROUGH_HOLE ) == FP_THROUGH_HOLE )
|
|
|
|
return _( "Through hole" );
|
|
|
|
|
|
|
|
return _( "Other" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
EDA_RECT FOOTPRINT::GetFpPadsLocalBbox() const
|
2019-11-01 12:35:42 +00:00
|
|
|
{
|
|
|
|
EDA_RECT area;
|
|
|
|
|
|
|
|
// We want the bounding box of the footprint pads at rot 0, not flipped
|
|
|
|
// Create such a image:
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT dummy( *this );
|
2019-11-01 12:35:42 +00:00
|
|
|
|
|
|
|
dummy.SetPosition( wxPoint( 0, 0 ) );
|
2020-10-14 23:37:26 +00:00
|
|
|
|
2019-11-01 12:35:42 +00:00
|
|
|
if( dummy.IsFlipped() )
|
|
|
|
dummy.Flip( wxPoint( 0, 0 ) , false );
|
|
|
|
|
|
|
|
if( dummy.GetOrientation() )
|
|
|
|
dummy.SetOrientation( 0 );
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : dummy.Pads() )
|
2019-11-01 12:35:42 +00:00
|
|
|
area.Merge( pad->GetBoundingBox() );
|
|
|
|
|
|
|
|
return area;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
const EDA_RECT FOOTPRINT::GetBoundingBox() const
|
2008-03-13 10:08:42 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
return GetBoundingBox( true, true );
|
2008-03-13 10:08:42 +00:00
|
|
|
}
|
|
|
|
|
2008-03-15 10:24:32 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisibleText ) const
|
2019-08-20 11:58:14 +00:00
|
|
|
{
|
2021-08-16 09:53:27 +00:00
|
|
|
const BOARD* board = GetBoard();
|
2021-02-26 13:49:40 +00:00
|
|
|
|
|
|
|
if( board )
|
|
|
|
{
|
|
|
|
if( aIncludeText && aIncludeInvisibleText )
|
|
|
|
{
|
|
|
|
if( m_boundingBoxCacheTimeStamp >= board->GetTimeStamp() )
|
|
|
|
return m_cachedBoundingBox;
|
|
|
|
}
|
|
|
|
else if( aIncludeText )
|
|
|
|
{
|
|
|
|
if( m_visibleBBoxCacheTimeStamp >= board->GetTimeStamp() )
|
|
|
|
return m_cachedVisibleBBox;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( m_textExcludedBBoxCacheTimeStamp >= board->GetTimeStamp() )
|
|
|
|
return m_cachedTextExcludedBBox;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EDA_RECT area;
|
|
|
|
|
|
|
|
area.SetOrigin( m_pos );
|
|
|
|
area.SetEnd( m_pos );
|
|
|
|
area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area
|
2019-08-20 11:58:14 +00:00
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2019-08-20 11:58:14 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
if( item->Type() == PCB_FP_SHAPE_T )
|
2019-08-20 11:58:14 +00:00
|
|
|
area.Merge( item->GetBoundingBox() );
|
|
|
|
}
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
for( PAD* pad : m_pads )
|
|
|
|
area.Merge( pad->GetBoundingBox() );
|
2020-09-16 05:04:48 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
|
|
|
area.Merge( zone->GetBoundingBox() );
|
|
|
|
|
2021-06-23 22:58:58 +00:00
|
|
|
bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_fp_zones.empty() );
|
2021-02-26 13:49:40 +00:00
|
|
|
|
2021-06-23 22:58:58 +00:00
|
|
|
// Groups do not contribute to the rect, only their members
|
|
|
|
if( aIncludeText || noDrawItems )
|
2020-09-16 05:04:48 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
|
|
|
{
|
|
|
|
if( item->Type() == PCB_FP_TEXT_T )
|
|
|
|
area.Merge( item->GetBoundingBox() );
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can be further optimized when aIncludeInvisibleText is true, but currently
|
|
|
|
// leaving this as is until it's determined there is a noticeable speed hit.
|
|
|
|
bool valueLayerIsVisible = true;
|
|
|
|
bool refLayerIsVisible = true;
|
|
|
|
|
|
|
|
if( board )
|
|
|
|
{
|
|
|
|
// The first "&&" conditional handles the user turning layers off as well as layers
|
|
|
|
// not being present in the current PCB stackup. Values, references, and all
|
|
|
|
// footprint text can also be turned off via the GAL meta-layers, so the 2nd and
|
|
|
|
// 3rd "&&" conditionals handle that.
|
|
|
|
valueLayerIsVisible = board->IsLayerVisible( m_value->GetLayer() )
|
|
|
|
&& board->IsElementVisible( LAYER_MOD_VALUES )
|
2021-10-22 20:12:57 +00:00
|
|
|
&& board->IsElementVisible( LAYER_MOD_TEXT );
|
2021-02-26 13:49:40 +00:00
|
|
|
|
|
|
|
refLayerIsVisible = board->IsLayerVisible( m_reference->GetLayer() )
|
|
|
|
&& board->IsElementVisible( LAYER_MOD_REFERENCES )
|
2021-10-22 20:12:57 +00:00
|
|
|
&& board->IsElementVisible( LAYER_MOD_TEXT );
|
2021-02-26 13:49:40 +00:00
|
|
|
}
|
2020-09-16 05:04:48 +00:00
|
|
|
|
|
|
|
|
2021-06-23 22:58:58 +00:00
|
|
|
if( ( m_value->IsVisible() && valueLayerIsVisible )
|
|
|
|
|| aIncludeInvisibleText || noDrawItems )
|
2021-02-26 13:49:40 +00:00
|
|
|
area.Merge( m_value->GetBoundingBox() );
|
|
|
|
|
2021-06-23 22:58:58 +00:00
|
|
|
if( ( m_reference->IsVisible() && refLayerIsVisible )
|
|
|
|
|| aIncludeInvisibleText || noDrawItems )
|
2021-02-26 13:49:40 +00:00
|
|
|
area.Merge( m_reference->GetBoundingBox() );
|
|
|
|
}
|
2019-08-20 11:58:14 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
if( board )
|
|
|
|
{
|
2021-06-23 22:58:58 +00:00
|
|
|
if( ( aIncludeText && aIncludeInvisibleText ) || noDrawItems )
|
2021-02-26 13:49:40 +00:00
|
|
|
{
|
|
|
|
m_boundingBoxCacheTimeStamp = board->GetTimeStamp();
|
|
|
|
m_cachedBoundingBox = area;
|
|
|
|
}
|
|
|
|
else if( aIncludeText )
|
|
|
|
{
|
|
|
|
m_visibleBBoxCacheTimeStamp = board->GetTimeStamp();
|
|
|
|
m_cachedVisibleBBox = area;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = board->GetTimeStamp();
|
|
|
|
m_cachedTextExcludedBBox = area;
|
|
|
|
}
|
|
|
|
}
|
2019-08-20 11:58:14 +00:00
|
|
|
|
|
|
|
return area;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
|
2021-01-31 14:32:14 +00:00
|
|
|
{
|
2021-08-16 09:53:27 +00:00
|
|
|
const BOARD* board = GetBoard();
|
2021-02-26 13:49:40 +00:00
|
|
|
|
|
|
|
if( board )
|
|
|
|
{
|
|
|
|
if( m_hullCacheTimeStamp >= board->GetTimeStamp() )
|
|
|
|
return m_cachedHull;
|
|
|
|
}
|
2021-01-31 14:32:14 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
SHAPE_POLY_SET rawPolys;
|
2021-01-31 14:32:14 +00:00
|
|
|
SHAPE_POLY_SET hull;
|
2018-02-19 00:00:29 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
|
|
|
{
|
|
|
|
if( item->Type() == PCB_FP_SHAPE_T )
|
|
|
|
{
|
|
|
|
item->TransformShapeWithClearanceToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
|
|
|
|
ERROR_OUTSIDE );
|
|
|
|
}
|
2018-02-22 01:39:46 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
// We intentionally exclude footprint text from the bounding hull.
|
|
|
|
}
|
2018-02-19 00:00:29 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
for( PAD* pad : m_pads )
|
|
|
|
{
|
|
|
|
pad->TransformShapeWithClearanceToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
|
|
|
|
ERROR_OUTSIDE );
|
2021-09-26 22:17:50 +00:00
|
|
|
// In case hole is larger than pad
|
|
|
|
pad->TransformHoleWithClearanceToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
|
2020-11-29 14:00:39 +00:00
|
|
|
}
|
2018-02-22 01:39:46 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
2018-02-22 01:39:46 +00:00
|
|
|
{
|
2020-11-29 14:00:39 +00:00
|
|
|
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
|
|
|
{
|
|
|
|
SHAPE_POLY_SET layerPoly = zone->GetFilledPolysList( layer );
|
|
|
|
|
|
|
|
for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
|
|
|
|
{
|
|
|
|
const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
|
|
|
|
rawPolys.AddOutline( poly );
|
|
|
|
}
|
|
|
|
}
|
2018-02-22 01:39:46 +00:00
|
|
|
}
|
2018-02-19 00:00:29 +00:00
|
|
|
|
2021-01-09 15:46:00 +00:00
|
|
|
// If there are some graphic items, build the actual hull.
|
|
|
|
// However if no items, create a minimal polygon (can happen if a footprint
|
|
|
|
// is created with no item: it contains only 2 texts.
|
|
|
|
if( rawPolys.OutlineCount() == 0 )
|
|
|
|
{
|
|
|
|
// generate a small dummy rectangular outline around the anchor
|
2021-06-23 22:58:58 +00:00
|
|
|
const int halfsize = Millimeter2iu( 1.0 );
|
2021-01-09 15:46:00 +00:00
|
|
|
|
|
|
|
rawPolys.NewOutline();
|
2021-06-23 22:58:58 +00:00
|
|
|
|
2021-01-09 15:46:00 +00:00
|
|
|
// add a square:
|
|
|
|
rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
|
|
|
|
rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
|
|
|
|
rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
|
|
|
|
rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
|
|
|
|
}
|
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
std::vector<wxPoint> convex_hull;
|
|
|
|
BuildConvexHull( convex_hull, rawPolys );
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedHull.RemoveAllContours();
|
|
|
|
m_cachedHull.NewOutline();
|
2020-11-29 14:00:39 +00:00
|
|
|
|
|
|
|
for( const wxPoint& pt : convex_hull )
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedHull.Append( pt );
|
2021-01-31 14:32:14 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
if( board )
|
|
|
|
m_hullCacheTimeStamp = board->GetTimeStamp();
|
2021-01-31 14:32:14 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
return m_cachedHull;
|
2021-01-31 14:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
|
2007-06-05 12:10:51 +00:00
|
|
|
{
|
2020-05-22 10:31:14 +00:00
|
|
|
wxString msg, msg2;
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( m_reference->GetShownText(), m_value->GetShownText() );
|
2010-12-10 19:47:44 +00:00
|
|
|
|
2020-05-22 10:31:14 +00:00
|
|
|
if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
|
|
|
|
|| aFrame->IsType( FRAME_FOOTPRINT_VIEWER_MODAL )
|
|
|
|
|| aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
|
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
wxDateTime date( static_cast<time_t>( m_lastEditTime ) );
|
2009-10-11 13:04:47 +00:00
|
|
|
|
2020-05-22 10:31:14 +00:00
|
|
|
// Date format: see http://www.cplusplus.com/reference/ctime/strftime
|
2020-11-13 02:57:11 +00:00
|
|
|
if( m_lastEditTime && date.IsValid() )
|
2020-05-22 10:31:14 +00:00
|
|
|
msg = date.Format( wxT( "%b %d, %Y" ) ); // Abbreviated_month_name Day, Year
|
|
|
|
else
|
|
|
|
msg = _( "Unknown" );
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( _( "Last Change" ), msg );
|
2020-05-22 10:31:14 +00:00
|
|
|
}
|
|
|
|
else if( aFrame->IsType( FRAME_PCB_EDITOR ) )
|
|
|
|
{
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( _( "Board Side" ), IsFlipped() ? _( "Back (Flipped)" ) : _( "Front" ) );
|
2020-05-22 10:31:14 +00:00
|
|
|
}
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-08-26 21:43:38 +00:00
|
|
|
auto addToken = []( wxString* aStr, const wxString& aAttr )
|
2020-10-14 23:37:26 +00:00
|
|
|
{
|
|
|
|
if( !aStr->IsEmpty() )
|
|
|
|
*aStr += wxT( ", " );
|
2020-08-26 21:43:38 +00:00
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
*aStr += aAttr;
|
|
|
|
};
|
2020-08-26 21:43:38 +00:00
|
|
|
|
|
|
|
wxString status;
|
|
|
|
wxString attrs;
|
2010-12-08 20:12:46 +00:00
|
|
|
|
2021-10-31 16:32:24 +00:00
|
|
|
if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
|
2021-04-21 14:40:16 +00:00
|
|
|
addToken( &status, _( "Locked" ) );
|
2010-12-08 20:12:46 +00:00
|
|
|
|
2020-11-13 11:17:15 +00:00
|
|
|
if( m_fpStatus & FP_is_PLACED )
|
2020-08-26 21:43:38 +00:00
|
|
|
addToken( &status, _( "autoplaced" ) );
|
2010-12-08 20:12:46 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( m_attributes & FP_BOARD_ONLY )
|
2020-08-26 21:43:38 +00:00
|
|
|
addToken( &attrs, _( "not in schematic" ) );
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( m_attributes & FP_EXCLUDE_FROM_POS_FILES )
|
2020-08-26 21:43:38 +00:00
|
|
|
addToken( &attrs, _( "exclude from pos files" ) );
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( m_attributes & FP_EXCLUDE_FROM_BOM )
|
2020-08-26 21:43:38 +00:00
|
|
|
addToken( &attrs, _( "exclude from BOM" ) );
|
2012-12-06 21:53:00 +00:00
|
|
|
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
|
2010-12-08 20:12:46 +00:00
|
|
|
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( _( "Rotation" ), wxString::Format( "%.4g", GetOrientationDegrees() ) );
|
2020-07-06 13:00:36 +00:00
|
|
|
|
2021-10-10 08:05:37 +00:00
|
|
|
msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
|
2020-11-30 14:35:48 +00:00
|
|
|
msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" )
|
|
|
|
: m_3D_Drawings.front().m_Filename );
|
|
|
|
aList.emplace_back( msg, msg2 );
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
msg.Printf( _( "Doc: %s" ), m_doc );
|
|
|
|
msg2.Printf( _( "Keywords: %s" ), m_keywords );
|
2020-11-30 14:35:48 +00:00
|
|
|
aList.emplace_back( msg, msg2 );
|
2007-08-06 02:02:39 +00:00
|
|
|
}
|
2007-06-05 12:10:51 +00:00
|
|
|
|
2007-08-06 02:02:39 +00:00
|
|
|
|
2020-11-26 19:45:22 +00:00
|
|
|
bool FOOTPRINT::IsOnLayer( PCB_LAYER_ID aLayer ) const
|
|
|
|
{
|
|
|
|
// If we have any pads, fall back on normal checking
|
|
|
|
if( !m_pads.empty() )
|
|
|
|
return m_layer == aLayer;
|
|
|
|
|
|
|
|
// No pads? Check if this entire footprint exists on the given layer
|
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
|
|
|
{
|
|
|
|
if( !zone->IsOnLayer( aLayer ) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
|
|
|
{
|
|
|
|
if( !item->IsOnLayer( aLayer ) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
|
2007-08-08 03:50:44 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
EDA_RECT rect = GetBoundingBox( false, false );
|
2019-05-05 10:33:34 +00:00
|
|
|
return rect.Inflate( aAccuracy ).Contains( aPosition );
|
2007-08-08 03:50:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::HitTestAccurate( const wxPoint& aPosition, int aAccuracy ) const
|
2018-02-19 00:00:29 +00:00
|
|
|
{
|
2020-11-29 14:00:39 +00:00
|
|
|
return GetBoundingHull().Collide( aPosition, aAccuracy );
|
2018-02-19 00:00:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
|
2008-01-06 12:43:57 +00:00
|
|
|
{
|
2013-09-21 18:09:41 +00:00
|
|
|
EDA_RECT arect = aRect;
|
|
|
|
arect.Inflate( aAccuracy );
|
2011-09-01 21:39:38 +00:00
|
|
|
|
2013-09-21 18:09:41 +00:00
|
|
|
if( aContained )
|
2021-06-23 22:58:58 +00:00
|
|
|
{
|
2021-02-26 17:44:36 +00:00
|
|
|
return arect.Contains( GetBoundingBox( false, false ) );
|
2021-06-23 22:58:58 +00:00
|
|
|
}
|
2013-09-21 18:09:41 +00:00
|
|
|
else
|
2017-05-02 06:44:41 +00:00
|
|
|
{
|
|
|
|
// If the rect does not intersect the bounding box, skip any tests
|
2021-02-26 13:49:40 +00:00
|
|
|
if( !aRect.Intersects( GetBoundingBox( false, false ) ) )
|
2017-05-02 06:44:41 +00:00
|
|
|
return false;
|
|
|
|
|
2021-06-23 22:58:58 +00:00
|
|
|
// The empty footprint dummy rectangle intersects the selection area.
|
|
|
|
if( m_pads.empty() && m_fp_zones.empty() && m_drawings.empty() )
|
|
|
|
return GetBoundingBox( true, false ).Intersects( arect );
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
// Determine if any elements in the FOOTPRINT intersect the rect
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2017-05-02 06:44:41 +00:00
|
|
|
{
|
|
|
|
if( pad->HitTest( arect, false, 0 ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
|
|
|
if( zone->HitTest( arect, false, 0 ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2017-05-02 06:44:41 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
if( item->Type() != PCB_FP_TEXT_T && item->HitTest( arect, false, 0 ) )
|
2017-05-02 06:44:41 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
// Groups are not hit-tested; only their members
|
|
|
|
|
2017-05-02 06:44:41 +00:00
|
|
|
// No items were hit
|
|
|
|
return false;
|
|
|
|
}
|
2008-01-06 12:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
|
2008-03-04 04:22:27 +00:00
|
|
|
{
|
2021-08-16 18:52:49 +00:00
|
|
|
bool can_select = aSearchAfterMe ? false : true;
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2008-03-04 04:22:27 +00:00
|
|
|
{
|
2021-08-16 18:52:49 +00:00
|
|
|
if( !can_select && pad == aSearchAfterMe )
|
|
|
|
{
|
|
|
|
can_select = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
if( can_select && pad->GetNumber() == aPadNumber )
|
2008-03-04 04:22:27 +00:00
|
|
|
return pad;
|
|
|
|
}
|
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
return nullptr;
|
2008-03-04 04:22:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
PAD* FOOTPRINT::GetPad( const wxPoint& aPosition, LSET aLayerMask )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2011-09-15 17:58:35 +00:00
|
|
|
{
|
2012-02-19 04:02:19 +00:00
|
|
|
// ... and on the correct layer.
|
2014-06-24 18:30:39 +00:00
|
|
|
if( !( pad->GetLayerSet() & aLayerMask ).any() )
|
2011-09-15 17:58:35 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if( pad->HitTest( aPosition ) )
|
|
|
|
return pad;
|
|
|
|
}
|
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
return nullptr;
|
2011-09-15 17:58:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
PAD* FOOTPRINT::GetTopLeftPad()
|
2017-04-22 20:07:29 +00:00
|
|
|
{
|
2021-02-01 20:00:42 +00:00
|
|
|
PAD* topLeftPad = m_pads.front();
|
2017-04-22 20:07:29 +00:00
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* p : m_pads )
|
2017-04-22 20:07:29 +00:00
|
|
|
{
|
|
|
|
wxPoint pnt = p->GetPosition(); // GetPosition() returns the center of the pad
|
|
|
|
|
|
|
|
if( ( pnt.x < topLeftPad->GetPosition().x ) ||
|
2020-05-22 10:31:14 +00:00
|
|
|
( topLeftPad->GetPosition().x == pnt.x && pnt.y < topLeftPad->GetPosition().y ) )
|
2017-04-22 20:07:29 +00:00
|
|
|
{
|
|
|
|
topLeftPad = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return topLeftPad;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
unsigned FOOTPRINT::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
|
2013-06-07 13:17:52 +00:00
|
|
|
{
|
|
|
|
if( aIncludeNPTH )
|
2019-06-01 23:23:36 +00:00
|
|
|
return m_pads.size();
|
2013-06-07 13:17:52 +00:00
|
|
|
|
|
|
|
unsigned cnt = 0;
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2013-06-07 13:17:52 +00:00
|
|
|
{
|
2021-05-01 14:46:50 +00:00
|
|
|
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
2013-06-07 13:17:52 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
unsigned FOOTPRINT::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
|
2015-11-11 18:35:26 +00:00
|
|
|
{
|
2021-08-23 23:10:21 +00:00
|
|
|
std::set<wxString> usedNumbers;
|
2015-11-11 18:35:26 +00:00
|
|
|
|
|
|
|
// Create a set of used pad numbers
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2015-11-11 18:35:26 +00:00
|
|
|
{
|
2015-11-13 11:32:42 +00:00
|
|
|
// Skip pads not on copper layers (used to build complex
|
|
|
|
// solder paste shapes for instance)
|
|
|
|
if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Skip pads with no name, because they are usually "mechanical"
|
|
|
|
// pads, not "electrical" pads
|
2021-08-23 23:10:21 +00:00
|
|
|
if( pad->GetNumber().IsEmpty() )
|
2015-11-13 11:32:42 +00:00
|
|
|
continue;
|
|
|
|
|
2015-11-11 18:35:26 +00:00
|
|
|
if( !aIncludeNPTH )
|
|
|
|
{
|
2015-11-13 11:32:42 +00:00
|
|
|
// skip NPTH
|
2021-05-01 14:46:50 +00:00
|
|
|
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
2015-11-11 18:35:26 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
usedNumbers.insert( pad->GetNumber() );
|
2015-11-11 18:35:26 +00:00
|
|
|
}
|
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
return usedNumbers.size();
|
2015-11-11 18:35:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::Add3DModel( FP_3DMODEL* a3DModel )
|
2012-06-09 17:00:13 +00:00
|
|
|
{
|
2020-12-28 16:13:15 +00:00
|
|
|
if( nullptr == a3DModel )
|
2016-07-19 17:35:25 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if( !a3DModel->m_Filename.empty() )
|
|
|
|
m_3D_Drawings.push_back( *a3DModel );
|
2012-06-09 17:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
// see footprint.h
|
2020-11-13 15:15:52 +00:00
|
|
|
SEARCH_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
|
2007-08-09 21:15:08 +00:00
|
|
|
{
|
2008-03-15 10:24:32 +00:00
|
|
|
KICAD_T stype;
|
2019-12-28 00:55:11 +00:00
|
|
|
SEARCH_RESULT result = SEARCH_RESULT::CONTINUE;
|
2008-03-15 10:24:32 +00:00
|
|
|
const KICAD_T* p = scanTypes;
|
|
|
|
bool done = false;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2009-11-04 19:08:08 +00:00
|
|
|
#if 0 && defined(DEBUG)
|
2008-03-15 10:24:32 +00:00
|
|
|
std::cout << GetClass().mb_str() << ' ';
|
2008-02-19 00:30:10 +00:00
|
|
|
#endif
|
2007-08-09 21:15:08 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
while( !done )
|
2007-08-09 21:15:08 +00:00
|
|
|
{
|
2007-08-24 03:40:04 +00:00
|
|
|
stype = *p;
|
2008-03-15 10:24:32 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
switch( stype )
|
|
|
|
{
|
2020-11-13 12:21:02 +00:00
|
|
|
case PCB_FOOTPRINT_T:
|
2016-07-12 19:05:54 +00:00
|
|
|
result = inspector( this, testData ); // inspect me
|
2007-08-24 03:40:04 +00:00
|
|
|
++p;
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
case PCB_PAD_T:
|
2020-11-12 22:30:02 +00:00
|
|
|
result = IterateForward<PAD*>( m_pads, inspector, testData, p );
|
2007-08-24 03:40:04 +00:00
|
|
|
++p;
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
case PCB_FP_ZONE_T:
|
|
|
|
result = IterateForward<FP_ZONE*>( m_fp_zones, inspector, testData, p );
|
2019-10-29 10:09:54 +00:00
|
|
|
++p;
|
|
|
|
break;
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2020-11-13 02:57:11 +00:00
|
|
|
result = inspector( m_reference, testData );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
result = inspector( m_value, testData );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
// Intentionally fall through since m_Drawings can hold PCB_FP_SHAPE_T also
|
2020-04-24 23:44:09 +00:00
|
|
|
KI_FALLTHROUGH;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2019-06-02 03:55:32 +00:00
|
|
|
result = IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, p );
|
2008-03-15 10:24:32 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
// skip over any types handled in the above call.
|
2008-03-15 10:24:32 +00:00
|
|
|
for( ; ; )
|
2007-08-24 03:40:04 +00:00
|
|
|
{
|
|
|
|
switch( stype = *++p )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
|
|
|
case PCB_FP_SHAPE_T:
|
2007-08-24 03:40:04 +00:00
|
|
|
continue;
|
2008-03-15 10:24:32 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
;
|
2007-08-24 03:40:04 +00:00
|
|
|
}
|
2008-03-15 10:24:32 +00:00
|
|
|
|
2007-08-24 03:40:04 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-03-15 10:24:32 +00:00
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
result = IterateForward<PCB_GROUP*>( m_fp_groups, inspector, testData, p );
|
|
|
|
++p;
|
|
|
|
break;
|
|
|
|
|
2007-08-09 21:15:08 +00:00
|
|
|
default:
|
2007-08-24 03:40:04 +00:00
|
|
|
done = true;
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-02-19 00:30:10 +00:00
|
|
|
|
2019-12-28 00:55:11 +00:00
|
|
|
if( result == SEARCH_RESULT::QUIT )
|
2007-08-09 21:15:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-02-19 00:30:10 +00:00
|
|
|
return result;
|
2007-08-09 21:15:08 +00:00
|
|
|
}
|
|
|
|
|
2007-08-08 03:50:44 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
wxString FOOTPRINT::GetSelectMenuText( EDA_UNITS aUnits ) const
|
2011-07-14 15:42:44 +00:00
|
|
|
{
|
2018-01-31 09:08:57 +00:00
|
|
|
wxString reference = GetReference();
|
|
|
|
|
|
|
|
if( reference.IsEmpty() )
|
2020-03-17 16:08:29 +00:00
|
|
|
reference = _( "<no reference designator>" );
|
2018-01-31 09:08:57 +00:00
|
|
|
|
2020-09-19 16:12:00 +00:00
|
|
|
return wxString::Format( _( "Footprint %s" ), reference );
|
2011-07-14 15:42:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-08 02:59:07 +00:00
|
|
|
BITMAPS FOOTPRINT::GetMenuImage() const
|
2017-02-20 12:20:39 +00:00
|
|
|
{
|
2021-03-08 02:59:07 +00:00
|
|
|
return BITMAPS::module;
|
2017-02-20 12:20:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
EDA_ITEM* FOOTPRINT::Clone() const
|
2012-01-14 19:50:32 +00:00
|
|
|
{
|
2020-11-13 15:15:52 +00:00
|
|
|
return new FOOTPRINT( *this );
|
2012-01-14 19:50:32 +00:00
|
|
|
}
|
|
|
|
|
2013-04-25 16:29:35 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::RunOnChildren( const std::function<void ( BOARD_ITEM*)>& aFunction ) const
|
2013-12-18 12:39:11 +00:00
|
|
|
{
|
2015-02-15 22:21:52 +00:00
|
|
|
try
|
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2015-02-15 22:21:52 +00:00
|
|
|
aFunction( static_cast<BOARD_ITEM*>( pad ) );
|
2013-12-18 12:39:11 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( FP_ZONE* zone : m_fp_zones )
|
|
|
|
aFunction( static_cast<FP_ZONE*>( zone ) );
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
for( PCB_GROUP* group : m_fp_groups )
|
|
|
|
aFunction( static_cast<PCB_GROUP*>( group ) );
|
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* drawing : m_drawings )
|
2019-06-02 03:55:32 +00:00
|
|
|
aFunction( static_cast<BOARD_ITEM*>( drawing ) );
|
2013-12-18 12:39:11 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
aFunction( static_cast<BOARD_ITEM*>( m_reference ) );
|
|
|
|
aFunction( static_cast<BOARD_ITEM*>( m_value ) );
|
2015-02-15 22:21:52 +00:00
|
|
|
}
|
2016-10-10 15:04:48 +00:00
|
|
|
catch( std::bad_function_call& )
|
2015-02-15 22:21:52 +00:00
|
|
|
{
|
2020-11-13 15:15:52 +00:00
|
|
|
wxFAIL_MSG( "Error running FOOTPRINT::RunOnChildren" );
|
2015-02-15 22:21:52 +00:00
|
|
|
}
|
2013-12-18 12:39:11 +00:00
|
|
|
}
|
|
|
|
|
2018-03-08 01:40:50 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::GetAllDrawingLayers( int aLayers[], int& aCount, bool aIncludePads ) const
|
2018-03-08 01:40:50 +00:00
|
|
|
{
|
|
|
|
std::unordered_set<int> layers;
|
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2018-03-08 01:40:50 +00:00
|
|
|
layers.insert( static_cast<int>( item->GetLayer() ) );
|
|
|
|
|
|
|
|
if( aIncludePads )
|
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2018-03-08 01:40:50 +00:00
|
|
|
{
|
|
|
|
int pad_layers[KIGFX::VIEW::VIEW_MAX_LAYERS], pad_layers_count;
|
|
|
|
pad->ViewGetLayers( pad_layers, pad_layers_count );
|
|
|
|
|
|
|
|
for( int i = 0; i < pad_layers_count; i++ )
|
|
|
|
layers.insert( pad_layers[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aCount = layers.size();
|
|
|
|
int i = 0;
|
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( int layer : layers )
|
2018-03-08 01:40:50 +00:00
|
|
|
aLayers[i++] = layer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::ViewGetLayers( int aLayers[], int& aCount ) const
|
2014-07-09 13:02:56 +00:00
|
|
|
{
|
2014-08-04 08:06:24 +00:00
|
|
|
aCount = 2;
|
2017-03-13 03:19:33 +00:00
|
|
|
aLayers[0] = LAYER_ANCHOR;
|
2014-08-04 08:06:24 +00:00
|
|
|
|
2020-11-14 14:29:11 +00:00
|
|
|
switch( m_layer )
|
2014-08-04 08:06:24 +00:00
|
|
|
{
|
2016-03-13 15:39:18 +00:00
|
|
|
default:
|
2020-10-14 23:37:26 +00:00
|
|
|
wxASSERT_MSG( false, "Illegal layer" ); // do you really have footprints placed on
|
|
|
|
// other layers?
|
2020-04-24 23:44:09 +00:00
|
|
|
KI_FALLTHROUGH;
|
|
|
|
|
2014-08-04 08:06:24 +00:00
|
|
|
case F_Cu:
|
2017-03-13 03:19:33 +00:00
|
|
|
aLayers[1] = LAYER_MOD_FR;
|
2014-08-04 08:06:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case B_Cu:
|
2017-03-13 03:19:33 +00:00
|
|
|
aLayers[1] = LAYER_MOD_BK;
|
2014-08-04 08:06:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-01-04 22:39:36 +00:00
|
|
|
|
2020-08-30 21:20:37 +00:00
|
|
|
// If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
|
|
|
|
// layer as well so that the component can be edited with the silkscreen layer
|
2018-01-04 22:39:36 +00:00
|
|
|
bool f_silk = false, b_silk = false, non_silk = false;
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2018-01-04 22:39:36 +00:00
|
|
|
{
|
|
|
|
if( item->GetLayer() == F_SilkS )
|
|
|
|
f_silk = true;
|
|
|
|
else if( item->GetLayer() == B_SilkS )
|
|
|
|
b_silk = true;
|
|
|
|
else
|
|
|
|
non_silk = true;
|
|
|
|
}
|
|
|
|
|
2019-06-01 23:23:36 +00:00
|
|
|
if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
|
2018-01-04 22:39:36 +00:00
|
|
|
{
|
|
|
|
if( f_silk )
|
|
|
|
aLayers[ aCount++ ] = F_SilkS;
|
|
|
|
|
|
|
|
if( b_silk )
|
|
|
|
aLayers[ aCount++ ] = B_SilkS;
|
|
|
|
}
|
2014-07-09 13:02:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
double FOOTPRINT::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
|
2014-07-09 13:02:56 +00:00
|
|
|
{
|
2020-11-14 14:29:11 +00:00
|
|
|
int layer = ( m_layer == F_Cu ) ? LAYER_MOD_FR :
|
|
|
|
( m_layer == B_Cu ) ? LAYER_MOD_BK : LAYER_ANCHOR;
|
2016-06-07 13:07:47 +00:00
|
|
|
|
2020-08-30 21:20:37 +00:00
|
|
|
// Currently this is only pertinent for the anchor layer; everything else is drawn from the
|
|
|
|
// children.
|
2020-09-21 15:03:08 +00:00
|
|
|
// The "good" value is experimentally chosen.
|
|
|
|
#define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY 1.5
|
2020-09-21 06:56:53 +00:00
|
|
|
|
2017-03-13 03:19:33 +00:00
|
|
|
if( aView->IsLayerVisible( layer ) )
|
2020-09-21 06:56:53 +00:00
|
|
|
return MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY;
|
2016-06-07 13:07:47 +00:00
|
|
|
|
2020-09-21 15:03:08 +00:00
|
|
|
return std::numeric_limits<double>::max();
|
2014-07-09 13:02:56 +00:00
|
|
|
}
|
|
|
|
|
2015-06-16 15:03:36 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
const BOX2I FOOTPRINT::ViewBBox() const
|
2014-11-13 16:32:59 +00:00
|
|
|
{
|
2021-02-26 13:49:40 +00:00
|
|
|
EDA_RECT area = GetBoundingBox( true, true );
|
2018-06-12 15:37:02 +00:00
|
|
|
|
2020-08-30 21:20:37 +00:00
|
|
|
// Add the Clearance shape size: (shape around the pads when the clearance is shown. Not
|
|
|
|
// optimized, but the draw cost is small (perhaps smaller than optimization).
|
2021-08-16 09:53:27 +00:00
|
|
|
const BOARD* board = GetBoard();
|
2020-12-05 18:03:03 +00:00
|
|
|
|
2018-06-12 15:37:02 +00:00
|
|
|
if( board )
|
|
|
|
{
|
|
|
|
int biggest_clearance = board->GetDesignSettings().GetBiggestClearanceValue();
|
|
|
|
area.Inflate( biggest_clearance );
|
|
|
|
}
|
|
|
|
|
2018-01-30 06:45:53 +00:00
|
|
|
return area;
|
2014-11-13 16:32:59 +00:00
|
|
|
}
|
2014-07-09 13:02:56 +00:00
|
|
|
|
2015-06-16 15:03:36 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::IsLibNameValid( const wxString & aName )
|
2012-12-10 11:18:42 +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
|
|
|
const wxChar * invalids = StringLibNameInvalidChars( false );
|
2012-12-10 11:18:42 +00:00
|
|
|
|
|
|
|
if( aName.find_first_of( invalids ) != std::string::npos )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
|
2012-12-10 11:18:42 +00:00
|
|
|
{
|
2019-06-29 20:31:11 +00:00
|
|
|
// This list of characters is also duplicated in validators.cpp and
|
|
|
|
// lib_id.cpp
|
|
|
|
// TODO: Unify forbidden character lists
|
|
|
|
static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
|
|
|
|
static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
|
2012-12-10 11:18:42 +00:00
|
|
|
|
|
|
|
if( aUserReadable )
|
|
|
|
return invalidCharsReadable;
|
|
|
|
else
|
|
|
|
return invalidChars;
|
|
|
|
}
|
|
|
|
|
2012-01-14 19:50:32 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::Move( const wxPoint& aMoveVector )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
wxPoint newpos = m_pos + aMoveVector;
|
2013-03-13 18:53:58 +00:00
|
|
|
SetPosition( newpos );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::Rotate( const wxPoint& aRotCentre, double aAngle )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2019-04-07 21:09:50 +00:00
|
|
|
double orientation = GetOrientation();
|
|
|
|
double newOrientation = orientation + aAngle;
|
2020-11-13 02:57:11 +00:00
|
|
|
wxPoint newpos = m_pos;
|
2013-03-13 18:53:58 +00:00
|
|
|
RotatePoint( &newpos, aRotCentre, aAngle );
|
|
|
|
SetPosition( newpos );
|
2019-04-07 21:09:50 +00:00
|
|
|
SetOrientation( newOrientation );
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference->KeepUpright( orientation, newOrientation );
|
|
|
|
m_value->KeepUpright( orientation, newOrientation );
|
2019-04-07 21:09:50 +00:00
|
|
|
|
2020-06-27 18:01:00 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2019-04-07 21:09:50 +00:00
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
if( item->Type() == PCB_FP_TEXT_T )
|
2020-10-04 23:34:59 +00:00
|
|
|
static_cast<FP_TEXT*>( item )->KeepUpright( orientation, newOrientation );
|
2019-04-07 21:09:50 +00:00
|
|
|
}
|
2021-02-26 13:49:40 +00:00
|
|
|
|
|
|
|
m_boundingBoxCacheTimeStamp = 0;
|
|
|
|
m_visibleBBoxCacheTimeStamp = 0;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = 0;
|
|
|
|
m_hullCacheTimeStamp = 0;
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::Flip( const wxPoint& aCentre, bool aFlipLeftRight )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
// Move footprint to its final position:
|
2020-11-13 02:57:11 +00:00
|
|
|
wxPoint finalPos = m_pos;
|
2019-07-12 21:02:10 +00:00
|
|
|
|
2019-09-17 16:51:27 +00:00
|
|
|
// Now Flip the footprint.
|
2020-11-13 02:57:11 +00:00
|
|
|
// Flipping a footprint is a specific transform: it is not mirrored like a text.
|
|
|
|
// We have to change the side, and ensure the footprint rotation is modified according to the
|
|
|
|
// transform, because this parameter is used in pick and place files, and when updating the
|
|
|
|
// footprint from library.
|
2019-09-17 16:51:27 +00:00
|
|
|
// When flipped around the X axis (Y coordinates changed) orientation is negated
|
|
|
|
// When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
|
2021-06-09 19:32:58 +00:00
|
|
|
// Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
|
2019-09-17 16:51:27 +00:00
|
|
|
|
|
|
|
MIRROR( finalPos.y, aCentre.y ); /// Mirror the Y position (around the X axis)
|
2019-07-12 21:02:10 +00:00
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
SetPosition( finalPos );
|
|
|
|
|
|
|
|
// Flip layer
|
2013-04-05 19:04:58 +00:00
|
|
|
SetLayer( FlipLayer( GetLayer() ) );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
|
|
|
// Reverse mirror orientation.
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = -m_orient;
|
2019-09-17 16:51:27 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
NORMALIZE_ANGLE_180( m_orient );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2019-07-12 21:02:10 +00:00
|
|
|
// Mirror pads to other side of board.
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2020-11-13 02:57:11 +00:00
|
|
|
pad->Flip( m_pos, false );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2019-10-29 10:09:54 +00:00
|
|
|
// Mirror zones to other side of board.
|
2020-11-11 23:05:59 +00:00
|
|
|
for( ZONE* zone : m_fp_zones )
|
2021-02-02 00:07:04 +00:00
|
|
|
zone->Flip( m_pos, false );
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2015-07-31 19:04:30 +00:00
|
|
|
// Mirror reference and value.
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference->Flip( m_pos, false );
|
|
|
|
m_value->Flip( m_pos, false );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-11-13 11:17:15 +00:00
|
|
|
// Reverse mirror footprint graphics and texts.
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2020-11-13 02:57:11 +00:00
|
|
|
static_cast<FP_SHAPE*>( item )->Flip( m_pos, false );
|
2013-03-13 18:53:58 +00:00
|
|
|
break;
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2020-11-13 02:57:11 +00:00
|
|
|
static_cast<FP_TEXT*>( item )->Flip( m_pos, false );
|
2013-03-13 18:53:58 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-11-13 21:07:01 +00:00
|
|
|
wxMessageBox( wxT( "FOOTPRINT::Flip() error: Unknown Draw Type" ) );
|
2013-03-13 18:53:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 16:51:27 +00:00
|
|
|
// Now rotate 180 deg if required
|
|
|
|
if( aFlipLeftRight )
|
|
|
|
Rotate( aCentre, 1800.0 );
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_boundingBoxCacheTimeStamp = 0;
|
|
|
|
m_visibleBBoxCacheTimeStamp = 0;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = 0;
|
2021-01-31 14:32:14 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedHull.Mirror( aFlipLeftRight, !aFlipLeftRight, m_pos );
|
2021-02-20 13:25:02 +00:00
|
|
|
|
|
|
|
std::swap( m_poly_courtyard_front, m_poly_courtyard_back );
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::SetPosition( const wxPoint& aPos )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
wxPoint delta = aPos - m_pos;
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_pos += delta;
|
2017-01-23 20:30:11 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference->EDA_TEXT::Offset( delta );
|
|
|
|
m_value->EDA_TEXT::Offset( delta );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2013-03-13 18:53:58 +00:00
|
|
|
pad->SetPosition( pad->GetPosition() + delta );
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( ZONE* zone : m_fp_zones )
|
2019-10-29 10:09:54 +00:00
|
|
|
zone->Move( delta );
|
|
|
|
|
2020-10-14 23:37:26 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
|
|
|
|
shape->SetDrawCoord();
|
2013-03-13 18:53:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
2017-01-23 20:30:11 +00:00
|
|
|
text->EDA_TEXT::Offset( delta );
|
2013-03-13 18:53:58 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxMessageBox( wxT( "Draw type undefined." ) );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedBoundingBox.Move( delta );
|
|
|
|
m_cachedVisibleBBox.Move( delta );
|
|
|
|
m_cachedTextExcludedBBox.Move( delta );
|
|
|
|
m_cachedHull.Move( delta );
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-25 16:29:35 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::MoveAnchorPosition( const wxPoint& aMoveVector )
|
2013-03-23 13:30:00 +00:00
|
|
|
{
|
|
|
|
/* Move the reference point of the footprint
|
|
|
|
* the footprints elements (pads, outlines, edges .. ) are moved
|
|
|
|
* but:
|
|
|
|
* - the footprint position is not modified.
|
|
|
|
* - the relative (local) coordinates of these items are modified
|
2015-03-02 08:28:49 +00:00
|
|
|
* - Draw coordinates are updated
|
2013-03-23 13:30:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2015-03-02 08:28:49 +00:00
|
|
|
// Update (move) the relative coordinates relative to the new anchor point.
|
2013-03-23 13:30:00 +00:00
|
|
|
wxPoint moveVector = aMoveVector;
|
|
|
|
RotatePoint( &moveVector, -GetOrientation() );
|
|
|
|
|
|
|
|
// Update of the reference and value.
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference->SetPos0( m_reference->GetPos0() + moveVector );
|
|
|
|
m_reference->SetDrawCoord();
|
|
|
|
m_value->SetPos0( m_value->GetPos0() + moveVector );
|
|
|
|
m_value->SetDrawCoord();
|
2013-03-23 13:30:00 +00:00
|
|
|
|
|
|
|
// Update the pad local coordinates.
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2013-03-23 13:30:00 +00:00
|
|
|
{
|
|
|
|
pad->SetPos0( pad->GetPos0() + moveVector );
|
2015-02-27 14:33:13 +00:00
|
|
|
pad->SetDrawCoord();
|
2013-03-23 13:30:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the draw element coordinates.
|
2020-10-03 11:16:29 +00:00
|
|
|
for( BOARD_ITEM* item : GraphicalItems() )
|
2013-03-23 13:30:00 +00:00
|
|
|
{
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2021-03-07 14:53:27 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
|
2021-03-07 14:53:27 +00:00
|
|
|
shape->Move( moveVector );
|
|
|
|
}
|
2014-11-15 19:06:05 +00:00
|
|
|
break;
|
2013-03-23 13:30:00 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2021-03-07 14:53:27 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
2014-11-15 19:06:05 +00:00
|
|
|
text->SetPos0( text->GetPos0() + moveVector );
|
|
|
|
text->SetDrawCoord();
|
2021-03-07 14:53:27 +00:00
|
|
|
}
|
2014-11-15 19:06:05 +00:00
|
|
|
break;
|
2013-03-23 13:30:00 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-07 14:53:27 +00:00
|
|
|
// Update the keepout zones
|
|
|
|
for( ZONE* zone : Zones() )
|
|
|
|
{
|
|
|
|
zone->Move( moveVector );
|
|
|
|
}
|
|
|
|
|
2021-03-12 17:37:09 +00:00
|
|
|
// Update the 3D models
|
|
|
|
for( FP_3DMODEL& model : Models() )
|
|
|
|
{
|
|
|
|
model.m_Offset.x += Iu2Millimeter( moveVector.x );
|
|
|
|
model.m_Offset.y -= Iu2Millimeter( moveVector.y );
|
|
|
|
}
|
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_cachedBoundingBox.Move( moveVector );
|
|
|
|
m_cachedVisibleBBox.Move( moveVector );
|
|
|
|
m_cachedTextExcludedBBox.Move( moveVector );
|
|
|
|
m_cachedHull.Move( moveVector );
|
2013-03-23 13:30:00 +00:00
|
|
|
}
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2013-04-25 16:29:35 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::SetOrientation( double aNewAngle )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-11-13 02:57:11 +00:00
|
|
|
double angleChange = aNewAngle - m_orient; // change in rotation
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-09-14 00:15:46 +00:00
|
|
|
NORMALIZE_ANGLE_180( aNewAngle );
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
m_orient = aNewAngle;
|
2013-03-13 18:53:58 +00:00
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
|
|
|
pad->SetOrientation( pad->GetOrientation() + angleChange );
|
2015-02-27 14:33:13 +00:00
|
|
|
pad->SetDrawCoord();
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
for( ZONE* zone : m_fp_zones )
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
|
|
|
zone->Rotate( GetPosition(), angleChange );
|
|
|
|
}
|
|
|
|
|
2013-03-13 18:53:58 +00:00
|
|
|
// Update of the reference and value.
|
2020-11-13 02:57:11 +00:00
|
|
|
m_reference->SetDrawCoord();
|
|
|
|
m_value->SetDrawCoord();
|
2013-03-13 18:53:58 +00:00
|
|
|
|
|
|
|
// Displace contours and text of the footprint.
|
2020-10-03 11:16:29 +00:00
|
|
|
for( BOARD_ITEM* item : m_drawings )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
if( item->Type() == PCB_FP_SHAPE_T )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
static_cast<FP_SHAPE*>( item )->SetDrawCoord();
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
2020-10-04 14:19:33 +00:00
|
|
|
else if( item->Type() == PCB_FP_TEXT_T )
|
2013-03-13 18:53:58 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
static_cast<FP_TEXT*>( item )->SetDrawCoord();
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-31 14:32:14 +00:00
|
|
|
|
2021-02-26 13:49:40 +00:00
|
|
|
m_boundingBoxCacheTimeStamp = 0;
|
|
|
|
m_visibleBBoxCacheTimeStamp = 0;
|
|
|
|
m_textExcludedBBoxCacheTimeStamp = 0;
|
|
|
|
|
|
|
|
m_cachedHull.Rotate( -DECIDEG2RAD( angleChange ), GetPosition() );
|
2013-03-13 18:53:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-03 07:59:05 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
BOARD_ITEM* FOOTPRINT::Duplicate() const
|
2020-08-10 13:22:02 +00:00
|
|
|
{
|
2020-11-13 15:15:52 +00:00
|
|
|
FOOTPRINT* dupe = (FOOTPRINT*) Clone();
|
2020-08-10 13:22:02 +00:00
|
|
|
const_cast<KIID&>( dupe->m_Uuid ) = KIID();
|
|
|
|
|
|
|
|
dupe->RunOnChildren( [&]( BOARD_ITEM* child )
|
|
|
|
{
|
|
|
|
const_cast<KIID&>( child->m_Uuid ) = KIID();
|
|
|
|
});
|
|
|
|
|
|
|
|
return static_cast<BOARD_ITEM*>( dupe );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint )
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2021-04-22 21:20:34 +00:00
|
|
|
BOARD_ITEM* new_item = nullptr;
|
|
|
|
FP_ZONE* new_zone = nullptr;
|
2015-02-12 03:22:24 +00:00
|
|
|
|
|
|
|
switch( aItem->Type() )
|
|
|
|
{
|
|
|
|
case PCB_PAD_T:
|
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
|
2020-08-10 13:22:02 +00:00
|
|
|
const_cast<KIID&>( new_pad->m_Uuid ) = KIID();
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( aAddToFootprint )
|
2019-06-01 23:23:36 +00:00
|
|
|
m_pads.push_back( new_pad );
|
2016-05-11 08:17:34 +00:00
|
|
|
|
2015-02-12 03:22:24 +00:00
|
|
|
new_item = new_pad;
|
|
|
|
break;
|
|
|
|
}
|
2016-03-07 07:13:06 +00:00
|
|
|
|
2020-11-11 23:05:59 +00:00
|
|
|
case PCB_FP_ZONE_T:
|
2019-10-29 10:09:54 +00:00
|
|
|
{
|
2020-11-11 23:05:59 +00:00
|
|
|
new_zone = new FP_ZONE( *static_cast<const FP_ZONE*>( aItem ) );
|
2020-08-10 13:22:02 +00:00
|
|
|
const_cast<KIID&>( new_zone->m_Uuid ) = KIID();
|
2019-10-29 10:09:54 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( aAddToFootprint )
|
2019-10-26 15:49:29 +00:00
|
|
|
m_fp_zones.push_back( new_zone );
|
2019-10-29 10:09:54 +00:00
|
|
|
|
|
|
|
new_item = new_zone;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_TEXT* new_text = new FP_TEXT( *static_cast<const FP_TEXT*>( aItem ) );
|
2020-08-10 13:22:02 +00:00
|
|
|
const_cast<KIID&>( new_text->m_Uuid ) = KIID();
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2020-10-04 23:34:59 +00:00
|
|
|
if( new_text->GetType() == FP_TEXT::TEXT_is_REFERENCE )
|
2020-02-02 23:46:26 +00:00
|
|
|
{
|
2020-03-26 11:02:59 +00:00
|
|
|
new_text->SetText( wxT( "${REFERENCE}" ) );
|
2020-10-04 23:34:59 +00:00
|
|
|
new_text->SetType( FP_TEXT::TEXT_is_DIVERS );
|
2020-02-02 23:46:26 +00:00
|
|
|
}
|
2020-10-04 23:34:59 +00:00
|
|
|
else if( new_text->GetType() == FP_TEXT::TEXT_is_VALUE )
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2020-03-26 11:02:59 +00:00
|
|
|
new_text->SetText( wxT( "${VALUE}" ) );
|
2020-10-04 23:34:59 +00:00
|
|
|
new_text->SetType( FP_TEXT::TEXT_is_DIVERS );
|
2020-02-02 23:46:26 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( aAddToFootprint )
|
2020-02-02 23:46:26 +00:00
|
|
|
Add( new_text );
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2020-02-02 23:46:26 +00:00
|
|
|
new_item = new_text;
|
2016-05-11 08:17:34 +00:00
|
|
|
|
2015-02-12 03:22:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-03-07 07:13:06 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
FP_SHAPE* new_shape = new FP_SHAPE( *static_cast<const FP_SHAPE*>( aItem ) );
|
|
|
|
const_cast<KIID&>( new_shape->m_Uuid ) = KIID();
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2020-11-13 02:57:11 +00:00
|
|
|
if( aAddToFootprint )
|
2020-10-04 23:34:59 +00:00
|
|
|
Add( new_shape );
|
2016-05-11 08:17:34 +00:00
|
|
|
|
2020-10-04 23:34:59 +00:00
|
|
|
new_item = new_shape;
|
2015-02-12 03:22:24 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-03-07 07:13:06 +00:00
|
|
|
|
2020-10-03 11:16:29 +00:00
|
|
|
case PCB_GROUP_T:
|
|
|
|
new_item = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
|
|
|
|
break;
|
|
|
|
|
2020-11-13 12:21:02 +00:00
|
|
|
case PCB_FOOTPRINT_T:
|
2020-10-14 23:37:26 +00:00
|
|
|
// Ignore the footprint itself
|
2015-02-12 03:22:24 +00:00
|
|
|
break;
|
|
|
|
|
2015-02-12 03:22:24 +00:00
|
|
|
default:
|
|
|
|
// Un-handled item for duplication
|
2020-02-02 23:46:26 +00:00
|
|
|
wxFAIL_MSG( "Duplication not supported for items of class " + aItem->GetClass() );
|
2015-02-12 03:22:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new_item;
|
|
|
|
}
|
|
|
|
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
|
2015-02-12 03:22:24 +00:00
|
|
|
{
|
2021-08-23 23:10:21 +00:00
|
|
|
std::set<wxString> usedNumbers;
|
2015-02-12 03:22:24 +00:00
|
|
|
|
|
|
|
// Create a set of used pad numbers
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : m_pads )
|
2021-08-23 23:10:21 +00:00
|
|
|
usedNumbers.insert( pad->GetNumber() );
|
2020-05-03 07:59:05 +00:00
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
// Pad numbers aren't technically reference designators, but the formatting is close enough
|
2021-04-17 14:10:59 +00:00
|
|
|
// for these to give us what we need.
|
2021-08-23 23:10:21 +00:00
|
|
|
wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
|
|
|
|
int num = GetTrailingInt( aLastPadNumber );
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2021-08-23 23:10:21 +00:00
|
|
|
while( usedNumbers.count( wxString::Format( "%s%d", prefix, num ) ) )
|
2020-05-03 07:59:05 +00:00
|
|
|
num++;
|
2015-02-12 03:22:24 +00:00
|
|
|
|
2020-05-03 07:59:05 +00:00
|
|
|
return wxString::Format( "%s%d", prefix, num );
|
2015-02-12 03:22:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::IncrementReference( int aDelta )
|
2018-07-24 18:41:08 +00:00
|
|
|
{
|
2020-10-14 23:37:26 +00:00
|
|
|
const wxString& refdes = GetReference();
|
|
|
|
|
|
|
|
SetReference( wxString::Format( wxT( "%s%i" ),
|
2021-04-17 14:10:59 +00:00
|
|
|
UTIL::GetRefDesPrefix( refdes ),
|
|
|
|
GetTrailingInt( refdes ) + aDelta ) );
|
2018-07-24 18:41:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-13 10:22:29 +00:00
|
|
|
// Calculate the area of a PolySet, polygons with hole are allowed.
|
2017-12-31 16:46:38 +00:00
|
|
|
static double polygonArea( SHAPE_POLY_SET& aPolySet )
|
2017-12-22 00:49:35 +00:00
|
|
|
{
|
2021-09-13 10:22:29 +00:00
|
|
|
// Ensure all outlines are closed, before calculating the SHAPE_POLY_SET area
|
2017-12-31 16:46:38 +00:00
|
|
|
for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
|
2017-12-22 00:49:35 +00:00
|
|
|
{
|
2017-12-31 16:46:38 +00:00
|
|
|
SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
|
|
|
|
outline.SetClosed( true );
|
|
|
|
|
2021-09-13 10:22:29 +00:00
|
|
|
for( int jj = 0; jj < aPolySet.HoleCount( ii ); jj++ )
|
|
|
|
aPolySet.Hole( ii, jj ).SetClosed( true );
|
|
|
|
}
|
2017-12-31 16:46:38 +00:00
|
|
|
|
2021-09-13 10:22:29 +00:00
|
|
|
return aPolySet.Area();
|
2017-12-22 00:49:35 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
|
|
|
|
double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLECTOR& aCollector )
|
2017-12-31 16:46:38 +00:00
|
|
|
{
|
2020-11-27 18:04:30 +00:00
|
|
|
int textMargin = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
|
|
|
|
SHAPE_POLY_SET poly;
|
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
if( aItem->Type() == PCB_MARKER_T )
|
|
|
|
{
|
|
|
|
const PCB_MARKER* marker = static_cast<const PCB_MARKER*>( aItem );
|
|
|
|
SHAPE_LINE_CHAIN markerShape;
|
|
|
|
|
|
|
|
marker->ShapeToPolygon( markerShape );
|
|
|
|
return markerShape.Area();
|
|
|
|
}
|
2020-12-01 14:03:34 +00:00
|
|
|
else if( aItem->Type() == PCB_GROUP_T )
|
|
|
|
{
|
|
|
|
double combinedArea = 0.0;
|
|
|
|
|
|
|
|
for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
|
|
|
|
combinedArea += GetCoverageArea( member, aCollector );
|
|
|
|
|
|
|
|
return combinedArea;
|
|
|
|
}
|
|
|
|
if( aItem->Type() == PCB_FOOTPRINT_T )
|
2020-11-27 18:04:30 +00:00
|
|
|
{
|
|
|
|
const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
|
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
poly = footprint->GetBoundingHull();
|
2020-11-27 18:04:30 +00:00
|
|
|
}
|
|
|
|
else if( aItem->Type() == PCB_FP_TEXT_T )
|
|
|
|
{
|
|
|
|
const FP_TEXT* text = static_cast<const FP_TEXT*>( aItem );
|
|
|
|
|
|
|
|
text->TransformTextShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, textMargin,
|
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE );
|
|
|
|
}
|
2021-03-20 16:46:19 +00:00
|
|
|
else if( aItem->Type() == PCB_SHAPE_T )
|
|
|
|
{
|
|
|
|
// Approximate "linear" shapes with just their width squared, as we don't want to consider
|
|
|
|
// a linear shape as being much bigger than another for purposes of selection filtering
|
|
|
|
// just because it happens to be really long.
|
|
|
|
|
|
|
|
const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
|
|
|
|
|
|
|
|
switch( shape->GetShape() )
|
|
|
|
{
|
2021-07-21 18:31:25 +00:00
|
|
|
case SHAPE_T::SEGMENT:
|
|
|
|
case SHAPE_T::ARC:
|
|
|
|
case SHAPE_T::BEZIER:
|
2021-03-20 16:46:19 +00:00
|
|
|
return shape->GetWidth() * shape->GetWidth();
|
|
|
|
|
2021-07-21 18:31:25 +00:00
|
|
|
case SHAPE_T::RECT:
|
|
|
|
case SHAPE_T::CIRCLE:
|
|
|
|
case SHAPE_T::POLY:
|
2021-03-20 16:46:19 +00:00
|
|
|
{
|
|
|
|
if( !shape->IsFilled() )
|
|
|
|
return shape->GetWidth() * shape->GetWidth();
|
|
|
|
|
|
|
|
KI_FALLTHROUGH;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
aItem->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
|
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
|
|
|
|
{
|
2021-06-11 21:07:02 +00:00
|
|
|
double width = static_cast<const PCB_TRACK*>( aItem )->GetWidth();
|
2021-03-20 16:46:19 +00:00
|
|
|
return width * width;
|
|
|
|
}
|
2020-11-27 18:04:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
aItem->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
|
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE );
|
|
|
|
}
|
2017-12-22 00:49:35 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
return polygonArea( poly );
|
2017-12-31 16:46:38 +00:00
|
|
|
}
|
2017-12-22 00:49:35 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
|
2015-02-17 23:58:14 +00:00
|
|
|
{
|
2020-11-27 18:04:30 +00:00
|
|
|
int textMargin = KiROUND( 5 * aCollector.GetGuide()->OnePixelInIU() );
|
2015-02-17 23:58:14 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
|
2020-11-27 18:04:30 +00:00
|
|
|
SHAPE_POLY_SET coveredRegion;
|
2018-02-22 10:10:48 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
TransformPadsWithClearanceToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
|
|
|
|
ERROR_OUTSIDE );
|
2015-02-17 23:58:14 +00:00
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
TransformFPShapesWithClearanceToPolygon( coveredRegion, UNDEFINED_LAYER, textMargin,
|
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE,
|
|
|
|
true, /* include text */
|
|
|
|
false /* include shapes */ );
|
2015-02-17 23:58:14 +00:00
|
|
|
|
2018-02-22 10:10:48 +00:00
|
|
|
for( int i = 0; i < aCollector.GetCount(); ++i )
|
|
|
|
{
|
2020-11-27 18:04:30 +00:00
|
|
|
const BOARD_ITEM* item = aCollector[i];
|
2018-02-22 10:10:48 +00:00
|
|
|
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
case PCB_FP_TEXT_T:
|
2020-11-27 18:04:30 +00:00
|
|
|
case PCB_FP_SHAPE_T:
|
|
|
|
if( item->GetParent() != this )
|
|
|
|
{
|
2020-11-29 14:00:39 +00:00
|
|
|
item->TransformShapeWithClearanceToPolygon( coveredRegion, UNDEFINED_LAYER, 0,
|
2020-11-27 18:04:30 +00:00
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PCB_TEXT_T:
|
|
|
|
case PCB_SHAPE_T:
|
2018-02-22 10:10:48 +00:00
|
|
|
case PCB_TRACE_T:
|
2019-05-17 00:13:21 +00:00
|
|
|
case PCB_ARC_T:
|
2018-02-22 10:10:48 +00:00
|
|
|
case PCB_VIA_T:
|
2020-11-29 14:00:39 +00:00
|
|
|
item->TransformShapeWithClearanceToPolygon( coveredRegion, UNDEFINED_LAYER, 0,
|
2020-11-27 18:04:30 +00:00
|
|
|
ARC_LOW_DEF, ERROR_OUTSIDE );
|
2018-02-22 10:10:48 +00:00
|
|
|
break;
|
2020-11-27 18:04:30 +00:00
|
|
|
|
2020-11-29 14:00:39 +00:00
|
|
|
case PCB_FOOTPRINT_T:
|
|
|
|
if( item != this )
|
|
|
|
{
|
|
|
|
const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
|
|
|
|
coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-02-22 10:10:48 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-27 18:04:30 +00:00
|
|
|
double footprintRegionArea = polygonArea( footprintRegion );
|
2021-09-13 10:22:29 +00:00
|
|
|
double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
|
2020-11-27 18:04:30 +00:00
|
|
|
double coveredArea = footprintRegionArea - uncoveredRegionArea;
|
|
|
|
double ratio = ( coveredArea / footprintRegionArea );
|
2017-12-31 16:46:38 +00:00
|
|
|
|
2021-09-13 10:22:29 +00:00
|
|
|
// Test for negative ratio (should not occur).
|
|
|
|
// better to be conservative (this will result in the disambiguate dialog)
|
|
|
|
if( ratio < 0.0 )
|
|
|
|
return 1.0;
|
|
|
|
|
2015-02-18 16:53:46 +00:00
|
|
|
return std::min( ratio, 1.0 );
|
2015-02-17 23:58:14 +00:00
|
|
|
}
|
2017-03-16 17:09:33 +00:00
|
|
|
|
2017-12-22 00:49:35 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
2020-08-26 22:04:16 +00:00
|
|
|
{
|
2020-10-25 19:15:32 +00:00
|
|
|
std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
|
|
|
|
|
|
|
|
// There are several possible interpretations here:
|
|
|
|
// 1) the bounding box (without or without invisible items)
|
2020-10-25 20:18:33 +00:00
|
|
|
// 2) just the pads and "edges" (ie: non-text graphic items)
|
2020-10-25 19:15:32 +00:00
|
|
|
// 3) the courtyard
|
|
|
|
|
|
|
|
// We'll go with (2) for now....
|
|
|
|
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : Pads() )
|
2020-10-25 23:38:21 +00:00
|
|
|
shape->AddShape( pad->GetEffectiveShape( aLayer )->Clone() );
|
2020-10-25 19:15:32 +00:00
|
|
|
|
|
|
|
for( BOARD_ITEM* item : GraphicalItems() )
|
2020-10-25 20:18:33 +00:00
|
|
|
{
|
|
|
|
if( item->Type() == PCB_FP_SHAPE_T )
|
2020-10-25 23:38:21 +00:00
|
|
|
shape->AddShape( item->GetEffectiveShape( aLayer )->Clone() );
|
2020-10-25 20:18:33 +00:00
|
|
|
}
|
2020-08-26 22:04:16 +00:00
|
|
|
|
|
|
|
return shape;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-21 20:42:27 +00:00
|
|
|
void FOOTPRINT::BuildPolyCourtyards( OUTLINE_ERROR_HANDLER* aErrorHandler )
|
2017-03-16 17:09:33 +00:00
|
|
|
{
|
2017-03-19 15:38:30 +00:00
|
|
|
m_poly_courtyard_front.RemoveAllContours();
|
|
|
|
m_poly_courtyard_back.RemoveAllContours();
|
2020-11-20 13:55:10 +00:00
|
|
|
ClearFlags( MALFORMED_COURTYARDS );
|
2020-10-25 15:02:49 +00:00
|
|
|
|
2017-03-16 17:09:33 +00:00
|
|
|
// Build the courtyard area from graphic items on the courtyard.
|
2020-10-04 14:19:33 +00:00
|
|
|
// Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored.
|
2017-03-16 17:09:33 +00:00
|
|
|
// Collect items:
|
2020-10-04 23:34:59 +00:00
|
|
|
std::vector<PCB_SHAPE*> list_front;
|
|
|
|
std::vector<PCB_SHAPE*> list_back;
|
2017-03-16 17:09:33 +00:00
|
|
|
|
2020-08-15 12:10:23 +00:00
|
|
|
for( BOARD_ITEM* item : GraphicalItems() )
|
2017-03-16 17:09:33 +00:00
|
|
|
{
|
2020-10-04 14:19:33 +00:00
|
|
|
if( item->GetLayer() == B_CrtYd && item->Type() == PCB_FP_SHAPE_T )
|
2020-10-04 23:34:59 +00:00
|
|
|
list_back.push_back( static_cast<PCB_SHAPE*>( item ) );
|
2017-03-16 17:09:33 +00:00
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
if( item->GetLayer() == F_CrtYd && item->Type() == PCB_FP_SHAPE_T )
|
2020-10-04 23:34:59 +00:00
|
|
|
list_front.push_back( static_cast<PCB_SHAPE*>( item ) );
|
2017-03-16 17:09:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 15:38:30 +00:00
|
|
|
if( !list_front.size() && !list_back.size() )
|
2020-10-25 15:02:49 +00:00
|
|
|
return;
|
2017-03-16 17:09:33 +00:00
|
|
|
|
2020-12-24 21:00:41 +00:00
|
|
|
int errorMax = Millimeter2iu( 0.02 ); // max error for polygonization
|
|
|
|
int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
|
2017-03-16 17:09:33 +00:00
|
|
|
|
2020-12-24 21:00:41 +00:00
|
|
|
if( ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, errorMax, chainingEpsilon,
|
|
|
|
aErrorHandler ) )
|
|
|
|
{
|
2021-03-31 21:34:13 +00:00
|
|
|
// Touching courtyards, or courtyards -at- the clearance distance are legal.
|
|
|
|
m_poly_courtyard_front.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
|
|
|
|
|
2020-12-23 17:33:39 +00:00
|
|
|
m_poly_courtyard_front.CacheTriangulation( false );
|
2020-12-24 21:00:41 +00:00
|
|
|
}
|
2020-12-23 17:33:39 +00:00
|
|
|
else
|
2020-12-24 21:00:41 +00:00
|
|
|
{
|
2020-11-20 13:55:10 +00:00
|
|
|
SetFlags( MALFORMED_F_COURTYARD );
|
2020-12-24 21:00:41 +00:00
|
|
|
}
|
2017-03-20 12:05:38 +00:00
|
|
|
|
2020-12-24 21:00:41 +00:00
|
|
|
if( ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, errorMax, chainingEpsilon,
|
|
|
|
aErrorHandler ) )
|
|
|
|
{
|
2021-03-31 21:34:13 +00:00
|
|
|
// Touching courtyards, or courtyards -at- the clearance distance are legal.
|
|
|
|
m_poly_courtyard_back.Inflate( -1, SHAPE_POLY_SET::CHAMFER_ACUTE_CORNERS );
|
|
|
|
|
2020-12-23 17:33:39 +00:00
|
|
|
m_poly_courtyard_back.CacheTriangulation( false );
|
2020-12-24 21:00:41 +00:00
|
|
|
}
|
2020-12-23 17:33:39 +00:00
|
|
|
else
|
2020-12-24 21:00:41 +00:00
|
|
|
{
|
2020-11-20 13:55:10 +00:00
|
|
|
SetFlags( MALFORMED_B_COURTYARD );
|
2020-12-24 21:00:41 +00:00
|
|
|
}
|
2017-03-16 17:09:33 +00:00
|
|
|
}
|
2017-10-31 13:59:03 +00:00
|
|
|
|
2019-09-15 09:10:53 +00:00
|
|
|
|
2021-09-25 15:55:33 +00:00
|
|
|
void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& msg )>* aErrorHandler )
|
|
|
|
{
|
|
|
|
|
|
|
|
int likelyAttr = GetLikelyAttribute();
|
|
|
|
int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
|
|
|
|
|
|
|
|
// This is only valid if the footprint doesn't have FP_SMD and FP_THROUGH_HOLE set
|
|
|
|
// Which is, unfortunately, possible in theory but not in the UI (I think)
|
|
|
|
if( aErrorHandler && likelyAttr != setAttr )
|
|
|
|
{
|
|
|
|
wxString msg;
|
|
|
|
|
|
|
|
if( likelyAttr == FP_THROUGH_HOLE )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Expected \"Through hole\" type but set to \"%s\"" ), GetTypeName() );
|
|
|
|
}
|
|
|
|
else if( likelyAttr == FP_SMD )
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Expected \"SMD\" type but set to \"%s\"" ), GetTypeName() );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg.Printf( _( "Expected \"Other\" type but set to \"%s\"" ), GetTypeName() );
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = "(" + msg + ")";
|
|
|
|
|
|
|
|
(*aErrorHandler)( msg );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-19 11:24:30 +00:00
|
|
|
void FOOTPRINT::CheckFootprintTHPadNoHoles(
|
|
|
|
const std::function<void( const wxString& msg, const wxPoint& position )>*
|
|
|
|
aErrorHandler )
|
|
|
|
{
|
|
|
|
if( aErrorHandler == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
for( const PAD* pad: Pads() )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( pad->GetAttribute() != PAD_ATTRIB::PTH
|
|
|
|
&& pad->GetAttribute() != PAD_ATTRIB::NPTH )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( pad->GetDrillSizeX() < 1 || pad->GetDrillSizeX() < 1 )
|
|
|
|
{
|
|
|
|
wxString msg;
|
|
|
|
msg.Printf( _( "(pad \"%s\")" ), pad->GetNumber() );
|
|
|
|
|
|
|
|
(*aErrorHandler)( msg, pad->GetPosition() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-25 15:55:33 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
void FOOTPRINT::SwapData( BOARD_ITEM* aImage )
|
2017-10-31 13:59:03 +00:00
|
|
|
{
|
2021-08-18 13:34:32 +00:00
|
|
|
wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
|
2017-10-31 13:59:03 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
std::swap( *((FOOTPRINT*) this), *((FOOTPRINT*) aImage) );
|
2017-10-31 13:59:03 +00:00
|
|
|
}
|
2019-09-15 09:10:53 +00:00
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::HasThroughHolePads() const
|
2019-09-15 09:10:53 +00:00
|
|
|
{
|
2020-11-12 22:30:02 +00:00
|
|
|
for( PAD* pad : Pads() )
|
2019-09-15 09:10:53 +00:00
|
|
|
{
|
2021-05-01 14:46:50 +00:00
|
|
|
if( pad->GetAttribute() != PAD_ATTRIB::SMD )
|
2019-09-15 09:10:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-02 18:40:14 +00:00
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
|
|
|
|
const BOARD_ITEM* aSecond ) const
|
2020-07-24 22:08:36 +00:00
|
|
|
{
|
|
|
|
if( aFirst->Type() != aSecond->Type() )
|
|
|
|
return aFirst->Type() < aSecond->Type();
|
|
|
|
|
|
|
|
if( aFirst->GetLayer() != aSecond->GetLayer() )
|
|
|
|
return aFirst->GetLayer() < aSecond->GetLayer();
|
|
|
|
|
2020-10-04 14:19:33 +00:00
|
|
|
if( aFirst->Type() == PCB_FP_SHAPE_T )
|
2020-07-24 22:08:36 +00:00
|
|
|
{
|
2020-10-04 23:34:59 +00:00
|
|
|
const FP_SHAPE* dwgA = static_cast<const FP_SHAPE*>( aFirst );
|
|
|
|
const FP_SHAPE* dwgB = static_cast<const FP_SHAPE*>( aSecond );
|
2020-07-24 22:08:36 +00:00
|
|
|
|
|
|
|
if( dwgA->GetShape() != dwgB->GetShape() )
|
|
|
|
return dwgA->GetShape() < dwgB->GetShape();
|
|
|
|
}
|
|
|
|
|
2020-10-11 17:02:38 +00:00
|
|
|
if( aFirst->m_Uuid != aSecond->m_Uuid ) // shopuld be always the case foer valid boards
|
|
|
|
return aFirst->m_Uuid < aSecond->m_Uuid;
|
|
|
|
|
|
|
|
return aFirst < aSecond;
|
2020-07-24 22:08:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
|
2020-07-24 22:08:36 +00:00
|
|
|
{
|
2021-08-23 23:10:21 +00:00
|
|
|
if( aFirst->GetNumber() != aSecond->GetNumber() )
|
|
|
|
return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
|
2020-07-24 22:08:36 +00:00
|
|
|
|
2020-10-11 17:02:38 +00:00
|
|
|
if( aFirst->m_Uuid != aSecond->m_Uuid ) // shopuld be always the case foer valid boards
|
|
|
|
return aFirst->m_Uuid < aSecond->m_Uuid;
|
|
|
|
|
|
|
|
return aFirst < aSecond;
|
2020-07-24 22:08:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-10-25 19:50:33 +00:00
|
|
|
void FOOTPRINT::TransformPadsWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
PCB_LAYER_ID aLayer, int aClearance,
|
|
|
|
int aMaxError, ERROR_LOC aErrorLoc,
|
|
|
|
bool aSkipNPTHPadsWihNoCopper,
|
|
|
|
bool aSkipPlatedPads,
|
|
|
|
bool aSkipNonPlatedPads ) const
|
|
|
|
{
|
|
|
|
for( const PAD* pad : m_pads )
|
|
|
|
{
|
|
|
|
if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( !pad->FlashLayer( aLayer ) && IsCopperLayer( aLayer ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// NPTH pads are not drawn on layers if the shape size and pos is the same
|
|
|
|
// as their hole:
|
|
|
|
if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
|
|
|
{
|
|
|
|
if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
|
|
|
|
{
|
|
|
|
switch( pad->GetShape() )
|
|
|
|
{
|
|
|
|
case PAD_SHAPE::CIRCLE:
|
|
|
|
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAD_SHAPE::OVAL:
|
|
|
|
if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool isPlated = ( ( aLayer == F_Cu ) && pad->FlashLayer( F_Mask ) ) ||
|
|
|
|
( ( aLayer == B_Cu ) && pad->FlashLayer( B_Mask ) );
|
|
|
|
|
|
|
|
if( aSkipPlatedPads && isPlated )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( aSkipNonPlatedPads && !isPlated )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wxSize clearance( aClearance, aClearance );
|
|
|
|
|
|
|
|
switch( aLayer )
|
|
|
|
{
|
|
|
|
case F_Mask:
|
|
|
|
case B_Mask:
|
|
|
|
clearance.x += pad->GetSolderMaskMargin();
|
|
|
|
clearance.y += pad->GetSolderMaskMargin();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case F_Paste:
|
|
|
|
case B_Paste:
|
|
|
|
clearance += pad->GetSolderPasteMargin();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Our standard TransformShapeWithClearanceToPolygon() routines can't handle differing
|
|
|
|
// x:y clearance values (which get generated when a relative paste margin is used with
|
|
|
|
// an oblong pad). So we apply this huge hack and fake a larger pad to run the transform
|
|
|
|
// on.
|
|
|
|
// Of course being a hack it falls down when dealing with custom shape pads (where the
|
|
|
|
// size is only the size of the anchor), so for those we punt and just use clearance.x.
|
|
|
|
|
|
|
|
if( ( clearance.x < 0 || clearance.x != clearance.y )
|
|
|
|
&& pad->GetShape() != PAD_SHAPE::CUSTOM )
|
|
|
|
{
|
2021-11-11 17:35:01 +00:00
|
|
|
wxSize dummySize = pad->GetSize() + clearance + clearance;
|
|
|
|
|
|
|
|
if( dummySize.x <= 0 || dummySize.y <= 0 )
|
|
|
|
continue;
|
|
|
|
|
2021-10-25 19:50:33 +00:00
|
|
|
PAD dummy( *pad );
|
2021-11-11 17:35:01 +00:00
|
|
|
dummy.SetSize( dummySize );
|
2021-10-25 19:50:33 +00:00
|
|
|
dummy.TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
|
|
|
aMaxError, aErrorLoc );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pad->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, clearance.x,
|
|
|
|
aMaxError, aErrorLoc );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void FOOTPRINT::TransformFPShapesWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
|
|
|
PCB_LAYER_ID aLayer, int aClearance,
|
|
|
|
int aError, ERROR_LOC aErrorLoc,
|
|
|
|
bool aIncludeText,
|
|
|
|
bool aIncludeShapes ) const
|
|
|
|
{
|
|
|
|
std::vector<FP_TEXT*> texts; // List of FP_TEXT to convert
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : GraphicalItems() )
|
|
|
|
{
|
|
|
|
if( item->Type() == PCB_FP_TEXT_T && aIncludeText )
|
|
|
|
{
|
|
|
|
FP_TEXT* text = static_cast<FP_TEXT*>( item );
|
|
|
|
|
|
|
|
if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
|
|
|
|
texts.push_back( text );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
|
|
|
|
{
|
|
|
|
const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
|
|
|
|
|
|
|
|
if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
|
|
|
|
{
|
|
|
|
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
|
|
|
|
aError, aErrorLoc );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( aIncludeText )
|
|
|
|
{
|
|
|
|
if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
|
|
|
|
texts.push_back( &Reference() );
|
|
|
|
|
|
|
|
if( Value().GetLayer() == aLayer && Value().IsVisible() )
|
|
|
|
texts.push_back( &Value() );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( const FP_TEXT* text : texts )
|
|
|
|
{
|
|
|
|
text->TransformTextShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
|
|
|
|
aError, aErrorLoc );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-14 19:16:42 +00:00
|
|
|
static struct FOOTPRINT_DESC
|
2020-02-02 18:40:14 +00:00
|
|
|
{
|
2020-11-14 19:16:42 +00:00
|
|
|
FOOTPRINT_DESC()
|
2020-02-02 18:40:14 +00:00
|
|
|
{
|
2020-08-18 21:42:56 +00:00
|
|
|
ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
|
|
|
|
|
|
|
|
if( layerEnum.Choices().GetCount() == 0 )
|
|
|
|
{
|
|
|
|
layerEnum.Undefined( UNDEFINED_LAYER );
|
|
|
|
|
|
|
|
for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
|
|
|
|
layerEnum.Map( *seq, LSET::Name( *seq ) );
|
|
|
|
}
|
|
|
|
|
2020-02-05 15:04:12 +00:00
|
|
|
wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
|
|
|
|
fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
|
|
|
|
fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
|
|
|
|
|
2020-02-02 18:40:14 +00:00
|
|
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
2020-11-13 15:15:52 +00:00
|
|
|
REGISTER_TYPE( FOOTPRINT );
|
|
|
|
propMgr.AddTypeCast( new TYPE_CAST<FOOTPRINT, BOARD_ITEM> );
|
|
|
|
propMgr.AddTypeCast( new TYPE_CAST<FOOTPRINT, BOARD_ITEM_CONTAINER> );
|
|
|
|
propMgr.InheritsAfter( TYPE_HASH( FOOTPRINT ), TYPE_HASH( BOARD_ITEM ) );
|
|
|
|
propMgr.InheritsAfter( TYPE_HASH( FOOTPRINT ), TYPE_HASH( BOARD_ITEM_CONTAINER ) );
|
|
|
|
|
|
|
|
auto layer = new PROPERTY_ENUM<FOOTPRINT, PCB_LAYER_ID, BOARD_ITEM>( _HKI( "Layer" ),
|
|
|
|
&FOOTPRINT::SetLayer, &FOOTPRINT::GetLayer );
|
2020-02-05 15:04:12 +00:00
|
|
|
layer->SetChoices( fpLayers );
|
2020-10-16 15:51:24 +00:00
|
|
|
propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
|
2020-02-05 15:04:12 +00:00
|
|
|
|
2020-11-13 15:15:52 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
|
|
|
|
&FOOTPRINT::SetReference, &FOOTPRINT::GetReference ) );
|
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Value" ),
|
|
|
|
&FOOTPRINT::SetValue, &FOOTPRINT::GetValue ) );
|
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
|
|
|
|
&FOOTPRINT::SetOrientationDegrees, &FOOTPRINT::GetOrientationDegrees,
|
2020-09-05 16:00:29 +00:00
|
|
|
PROPERTY_DISPLAY::DEGREE ) );
|
2021-05-29 18:12:23 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Clearance Override" ),
|
2020-11-13 15:15:52 +00:00
|
|
|
&FOOTPRINT::SetLocalClearance, &FOOTPRINT::GetLocalClearance,
|
2020-09-05 16:00:29 +00:00
|
|
|
PROPERTY_DISPLAY::DISTANCE ) );
|
2021-05-29 18:12:23 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Solderpaste Margin Override" ),
|
2020-11-13 15:15:52 +00:00
|
|
|
&FOOTPRINT::SetLocalSolderPasteMargin, &FOOTPRINT::GetLocalSolderPasteMargin,
|
2020-09-05 16:00:29 +00:00
|
|
|
PROPERTY_DISPLAY::DISTANCE ) );
|
2021-06-23 22:58:58 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT,
|
|
|
|
double>( _HKI( "Solderpaste Margin Ratio Override" ),
|
|
|
|
&FOOTPRINT::SetLocalSolderPasteMarginRatio,
|
|
|
|
&FOOTPRINT::GetLocalSolderPasteMarginRatio ) );
|
2021-05-29 18:12:23 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Width" ),
|
2021-06-23 22:58:58 +00:00
|
|
|
&FOOTPRINT::SetThermalWidth,
|
|
|
|
&FOOTPRINT::GetThermalWidth,
|
|
|
|
PROPERTY_DISPLAY::DISTANCE ) );
|
2021-05-29 18:12:23 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Thermal Relief Gap" ),
|
2021-06-23 22:58:58 +00:00
|
|
|
&FOOTPRINT::SetThermalGap,
|
|
|
|
&FOOTPRINT::GetThermalGap,
|
|
|
|
PROPERTY_DISPLAY::DISTANCE ) );
|
2020-02-02 18:40:14 +00:00
|
|
|
// TODO zone connection, FPID?
|
|
|
|
}
|
2020-11-14 19:16:42 +00:00
|
|
|
} _FOOTPRINT_DESC;
|