kicad/gerbview/class_GERBER.cpp

558 lines
18 KiB
C++
Raw Normal View History

/**
* @file class_GERBER.cpp
* a GERBER class handle for a given layer info about used D_CODES and how the layer is drawn
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2013 Jean-Pierre Charras jp.charras at wanadoo.fr
* Copyright (C) 1992-2013 KiCad Developers, see change_log.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
*/
#include <fctsys.h>
#include <common.h>
#include <class_drawpanel.h>
#include <confirm.h>
#include <macros.h>
#include <gerbview.h>
#include <gerbview_frame.h>
#include <class_GERBER.h>
#include <class_X2_gerber_attributes.h>
#include <algorithm>
/**
* Function scaletoIU
* converts a distance given in floating point to our internal units
*/
extern int scaletoIU( double aCoord, bool isMetric ); // defined it rs274d_read_XY_and_IJ_coordiantes.cpp
/* Format Gerber: NOTES:
* Tools and D_CODES
* tool number (identification of shapes)
* 1 to 999
*
* D_CODES:
* D01 ... D9 = action codes:
* D01 = activating light (lower pen) when di placement
* D02 = light extinction (lift pen) when di placement
* D03 Flash
* D09 = VAPE Flash
* D10 ... = Indentification Tool (Opening)
*
* For tools:
* DCode min = D10
* DCode max = 999
*/
GERBER_LAYER::GERBER_LAYER()
{
ResetDefaultValues();
}
GERBER_LAYER::~GERBER_LAYER()
{
}
void GERBER_LAYER::ResetDefaultValues()
{
m_LayerName = wxT( "no name" ); // Layer name from the LN command
m_LayerNegative = false; // true = Negative Layer
m_StepForRepeat.x = m_StepForRepeat.y = 0; // X and Y offsets for Step and Repeat command
m_XRepeatCount = 1; // The repeat count on X axis
m_YRepeatCount = 1; // The repeat count on Y axis
m_StepForRepeatMetric = false; // false = Inches, true = metric
}
GERBER_IMAGE::GERBER_IMAGE( GERBVIEW_FRAME* aParent, int aLayer )
{
m_Parent = aParent;
m_GraphicLayer = aLayer; // Graphic layer Number
m_Selected_Tool = FIRST_DCODE;
m_FileFunction = NULL; // file function parameters
ResetDefaultValues();
for( unsigned ii = 0; ii < DIM( m_Aperture_List ); ii++ )
m_Aperture_List[ii] = 0;
}
GERBER_IMAGE::~GERBER_IMAGE()
{
for( unsigned ii = 0; ii < DIM( m_Aperture_List ); ii++ )
{
delete m_Aperture_List[ii];
}
delete m_FileFunction;
}
/*
* Function GetItemsList
* returns the first GERBER_DRAW_ITEM * item of the items list
*/
GERBER_DRAW_ITEM * GERBER_IMAGE::GetItemsList()
{
return m_Parent->GetItemsList();
}
D_CODE* GERBER_IMAGE::GetDCODE( int aDCODE, bool create )
{
unsigned ndx = aDCODE - FIRST_DCODE;
if( ndx < (unsigned) DIM( m_Aperture_List ) )
{
// lazily create the D_CODE if it does not exist.
if( create )
{
if( m_Aperture_List[ndx] == NULL )
m_Aperture_List[ndx] = new D_CODE( ndx + FIRST_DCODE );
}
return m_Aperture_List[ndx];
}
return NULL;
}
APERTURE_MACRO* GERBER_IMAGE::FindApertureMacro( const APERTURE_MACRO& aLookup )
{
APERTURE_MACRO_SET::iterator iter = m_aperture_macros.find( aLookup );
if( iter != m_aperture_macros.end() )
{
APERTURE_MACRO* pam = (APERTURE_MACRO*) &(*iter);
return pam;
}
return NULL; // not found
}
void GERBER_IMAGE::ResetDefaultValues()
{
m_InUse = false;
m_GBRLayerParams.ResetDefaultValues();
m_FileName.Empty();
m_ImageName = wxT( "no name" ); // Image name from the IN command
m_ImageNegative = false; // true = Negative image
m_IsX2_file = false; // true only if a %TF, %TA or %TD command
delete m_FileFunction; // file function parameters
m_FileFunction = NULL;
m_MD5_value.Empty(); // MD5 value found in a %TF.MD5 command
m_PartString.Empty(); // string found in a %TF.Part command
m_hasNegativeItems = -1; // set to uninitialized
m_ImageJustifyOffset = wxPoint(0,0); // Image justify Offset
m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
m_GerbMetric = false; // false = Inches (default), true = metric
m_Relative = false; // false = absolute Coord,
// true = relative Coord
m_NoTrailingZeros = false; // true: trailing zeros deleted
m_DecimalFormat = false; // true: use floating point notations for coordinates
m_ImageOffset.x = m_ImageOffset.y = 0; // Coord Offset, from IO command
m_ImageRotation = 0; // Allowed 0, 90, 180, 270 (in degree)
m_LocalRotation = 0.0; // Layer totation from RO command (in 0.1 degree)
m_Offset.x = 0;
m_Offset.y = 0; // Coord Offset, from OF command
m_Scale.x = m_Scale.y = 1.0; // scale (A and B) this layer
m_MirrorA = false; // true: miror / axe A (default = X)
m_MirrorB = false; // true: miror / axe B (default = Y)
m_SwapAxis = false; // false if A = X, B = Y; true if A =Y, B = Y
m_Has_DCode = false; // true = DCodes in file
// false = no DCode->
// search for separate DCode file
m_FmtScale.x = m_FmtScale.y = 4; // Initialize default format to 3.4 => 4
m_FmtLen.x = m_FmtLen.y = 3 + 4; // Initialize default format len = 3+4
m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Linear, 90 arc, Circ.
m_360Arc_enbl = false; // 360 deg circular
// interpolation disable
m_Current_Tool = 0; // Current Dcode selected
m_CommandState = 0; // State of the current command
m_CurrentPos.x = m_CurrentPos.y = 0; // current specified coord
m_PreviousPos.x = m_PreviousPos.y = 0; // last specified coord
m_IJPos.x = m_IJPos.y = 0; // current centre coord for
// plot arcs & circles
m_Current_File = NULL; // Gerber file to read
m_FilesPtr = 0;
m_PolygonFillMode = false;
m_PolygonFillModeState = 0;
m_Selected_Tool = FIRST_DCODE;
m_Last_Pen_Command = 0;
m_Exposure = false;
for( unsigned ii = 0; ii < DIM( m_FilesList ); ii++ )
m_FilesList[ii] = NULL;
}
/* Function HasNegativeItems
* return true if at least one item must be drawn in background color
* used to optimize screen refresh
*/
bool GERBER_IMAGE::HasNegativeItems()
{
if( m_hasNegativeItems < 0 ) // negative items are not yet searched: find them if any
{
if( m_ImageNegative ) // A negative layer is expected having always negative objects.
m_hasNegativeItems = 1;
else
{
m_hasNegativeItems = 0;
for( GERBER_DRAW_ITEM* item = GetItemsList(); item; item = item->Next() )
{
if( item->GetLayer() != m_GraphicLayer )
continue;
if( item->HasNegativeItems() )
{
m_hasNegativeItems = 1;
break;
}
}
}
}
return m_hasNegativeItems == 1;
}
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
int GERBER_IMAGE::UsedDcodeNumber()
{
int count = 0;
for( unsigned ii = 0; ii < DIM( m_Aperture_List ); ii++ )
{
if( m_Aperture_List[ii] )
if( m_Aperture_List[ii]->m_InUse || m_Aperture_List[ii]->m_Defined )
++count;
}
return count;
}
void GERBER_IMAGE::InitToolTable()
{
for( int count = 0; count < TOOLS_MAX_COUNT; count++ )
{
if( m_Aperture_List[count] == NULL )
continue;
m_Aperture_List[count]->m_Num_Dcode = count + FIRST_DCODE;
m_Aperture_List[count]->Clear_D_CODE_Data();
}
m_aperture_macros.clear();
}
/**
* Function ReportMessage
* Add a message (a string) in message list
* for instance when reading a Gerber file
* @param aMessage = the straing to add in list
*/
void GERBER_IMAGE::ReportMessage( const wxString aMessage )
{
m_Parent->ReportMessage( aMessage );
}
/**
* Function ClearMessageList
* Clear the message list
* Call it before reading a Gerber file
*/
void GERBER_IMAGE::ClearMessageList()
{
m_Parent->ClearMessageList();
}
/**
* Function StepAndRepeatItem
* Gerber format has a command Step an Repeat
* This function must be called when reading a gerber file and
* after creating a new gerber item that must be repeated
* (i.e when m_XRepeatCount or m_YRepeatCount are > 1)
* @param aItem = the item to repeat
*/
void GERBER_IMAGE::StepAndRepeatItem( const GERBER_DRAW_ITEM& aItem )
{
if( GetLayerParams().m_XRepeatCount < 2 &&
GetLayerParams().m_YRepeatCount < 2 )
return; // Nothing to repeat
// Duplicate item:
wxString msg;
for( int ii = 0; ii < GetLayerParams().m_XRepeatCount; ii++ )
{
for( int jj = 0; jj < GetLayerParams().m_YRepeatCount; jj++ )
{
// the first gerber item already exists (this is the template)
// create duplicate only if ii or jj > 0
if( jj == 0 && ii == 0 )
continue;
GERBER_DRAW_ITEM* dupItem = new GERBER_DRAW_ITEM( aItem );
wxPoint move_vector;
move_vector.x = scaletoIU( ii * GetLayerParams().m_StepForRepeat.x,
GetLayerParams().m_StepForRepeatMetric );
move_vector.y = scaletoIU( jj * GetLayerParams().m_StepForRepeat.y,
GetLayerParams().m_StepForRepeatMetric );
dupItem->MoveXY( move_vector );
m_Parent->GetGerberLayout()->m_Drawings.Append( dupItem );
}
}
}
/**
* Function DisplayImageInfo
* has knowledge about the frame and how and where to put status information
* about this object into the frame's message panel.
* Display info about Image Parameters.
* These parameters are valid for the entire file, and must set only once
* (If more than once, only the last value is used)
*/
void GERBER_IMAGE::DisplayImageInfo( void )
{
wxString msg;
m_Parent->ClearMsgPanel();
// Display Image name (Image specific)
m_Parent->AppendMsgPanel( _( "Image name" ), m_ImageName, CYAN );
// Display graphic layer number used to draw this Image
// (not a Gerber parameter but is also image specific)
msg.Printf( wxT( "%d" ), m_GraphicLayer + 1 );
m_Parent->AppendMsgPanel( _( "Graphic layer" ), msg, BROWN );
// Display Image rotation (Image specific)
msg.Printf( wxT( "%d" ), m_ImageRotation );
m_Parent->AppendMsgPanel( _( "Img Rot." ), msg, CYAN );
// Display Image polarity (Image specific)
msg = m_ImageNegative ? _("Negative") : _("Normal");
m_Parent->AppendMsgPanel( _( "Polarity" ), msg, BROWN );
// Display Image justification and offset for justification (Image specific)
msg = m_ImageJustifyXCenter ? _("Center") : _("Normal");
m_Parent->AppendMsgPanel( _( "X Justify" ), msg, DARKRED );
msg = m_ImageJustifyYCenter ? _("Center") : _("Normal");
m_Parent->AppendMsgPanel( _( "Y Justify" ), msg, DARKRED );
if( g_UserUnit == INCHES )
msg.Printf( wxT( "X=%f Y=%f" ), (double) m_ImageJustifyOffset.x/10000,
(double) m_ImageJustifyOffset.y/10000 );
else
msg.Printf( wxT( "X=%f Y=%f" ), (double) m_ImageJustifyOffset.x*2.54/1000,
(double) m_ImageJustifyOffset.y*2.54/1000 );
m_Parent->AppendMsgPanel( _( "Image Justify Offset" ), msg, DARKRED );
}
// GERBER_IMAGE_LIST is a helper class to handle a list of GERBER_IMAGE files
GERBER_IMAGE_LIST::GERBER_IMAGE_LIST()
{
m_GERBER_List.reserve( GERBER_DRAWLAYERS_COUNT );
for( unsigned layer = 0; layer < GERBER_DRAWLAYERS_COUNT; ++layer )
m_GERBER_List.push_back( NULL );
}
GERBER_IMAGE_LIST::~GERBER_IMAGE_LIST()
{
ClearList();
for( unsigned layer = 0; layer < m_GERBER_List.size(); ++layer )
{
delete m_GERBER_List[layer];
m_GERBER_List[layer] = NULL;
}
}
GERBER_IMAGE* GERBER_IMAGE_LIST::GetGbrImage( int aIdx )
{
if( (unsigned)aIdx < m_GERBER_List.size() )
return m_GERBER_List[aIdx];
return NULL;
}
/**
* creates a new, empty GERBER_IMAGE* at index aIdx
* or at the first free location if aIdx < 0
* @param aIdx = the location to use ( 0 ... GERBER_DRAWLAYERS_COUNT-1 )
* @return true if the index used, or -1 if no room to add image
*/
int GERBER_IMAGE_LIST::AddGbrImage( GERBER_IMAGE* aGbrImage, int aIdx )
{
int idx = aIdx;
if( idx < 0 )
{
for( idx = 0; idx < (int)m_GERBER_List.size(); idx++ )
{
if( !IsUsed( idx ) )
break;
}
}
if( idx >= (int)m_GERBER_List.size() )
return -1; // No room
m_GERBER_List[idx] = aGbrImage;
return idx;
}
// remove all loaded data in list, but do not delete empty images
// (can be reused)
void GERBER_IMAGE_LIST::ClearList()
{
for( unsigned layer = 0; layer < m_GERBER_List.size(); ++layer )
ClearImage( layer );
}
// remove the loaded data of image aIdx, but do not delete it
void GERBER_IMAGE_LIST::ClearImage( int aIdx )
{
if( aIdx >= 0 && aIdx < (int)m_GERBER_List.size() && m_GERBER_List[aIdx] )
{
m_GERBER_List[aIdx]->InitToolTable();
m_GERBER_List[aIdx]->ResetDefaultValues();
m_GERBER_List[aIdx]->m_InUse = false;
}
}
// Build a name for image aIdx which can be used in layers manager
const wxString GERBER_IMAGE_LIST::GetDisplayName( int aIdx )
{
wxString name;
GERBER_IMAGE* gerber = NULL;
if( aIdx >= 0 && aIdx < (int)m_GERBER_List.size() )
gerber = m_GERBER_List[aIdx];
if( gerber && IsUsed(aIdx ) )
{
if( gerber->m_FileFunction )
{
if( gerber->m_FileFunction->IsCopper() )
{
name.Printf( _( "Layer %d (%s, %s, %s)" ), aIdx + 1,
GetChars( gerber->m_FileFunction->GetFileType() ),
GetChars( gerber->m_FileFunction->GetBrdLayerId() ),
GetChars( gerber->m_FileFunction->GetBrdLayerSide() ) );
}
else
{
name.Printf( _( "Layer %d (%s, %s)" ), aIdx + 1,
GetChars( gerber->m_FileFunction->GetFileType() ),
GetChars( gerber->m_FileFunction->GetBrdLayerId() ) );
}
}
else
name.Printf( _( "Layer %d *" ), aIdx + 1 );
}
else
name.Printf( _( "Layer %d" ), aIdx + 1 );
return name;
}
// return true if image is used (loaded and not cleared)
bool GERBER_IMAGE_LIST::IsUsed( int aIdx )
{
if( aIdx >= 0 && aIdx < (int)m_GERBER_List.size() )
return m_GERBER_List[aIdx] != NULL && m_GERBER_List[aIdx]->m_InUse;
return false;
}
// Helper function, for std::sort.
// Sort loaded images by Z order priority, if they have the X2 FileFormat info
// returns true if the first argument (ref) is ordered before the second (test).
static bool sortZorder( const GERBER_IMAGE* const& ref, const GERBER_IMAGE* const& test )
{
if( !ref && !test )
return false; // do not change order: no criteria to sort items
if( !ref || !ref->m_InUse )
return false; // Not used: ref ordered after
if( !test || !test->m_InUse )
return true; // Not used: ref ordered before
if( !ref->m_FileFunction && !test->m_FileFunction )
return false; // do not change order: no criteria to sort items
if( !ref->m_FileFunction )
return false;
if( !test->m_FileFunction )
return true;
if( ref->m_FileFunction->GetZOrder() != test->m_FileFunction->GetZOrder() )
return ref->m_FileFunction->GetZOrder() > test->m_FileFunction->GetZOrder();
return ref->m_FileFunction->GetZSubOrder() > test->m_FileFunction->GetZSubOrder();
}
void GERBER_IMAGE_LIST::SortImagesByZOrder( GERBER_DRAW_ITEM* aDrawList )
{
std::sort( m_GERBER_List.begin(), m_GERBER_List.end(), sortZorder );
// The image order has changed.
// Graphic layer numbering must be updated to match the widgets layer order
// Store the old/new graphic layer info:
std::map <int, int> tab_lyr;
for( unsigned layer = 0; layer < m_GERBER_List.size(); ++layer )
{
if( m_GERBER_List[layer] )
{
tab_lyr[m_GERBER_List[layer]->m_GraphicLayer] = layer;
m_GERBER_List[layer]->m_GraphicLayer = layer ;
}
}
// update the graphic layer in items to draw
for( GERBER_DRAW_ITEM* item = aDrawList; item; item = item->Next() )
{
int layer = item->GetLayer();
item->SetLayer( tab_lyr[layer] );
}
}
// The global image list:
GERBER_IMAGE_LIST g_GERBER_List;