summaryrefslogtreecommitdiff
path: root/pcbnew/class_module.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/class_module.cpp')
-rw-r--r--pcbnew/class_module.cpp1307
1 files changed, 1307 insertions, 0 deletions
diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
new file mode 100644
index 0000000..c1bc66b
--- /dev/null
+++ b/pcbnew/class_module.cpp
@@ -0,0 +1,1307 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
+ * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
+ * Copyright (C) 2015 Wayne Stambaugh <stambaughw@verizon.net>
+ * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the 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
+ */
+
+/**
+ * @file class_module.cpp
+ * @brief MODULE class implementation.
+ */
+
+#include <fctsys.h>
+#include <gr_basic.h>
+#include <wxstruct.h>
+#include <plot_common.h>
+#include <class_drawpanel.h>
+#include <trigo.h>
+#include <confirm.h>
+#include <kicad_string.h>
+#include <pcbnew.h>
+#include <colors_selection.h>
+#include <richio.h>
+#include <filter_reader.h>
+#include <macros.h>
+#include <3d_struct.h>
+#include <msgpanel.h>
+
+#include <class_board.h>
+#include <class_edge_mod.h>
+#include <class_module.h>
+
+
+MODULE::MODULE( BOARD* parent ) :
+ BOARD_ITEM( (BOARD_ITEM*) parent, PCB_MODULE_T ),
+ m_initial_comments( 0 )
+{
+ m_Attributs = MOD_DEFAULT;
+ m_Layer = F_Cu;
+ m_Orient = 0;
+ m_ModuleStatus = MODULE_PADS_LOCKED;
+ m_arflag = 0;
+ m_CntRot90 = m_CntRot180 = 0;
+ m_Surface = 0.0;
+ m_Link = 0;
+ m_LastEditTime = 0;
+ m_LocalClearance = 0;
+ m_LocalSolderMaskMargin = 0;
+ m_LocalSolderPasteMargin = 0;
+ m_LocalSolderPasteMarginRatio = 0.0;
+ m_ZoneConnection = PAD_ZONE_CONN_INHERITED; // Use zone setting by default
+ m_ThermalWidth = 0; // Use zone setting by default
+ m_ThermalGap = 0; // Use zone setting by default
+
+ // These are special and mandatory text fields
+ m_Reference = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_REFERENCE );
+ m_Value = new TEXTE_MODULE( this, TEXTE_MODULE::TEXT_is_VALUE );
+
+ // Reserve one void 3D entry, to avoid problems with void list
+ m_3D_Drawings.PushBack( new S3D_MASTER( this ) );
+}
+
+
+MODULE::MODULE( const MODULE& aModule ) :
+ BOARD_ITEM( aModule )
+{
+ m_Pos = aModule.m_Pos;
+ m_fpid = aModule.m_fpid;
+ m_Layer = aModule.m_Layer;
+ m_Attributs = aModule.m_Attributs;
+ m_ModuleStatus = aModule.m_ModuleStatus;
+ m_Orient = aModule.m_Orient;
+ m_BoundaryBox = aModule.m_BoundaryBox;
+ m_CntRot90 = aModule.m_CntRot90;
+ m_CntRot180 = aModule.m_CntRot180;
+ m_LastEditTime = aModule.m_LastEditTime;
+ m_Link = aModule.m_Link;
+ m_Path = aModule.m_Path; //is this correct behavior?
+
+ m_LocalClearance = aModule.m_LocalClearance;
+ m_LocalSolderMaskMargin = aModule.m_LocalSolderMaskMargin;
+ m_LocalSolderPasteMargin = aModule.m_LocalSolderPasteMargin;
+ m_LocalSolderPasteMarginRatio = aModule.m_LocalSolderPasteMarginRatio;
+ m_ZoneConnection = aModule.m_ZoneConnection;
+ m_ThermalWidth = aModule.m_ThermalWidth;
+ m_ThermalGap = aModule.m_ThermalGap;
+
+ // Copy reference and value.
+ m_Reference = new TEXTE_MODULE( *aModule.m_Reference );
+ m_Reference->SetParent( this );
+
+ m_Value = new TEXTE_MODULE( *aModule.m_Value );
+ m_Value->SetParent( this );
+
+ // Copy auxiliary data: Pads
+ for( D_PAD* pad = aModule.m_Pads; pad; pad = pad->Next() )
+ {
+ D_PAD* newpad = new D_PAD( *pad );
+ newpad->SetParent( this );
+ m_Pads.PushBack( newpad );
+ }
+
+ // Copy auxiliary data: Drawings
+ for( BOARD_ITEM* item = aModule.m_Drawings; item; item = item->Next() )
+ {
+ BOARD_ITEM* newItem;
+
+ switch( item->Type() )
+ {
+ case PCB_MODULE_TEXT_T:
+ case PCB_MODULE_EDGE_T:
+ newItem = static_cast<BOARD_ITEM*>( item->Clone() );
+ newItem->SetParent( this );
+ m_Drawings.PushBack( newItem );
+ break;
+
+ default:
+ wxLogMessage( wxT( "MODULE::Copy() Internal Err: unknown type" ) );
+ break;
+ }
+ }
+
+ // Copy auxiliary data: 3D_Drawings info
+ for( S3D_MASTER* item = aModule.m_3D_Drawings; item; item = item->Next() )
+ {
+ if( item->GetShape3DName().IsEmpty() ) // do not copy empty shapes.
+ continue;
+
+ S3D_MASTER* t3d = new S3D_MASTER( this );
+ t3d->Copy( item );
+ m_3D_Drawings.PushBack( t3d );
+ }
+
+ // Ensure there is at least one item in m_3D_Drawings.
+ if( m_3D_Drawings.GetCount() == 0 )
+ m_3D_Drawings.PushBack( new S3D_MASTER( this ) ); // push a void item
+
+ m_Doc = aModule.m_Doc;
+ m_KeyWord = aModule.m_KeyWord;
+
+ m_arflag = 0;
+
+ // Ensure auxiliary data is up to date
+ CalculateBoundingBox();
+
+ m_initial_comments = aModule.m_initial_comments ?
+ new wxArrayString( *aModule.m_initial_comments ) : 0;
+}
+
+
+MODULE::~MODULE()
+{
+ delete m_Reference;
+ delete m_Value;
+ delete m_initial_comments;
+}
+
+ /**
+ * Function ClearAllNets
+ * Clear (i.e. force the ORPHANED dummy net info) the net info which
+ * depends on a given board for all pads of the footprint.
+ * This is needed when a footprint is copied between the fp editor and
+ * the board editor for instance, because net info become fully broken
+ */
+void MODULE::ClearAllNets()
+{
+ // Force the ORPHANED dummy net info for all pads.
+ // ORPHANED dummy net does not depend on a board
+ for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
+ pad->SetNetCode( NETINFO_LIST::FORCE_ORPHANED );
+}
+
+
+/* Draw the anchor cross (vertical)
+ * Must be done after the pads, because drawing the hole will erase overwrite
+ * every thing already drawn.
+ */
+void MODULE::DrawAncre( EDA_DRAW_PANEL* panel, wxDC* DC, const wxPoint& offset,
+ int dim_ancre, GR_DRAWMODE draw_mode )
+{
+ GRSetDrawMode( DC, draw_mode );
+
+ if( GetBoard()->IsElementVisible( ANCHOR_VISIBLE ) )
+ {
+ GRDrawAnchor( panel->GetClipBox(), DC, m_Pos.x, m_Pos.y,
+ dim_ancre,
+ g_ColorsSettings.GetItemColor( ANCHOR_VISIBLE ) );
+ }
+}
+
+
+void MODULE::Copy( MODULE* aModule )
+{
+ m_Pos = aModule->m_Pos;
+ m_Layer = aModule->m_Layer;
+ m_fpid = aModule->m_fpid;
+ m_Attributs = aModule->m_Attributs;
+ m_ModuleStatus = aModule->m_ModuleStatus;
+ m_Orient = aModule->m_Orient;
+ m_BoundaryBox = aModule->m_BoundaryBox;
+ m_CntRot90 = aModule->m_CntRot90;
+ m_CntRot180 = aModule->m_CntRot180;
+ m_LastEditTime = aModule->m_LastEditTime;
+ m_Link = aModule->m_Link;
+ m_Path = aModule->m_Path; //is this correct behavior?
+ SetTimeStamp( GetNewTimeStamp() );
+
+ m_LocalClearance = aModule->m_LocalClearance;
+ m_LocalSolderMaskMargin = aModule->m_LocalSolderMaskMargin;
+ m_LocalSolderPasteMargin = aModule->m_LocalSolderPasteMargin;
+ m_LocalSolderPasteMarginRatio = aModule->m_LocalSolderPasteMarginRatio;
+ m_ZoneConnection = aModule->m_ZoneConnection;
+ m_ThermalWidth = aModule->m_ThermalWidth;
+ m_ThermalGap = aModule->m_ThermalGap;
+
+ // Copy reference and value.
+ m_Reference->Copy( aModule->m_Reference );
+ m_Value->Copy( aModule->m_Value );
+
+ // Copy auxiliary data: Pads
+ m_Pads.DeleteAll();
+
+ for( D_PAD* pad = aModule->m_Pads; pad; pad = pad->Next() )
+ {
+ D_PAD* newpad = new D_PAD( this );
+ newpad->Copy( pad );
+ m_Pads.PushBack( newpad );
+ }
+
+ // Copy auxiliary data: Drawings
+ m_Drawings.DeleteAll();
+
+ for( BOARD_ITEM* item = aModule->m_Drawings; item; item = item->Next() )
+ {
+ switch( item->Type() )
+ {
+ case PCB_MODULE_TEXT_T:
+ {
+ TEXTE_MODULE* textm = new TEXTE_MODULE( this );
+ textm->Copy( static_cast<TEXTE_MODULE*>( item ) );
+ m_Drawings.PushBack( textm );
+ break;
+ }
+
+ case PCB_MODULE_EDGE_T:
+ {
+ EDGE_MODULE * edge;
+ edge = new EDGE_MODULE( this );
+ edge->Copy( (EDGE_MODULE*) item );
+ m_Drawings.PushBack( edge );
+ break;
+ }
+
+ default:
+ wxLogMessage( wxT( "MODULE::Copy() Internal Err: unknown type" ) );
+ break;
+ }
+ }
+
+ // Copy auxiliary data: 3D_Drawings info
+ m_3D_Drawings.DeleteAll();
+
+ // Ensure there is one (or more) item in m_3D_Drawings
+ m_3D_Drawings.PushBack( new S3D_MASTER( this ) ); // push a void item
+
+ for( S3D_MASTER* item = aModule->m_3D_Drawings; item; item = item->Next() )
+ {
+ if( item->GetShape3DName().IsEmpty() ) // do not copy empty shapes.
+ continue;
+
+ S3D_MASTER* t3d = m_3D_Drawings;
+
+ if( t3d && t3d->GetShape3DName().IsEmpty() ) // The first entry can
+ { // exist, but is empty : use it.
+ t3d->Copy( item );
+ }
+ else
+ {
+ t3d = new S3D_MASTER( this );
+ t3d->Copy( item );
+ m_3D_Drawings.PushBack( t3d );
+ }
+ }
+
+ m_Doc = aModule->m_Doc;
+ m_KeyWord = aModule->m_KeyWord;
+
+ // Ensure auxiliary data is up to date
+ CalculateBoundingBox();
+}
+
+
+void MODULE::Add( BOARD_ITEM* aBoardItem, bool doAppend )
+{
+ switch( aBoardItem->Type() )
+ {
+ case PCB_MODULE_TEXT_T:
+ // Only user texts can be added this way. Reference and value are not hold in the DLIST.
+ assert( static_cast<TEXTE_MODULE*>( aBoardItem )->GetType() == TEXTE_MODULE::TEXT_is_DIVERS );
+
+ // no break
+
+ case PCB_MODULE_EDGE_T:
+ if( doAppend )
+ m_Drawings.PushBack( aBoardItem );
+ else
+ m_Drawings.PushFront( aBoardItem );
+ break;
+
+ case PCB_PAD_T:
+ if( doAppend )
+ m_Pads.PushBack( static_cast<D_PAD*>( aBoardItem ) );
+ else
+ m_Pads.PushFront( static_cast<D_PAD*>( aBoardItem ) );
+ break;
+
+ default:
+ {
+ wxString msg;
+ msg.Printf( wxT( "MODULE::Add() needs work: BOARD_ITEM type (%d) not handled" ),
+ aBoardItem->Type() );
+ wxFAIL_MSG( msg );
+
+ return;
+ }
+ }
+
+ aBoardItem->SetParent( this );
+}
+
+
+BOARD_ITEM* MODULE::Remove( BOARD_ITEM* aBoardItem )
+{
+ switch( aBoardItem->Type() )
+ {
+ case PCB_MODULE_TEXT_T:
+ // Only user texts can be removed this way. Reference and value are not hold in the DLIST.
+ assert( static_cast<TEXTE_MODULE*>( aBoardItem )->GetType() == TEXTE_MODULE::TEXT_is_DIVERS );
+
+ // no break
+
+ case PCB_MODULE_EDGE_T:
+ return m_Drawings.Remove( aBoardItem );
+
+ case PCB_PAD_T:
+ return m_Pads.Remove( static_cast<D_PAD*>( aBoardItem ) );
+
+ default:
+ {
+ wxString msg;
+ msg.Printf( wxT( "MODULE::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
+ aBoardItem->Type() );
+ wxFAIL_MSG( msg );
+ }
+ }
+
+ return NULL;
+}
+
+
+void MODULE::CopyNetlistSettings( MODULE* aModule, bool aCopyLocalSettings )
+{
+ // Don't do anything foolish like trying to copy to yourself.
+ wxCHECK_RET( aModule != NULL && aModule != this, wxT( "Cannot copy to NULL or yourself." ) );
+
+ // Not sure what to do with the value field. Use netlist for now.
+ aModule->SetPosition( GetPosition() );
+
+ if( aModule->GetLayer() != GetLayer() )
+ aModule->Flip( aModule->GetPosition() );
+
+ if( aModule->GetOrientation() != GetOrientation() )
+ aModule->Rotate( aModule->GetPosition(), GetOrientation() );
+
+ aModule->SetLocked( IsLocked() );
+
+ if( aCopyLocalSettings )
+ {
+ aModule->SetLocalSolderMaskMargin( GetLocalSolderMaskMargin() );
+ aModule->SetLocalClearance( GetLocalClearance() );
+ aModule->SetLocalSolderPasteMargin( GetLocalSolderPasteMargin() );
+ aModule->SetLocalSolderPasteMarginRatio( GetLocalSolderPasteMarginRatio() );
+ aModule->SetZoneConnection( GetZoneConnection() );
+ aModule->SetThermalWidth( GetThermalWidth() );
+ aModule->SetThermalGap( GetThermalGap() );
+ }
+
+ for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
+ {
+ // Fix me: if aCopyLocalSettings == true, for "multiple" pads
+ // (set of pads having the same name/number) this is broken
+ // because we copy settings from the first pad found.
+ // When old and new footprints have very few differences, a better
+ // algo can be used.
+ D_PAD* oldPad = FindPadByName( pad->GetPadName() );
+
+ if( oldPad )
+ oldPad->CopyNetlistSettings( pad, aCopyLocalSettings );
+ }
+
+ // Not sure about copying description, keywords, 3D models or any other
+ // local user changes to footprint. Stick with the new footprint settings
+ // called out in the footprint loaded in the netlist.
+ aModule->CalculateBoundingBox();
+}
+
+
+void MODULE::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode,
+ const wxPoint& aOffset )
+{
+ if( (m_Flags & DO_NOT_DRAW) || (IsMoving()) )
+ return;
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ if( pad->IsMoving() )
+ continue;
+
+ pad->Draw( aPanel, aDC, aDrawMode, aOffset );
+ }
+
+ BOARD* brd = GetBoard();
+
+ // Draws footprint anchor
+ DrawAncre( aPanel, aDC, aOffset, DIM_ANCRE_MODULE, aDrawMode );
+
+ // Draw graphic items
+ if( brd->IsElementVisible( MOD_REFERENCES_VISIBLE ) )
+ {
+ if( !(m_Reference->IsMoving()) )
+ m_Reference->Draw( aPanel, aDC, aDrawMode, aOffset );
+ }
+
+ if( brd->IsElementVisible( MOD_VALUES_VISIBLE ) )
+ {
+ if( !(m_Value->IsMoving()) )
+ m_Value->Draw( aPanel, aDC, aDrawMode, aOffset );
+ }
+
+ for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
+ {
+ if( item->IsMoving() )
+ continue;
+
+ switch( item->Type() )
+ {
+ case PCB_MODULE_TEXT_T:
+ case PCB_MODULE_EDGE_T:
+ item->Draw( aPanel, aDC, aDrawMode, aOffset );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Enable these line to draw m_BoundaryBox (debug tests purposes only)
+#if 0
+ GRRect( aPanel->GetClipBox(), aDC, m_BoundaryBox, 0, BROWN );
+#endif
+
+}
+
+
+void MODULE::DrawEdgesOnly( EDA_DRAW_PANEL* panel, wxDC* DC, const wxPoint& offset,
+ GR_DRAWMODE draw_mode )
+{
+ for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
+ {
+ switch( item->Type() )
+ {
+ case PCB_MODULE_EDGE_T:
+ item->Draw( panel, DC, draw_mode, offset );
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+void MODULE::CalculateBoundingBox()
+{
+ m_BoundaryBox = GetFootprintRect();
+ m_Surface = std::abs( (double) m_BoundaryBox.GetWidth() * m_BoundaryBox.GetHeight() );
+}
+
+
+EDA_RECT MODULE::GetFootprintRect() const
+{
+ EDA_RECT area;
+
+ area.SetOrigin( m_Pos );
+ area.SetEnd( m_Pos );
+ area.Inflate( Millimeter2iu( 0.25 ) ); // Give a min size to the area
+
+ for( const BOARD_ITEM* item = m_Drawings.GetFirst(); item; item = item->Next() )
+ {
+ const EDGE_MODULE* edge = dyn_cast<const EDGE_MODULE*>( item );
+
+ if( edge )
+ area.Merge( edge->GetBoundingBox() );
+ }
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ area.Merge( pad->GetBoundingBox() );
+
+ return area;
+}
+
+
+const EDA_RECT MODULE::GetBoundingBox() const
+{
+ EDA_RECT area = GetFootprintRect();
+
+ // Calculate extended area including text fields
+ area.Merge( m_Reference->GetBoundingBox() );
+ area.Merge( m_Value->GetBoundingBox() );
+
+ // 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).
+ BOARD* board = GetBoard();
+ if( board )
+ {
+ int biggest_clearance = board->GetDesignSettings().GetBiggestClearanceValue();
+ area.Inflate( biggest_clearance );
+ }
+
+ return area;
+}
+
+
+/* Virtual function, from EDA_ITEM.
+ * display module info on MsgPanel
+ */
+void MODULE::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
+{
+ int nbpad;
+ wxString msg;
+
+ aList.push_back( MSG_PANEL_ITEM( m_Reference->GetShownText(), m_Value->GetShownText(), DARKCYAN ) );
+
+ // Display last date the component was edited (useful in Module Editor).
+ wxDateTime date( m_LastEditTime );
+
+ if( m_LastEditTime && date.IsValid() )
+ // Date format: see http://www.cplusplus.com/reference/ctime/strftime
+ msg = date.Format( wxT( "%b %d, %Y" ) ); // Abbreviated_month_name Day, Year
+ else
+ msg = _( "Unknown" );
+
+ aList.push_back( MSG_PANEL_ITEM( _( "Last Change" ), msg, BROWN ) );
+
+ // display schematic path
+ aList.push_back( MSG_PANEL_ITEM( _( "Netlist Path" ), m_Path, BROWN ) );
+
+ aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), GetLayerName(), RED ) );
+
+ EDA_ITEM* PtStruct = m_Pads;
+ nbpad = 0;
+
+ while( PtStruct )
+ {
+ nbpad++;
+ PtStruct = PtStruct->Next();
+ }
+
+ msg.Printf( wxT( "%d" ), nbpad );
+ aList.push_back( MSG_PANEL_ITEM( _( "Pads" ), msg, BLUE ) );
+
+ msg = wxT( ".." );
+
+ if( IsLocked() )
+ msg[0] = 'L';
+
+ if( m_ModuleStatus & MODULE_is_PLACED )
+ msg[1] = 'P';
+
+ aList.push_back( MSG_PANEL_ITEM( _( "Status" ), msg, MAGENTA ) );
+
+ msg.Printf( wxT( "%.1f" ), m_Orient / 10.0 );
+ aList.push_back( MSG_PANEL_ITEM( _( "Angle" ), msg, BROWN ) );
+
+ // Controls on right side of the dialog
+ switch( m_Attributs & 255 )
+ {
+ case 0:
+ msg = _( "Normal" );
+ break;
+
+ case MOD_CMS:
+ msg = _( "Insert" );
+ break;
+
+ case MOD_VIRTUAL:
+ msg = _( "Virtual" );
+ break;
+
+ default:
+ msg = wxT( "???" );
+ break;
+ }
+
+ aList.push_back( MSG_PANEL_ITEM( _( "Attributes" ), msg, BROWN ) );
+ aList.push_back( MSG_PANEL_ITEM( _( "Footprint" ), FROM_UTF8( m_fpid.Format().c_str() ), BLUE ) );
+
+ msg = _( "No 3D shape" );
+ // Search the first active 3D shape in list
+ for( S3D_MASTER* struct3D = m_3D_Drawings; struct3D; struct3D = struct3D->Next() )
+ {
+ if( !struct3D->GetShape3DName().IsEmpty() )
+ {
+ msg = struct3D->GetShape3DName();
+ break;
+ }
+ }
+
+ aList.push_back( MSG_PANEL_ITEM( _( "3D-Shape" ), msg, RED ) );
+
+ wxString doc, keyword;
+ doc.Printf( _( "Doc: %s" ), GetChars( m_Doc ) );
+ keyword.Printf( _( "Key Words: %s" ), GetChars( m_KeyWord ) );
+ aList.push_back( MSG_PANEL_ITEM( doc, keyword, BLACK ) );
+}
+
+
+bool MODULE::HitTest( const wxPoint& aPosition ) const
+{
+ return m_BoundaryBox.Contains( aPosition );
+}
+
+
+bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
+{
+ EDA_RECT arect = aRect;
+ arect.Inflate( aAccuracy );
+
+ if( aContained )
+ return arect.Contains( m_BoundaryBox );
+ else
+ return m_BoundaryBox.Intersects( arect );
+}
+
+
+D_PAD* MODULE::FindPadByName( const wxString& aPadName ) const
+{
+ wxString buf;
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ pad->StringPadName( buf );
+#if 1
+ if( buf.CmpNoCase( aPadName ) == 0 ) // why case insensitive?
+#else
+ if( buf == aPadName )
+#endif
+ return pad;
+ }
+
+ return NULL;
+}
+
+
+D_PAD* MODULE::GetPad( const wxPoint& aPosition, LSET aLayerMask )
+{
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ // ... and on the correct layer.
+ if( !( pad->GetLayerSet() & aLayerMask ).any() )
+ continue;
+
+ if( pad->HitTest( aPosition ) )
+ return pad;
+ }
+
+ return NULL;
+}
+
+
+unsigned MODULE::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
+{
+ if( aIncludeNPTH )
+ return m_Pads.GetCount();
+
+ unsigned cnt = 0;
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
+ continue;
+
+ cnt++;
+ }
+
+ return cnt;
+}
+
+
+unsigned MODULE::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
+{
+ std::set<wxUint32> usedNames;
+
+ // Create a set of used pad numbers
+ for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
+ {
+ // 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
+ if( pad->GetPadName().IsEmpty() )
+ continue;
+
+ if( !aIncludeNPTH )
+ {
+ // skip NPTH
+ if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
+ {
+ continue;
+ }
+ }
+
+ usedNames.insert( pad->GetPackedPadName() );
+ }
+
+ return usedNames.size();
+}
+
+
+void MODULE::Add3DModel( S3D_MASTER* a3DModel )
+{
+ a3DModel->SetParent( this );
+ m_3D_Drawings.PushBack( a3DModel );
+}
+
+
+// see class_module.h
+SEARCH_RESULT MODULE::Visit( INSPECTOR* inspector, const void* testData,
+ const KICAD_T scanTypes[] )
+{
+ KICAD_T stype;
+ SEARCH_RESULT result = SEARCH_CONTINUE;
+ const KICAD_T* p = scanTypes;
+ bool done = false;
+
+#if 0 && defined(DEBUG)
+ std::cout << GetClass().mb_str() << ' ';
+#endif
+
+ while( !done )
+ {
+ stype = *p;
+
+ switch( stype )
+ {
+ case PCB_MODULE_T:
+ result = inspector->Inspect( this, testData ); // inspect me
+ ++p;
+ break;
+
+ case PCB_PAD_T:
+ result = IterateForward( m_Pads, inspector, testData, p );
+ ++p;
+ break;
+
+ case PCB_MODULE_TEXT_T:
+ result = inspector->Inspect( m_Reference, testData );
+
+ if( result == SEARCH_QUIT )
+ break;
+
+ result = inspector->Inspect( m_Value, testData );
+
+ if( result == SEARCH_QUIT )
+ break;
+
+ // m_Drawings can hold TYPETEXTMODULE also, so fall thru
+
+ case PCB_MODULE_EDGE_T:
+ result = IterateForward( m_Drawings, inspector, testData, p );
+
+ // skip over any types handled in the above call.
+ for( ; ; )
+ {
+ switch( stype = *++p )
+ {
+ case PCB_MODULE_TEXT_T:
+ case PCB_MODULE_EDGE_T:
+ continue;
+
+ default:
+ ;
+ }
+
+ break;
+ }
+
+ break;
+
+ default:
+ done = true;
+ break;
+ }
+
+ if( result == SEARCH_QUIT )
+ break;
+ }
+
+ return result;
+}
+
+
+wxString MODULE::GetSelectMenuText() const
+{
+ wxString text;
+ text.Printf( _( "Footprint %s on %s" ),
+ GetChars ( GetReference() ),
+ GetChars ( GetLayerName() ) );
+
+ return text;
+}
+
+
+EDA_ITEM* MODULE::Clone() const
+{
+ return new MODULE( *this );
+}
+
+
+void MODULE::RunOnChildren( boost::function<void (BOARD_ITEM*)> aFunction )
+{
+ try
+ {
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ aFunction( static_cast<BOARD_ITEM*>( pad ) );
+
+ for( BOARD_ITEM* drawing = m_Drawings; drawing; drawing = drawing->Next() )
+ aFunction( drawing );
+
+ aFunction( static_cast<BOARD_ITEM*>( m_Reference ) );
+ aFunction( static_cast<BOARD_ITEM*>( m_Value ) );
+ }
+ catch( boost::bad_function_call& e )
+ {
+ DisplayError( NULL, wxT( "Error running MODULE::RunOnChildren" ) );
+ }
+}
+
+
+void MODULE::ViewUpdate( int aUpdateFlags )
+{
+ if( !m_view )
+ return;
+
+ // Update the module itself
+ VIEW_ITEM::ViewUpdate( aUpdateFlags );
+
+ // Update pads
+ for( D_PAD* pad = m_Pads.GetFirst(); pad; pad = pad->Next() )
+ pad->ViewUpdate( aUpdateFlags );
+
+ // Update module's drawing (mostly silkscreen)
+ for( BOARD_ITEM* drawing = m_Drawings.GetFirst(); drawing; drawing = drawing->Next() )
+ drawing->ViewUpdate( aUpdateFlags );
+
+ // Update module's texts
+ m_Reference->ViewUpdate( aUpdateFlags );
+ m_Value->ViewUpdate( aUpdateFlags );
+}
+
+
+void MODULE::ViewGetLayers( int aLayers[], int& aCount ) const
+{
+ aCount = 2;
+ aLayers[0] = ITEM_GAL_LAYER( ANCHOR_VISIBLE );
+
+ switch( m_Layer )
+ {
+
+ default:
+ wxASSERT_MSG( false, "Illegal layer" ); // do you really have modules placed on other layers?
+ // pass through
+ case F_Cu:
+ aLayers[1] = ITEM_GAL_LAYER( MOD_FR_VISIBLE );
+ break;
+
+ case B_Cu:
+ aLayers[1] = ITEM_GAL_LAYER( MOD_BK_VISIBLE );
+ break;
+ }
+}
+
+
+unsigned int MODULE::ViewGetLOD( int aLayer ) const
+{
+ // Currently there is only one layer, so there is nothing to check
+// if( aLayer == ITEM_GAL_LAYER( ANCHOR_VISIBLE ) )
+ return 30;
+}
+
+
+const BOX2I MODULE::ViewBBox() const
+{
+ EDA_RECT fpRect = GetFootprintRect();
+
+ return BOX2I( VECTOR2I( fpRect.GetOrigin() ), VECTOR2I( fpRect.GetSize() ) );
+}
+
+
+/* Test for validity of the name in a library of the footprint
+ * ( no spaces, dir separators ... )
+ * return true if the given name is valid
+ * static function
+ */
+bool MODULE::IsLibNameValid( const wxString & aName )
+{
+ const wxChar * invalids = StringLibNameInvalidChars( false );
+
+ if( aName.find_first_of( invalids ) != std::string::npos )
+ return false;
+
+ return true;
+}
+
+
+/* Test for validity of the name of a footprint to be used in a footprint library
+ * ( no spaces, dir separators ... )
+ * param bool aUserReadable = false to get the list of invalid chars
+ * true to get a readable form (i.e ' ' = 'space' '\t'= 'tab')
+ * return a constant string giving the list of invalid chars in lib name
+ * static function
+ */
+const wxChar* MODULE::StringLibNameInvalidChars( bool aUserReadable )
+{
+ static const wxChar invalidChars[] = wxT("%$\t \"\\/");
+ static const wxChar invalidCharsReadable[] = wxT("% $ 'tab' 'space' \\ \" /");
+
+ if( aUserReadable )
+ return invalidCharsReadable;
+ else
+ return invalidChars;
+}
+
+
+void MODULE::Move( const wxPoint& aMoveVector )
+{
+ wxPoint newpos = m_Pos + aMoveVector;
+ SetPosition( newpos );
+}
+
+
+void MODULE::Rotate( const wxPoint& aRotCentre, double aAngle )
+{
+ wxPoint newpos = m_Pos;
+ RotatePoint( &newpos, aRotCentre, aAngle );
+ SetPosition( newpos );
+ SetOrientation( GetOrientation() + aAngle );
+}
+
+
+void MODULE::Flip( const wxPoint& aCentre )
+{
+ // Move module to its final position:
+ wxPoint finalPos = m_Pos;
+ MIRROR( finalPos.y, aCentre.y ); /// Mirror the Y position
+ SetPosition( finalPos );
+
+ // Flip layer
+ SetLayer( FlipLayer( GetLayer() ) );
+
+ // Reverse mirror orientation.
+ m_Orient = -m_Orient;
+ NORMALIZE_ANGLE_POS( m_Orient );
+
+ // Mirror pads to other side of board about the x axis, i.e. vertically.
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ pad->Flip( m_Pos );
+
+ // Mirror reference and value.
+ m_Reference->Flip( m_Pos );
+ m_Value->Flip( m_Pos );
+
+ // Reverse mirror module graphics and texts.
+ for( EDA_ITEM* item = m_Drawings; item; item = item->Next() )
+ {
+ switch( item->Type() )
+ {
+ case PCB_MODULE_EDGE_T:
+ ( (EDGE_MODULE*) item )->Flip( m_Pos );
+ break;
+
+ case PCB_MODULE_TEXT_T:
+ static_cast<TEXTE_MODULE*>( item )->Flip( m_Pos );
+ break;
+
+ default:
+ wxMessageBox( wxT( "MODULE::Flip() error: Unknown Draw Type" ) );
+ break;
+ }
+ }
+
+ CalculateBoundingBox();
+}
+
+
+void MODULE::SetPosition( const wxPoint& newpos )
+{
+ wxPoint delta = newpos - m_Pos;
+
+ m_Pos += delta;
+ m_Reference->SetTextPosition( m_Reference->GetTextPosition() + delta );
+ m_Value->SetTextPosition( m_Value->GetTextPosition() + delta );
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ pad->SetPosition( pad->GetPosition() + delta );
+ }
+
+ for( EDA_ITEM* item = m_Drawings; item; item = item->Next() )
+ {
+ switch( item->Type() )
+ {
+ case PCB_MODULE_EDGE_T:
+ {
+ EDGE_MODULE* pt_edgmod = (EDGE_MODULE*) item;
+ pt_edgmod->SetDrawCoord();
+ break;
+ }
+
+ case PCB_MODULE_TEXT_T:
+ {
+ TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
+ text->SetTextPosition( text->GetTextPosition() + delta );
+ break;
+ }
+
+ default:
+ wxMessageBox( wxT( "Draw type undefined." ) );
+ break;
+ }
+ }
+
+ CalculateBoundingBox();
+}
+
+
+void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector )
+{
+ /* 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
+ * - Draw coordinates are updated
+ */
+
+
+ // Update (move) the relative coordinates relative to the new anchor point.
+ wxPoint moveVector = aMoveVector;
+ RotatePoint( &moveVector, -GetOrientation() );
+
+ // Update of the reference and value.
+ m_Reference->SetPos0( m_Reference->GetPos0() + moveVector );
+ m_Reference->SetDrawCoord();
+ m_Value->SetPos0( m_Value->GetPos0() + moveVector );
+ m_Value->SetDrawCoord();
+
+ // Update the pad local coordinates.
+ for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
+ {
+ pad->SetPos0( pad->GetPos0() + moveVector );
+ pad->SetDrawCoord();
+ }
+
+ // Update the draw element coordinates.
+ for( EDA_ITEM* item = GraphicalItems(); item; item = item->Next() )
+ {
+ switch( item->Type() )
+ {
+ case PCB_MODULE_EDGE_T:
+ {
+ EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( item );
+ edge->m_Start0 += moveVector;
+ edge->m_End0 += moveVector;
+ edge->SetDrawCoord();
+ break;
+ }
+
+ case PCB_MODULE_TEXT_T:
+ {
+ TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
+ text->SetPos0( text->GetPos0() + moveVector );
+ text->SetDrawCoord();
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ CalculateBoundingBox();
+}
+
+
+void MODULE::SetOrientation( double newangle )
+{
+ double angleChange = newangle - m_Orient; // change in rotation
+
+ NORMALIZE_ANGLE_POS( newangle );
+
+ m_Orient = newangle;
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ {
+ pad->SetOrientation( pad->GetOrientation() + angleChange );
+ pad->SetDrawCoord();
+ }
+
+ // Update of the reference and value.
+ m_Reference->SetDrawCoord();
+ m_Value->SetDrawCoord();
+
+ // Displace contours and text of the footprint.
+ for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
+ {
+ if( item->Type() == PCB_MODULE_EDGE_T )
+ {
+ static_cast<EDGE_MODULE*>( item )->SetDrawCoord();
+ }
+ else if( item->Type() == PCB_MODULE_TEXT_T )
+ {
+ static_cast<TEXTE_MODULE*>( item )->SetDrawCoord();
+ }
+ }
+
+ CalculateBoundingBox();
+}
+
+BOARD_ITEM* MODULE::DuplicateAndAddItem( const BOARD_ITEM* aItem,
+ bool aIncrementPadNumbers )
+{
+ BOARD_ITEM* new_item = NULL;
+
+ switch( aItem->Type() )
+ {
+ case PCB_PAD_T:
+ {
+ D_PAD* new_pad = new D_PAD( *static_cast<const D_PAD*>( aItem ) );
+
+ Pads().PushBack( new_pad );
+ new_item = new_pad;
+ break;
+ }
+
+ case PCB_MODULE_TEXT_T:
+ {
+ const TEXTE_MODULE* old_text = static_cast<const TEXTE_MODULE*>( aItem );
+
+ // do not duplicate value or reference fields
+ // (there can only be one of each)
+ if( old_text->GetType() == TEXTE_MODULE::TEXT_is_DIVERS )
+ {
+ TEXTE_MODULE* new_text = new TEXTE_MODULE( *old_text );
+
+ GraphicalItems().PushBack( new_text );
+ new_item = new_text;
+ }
+ break;
+ }
+
+ case PCB_MODULE_EDGE_T:
+ {
+ EDGE_MODULE* new_edge = new EDGE_MODULE(
+ *static_cast<const EDGE_MODULE*>(aItem) );
+
+ GraphicalItems().PushBack( new_edge );
+ new_item = new_edge;
+ break;
+ }
+
+ case PCB_MODULE_T:
+ // Ignore the module itself
+ break;
+
+ default:
+ // Un-handled item for duplication
+ wxASSERT_MSG( false, "Duplication not supported for items of class "
+ + aItem->GetClass() );
+ break;
+ }
+
+ if( aIncrementPadNumbers && new_item )
+ {
+ new_item->IncrementItemReference();
+ }
+
+ return new_item;
+}
+
+
+wxString MODULE::GetNextPadName( bool aFillSequenceGaps ) const
+{
+ std::set<int> usedNumbers;
+
+ // Create a set of used pad numbers
+ for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
+ {
+ int padNumber = getTrailingInt( pad->GetPadName() );
+ usedNumbers.insert( padNumber );
+ }
+
+ const int nextNum = getNextNumberInSequence( usedNumbers, aFillSequenceGaps );
+
+ return wxString::Format( wxT( "%i" ), nextNum );
+}
+
+
+wxString MODULE::GetReferencePrefix() const
+{
+ wxString prefix = GetReference();
+
+ int strIndex = prefix.length() - 1;
+ while( strIndex >= 0 )
+ {
+ const wxUniChar chr = prefix.GetChar( strIndex );
+
+ // numeric suffix
+ if( chr >= '0' && chr <= '9' )
+ break;
+
+ strIndex--;
+ }
+
+ prefix = prefix.Mid( 0, strIndex );
+
+ return prefix;
+}
+
+
+bool MODULE::IncrementItemReference()
+{
+ // Take the next available module number
+ return IncrementReference( true );
+}
+
+
+bool MODULE::IncrementReference( bool aFillSequenceGaps )
+{
+ BOARD* board = GetBoard();
+
+ if( !board )
+ return false;
+
+ bool success = false;
+ const wxString prefix = GetReferencePrefix();
+ const wxString newReference = board->GetNextModuleReferenceWithPrefix(
+ prefix, aFillSequenceGaps );
+
+ if( !newReference.IsEmpty() )
+ {
+ SetReference( newReference );
+ success = true;
+ }
+
+ return success;
+}
+
+
+double MODULE::PadCoverageRatio() const
+{
+ double padArea = 0.0;
+ double moduleArea = GetFootprintRect().GetArea();
+
+ for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+ padArea += pad->GetBoundingBox().GetArea();
+
+ if( moduleArea == 0.0 )
+ return 1.0;
+
+ double ratio = padArea / moduleArea;
+
+ return std::min( ratio, 1.0 );
+}