1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
|
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 1992-2019 Jean-Pierre Charras jp.charras at wanadoo.fr
* Copyright The 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
*/
#include <gerbview.h>
#include <gerbview_frame.h>
#include <gerber_file_image.h>
#include <macros.h>
#include <X2_gerber_attributes.h>
#include <algorithm>
#include <map>
#include <core/arraydim.h>
#include <core/profile.h>
/**
* 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_coordinates.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 ... = Identification 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_FILE_IMAGE::GERBER_FILE_IMAGE( int aLayer ) :
EDA_ITEM( nullptr, GERBER_IMAGE_T )
{
m_GraphicLayer = aLayer; // Graphic layer Number
m_PositiveDrawColor = WHITE; // The color used to draw positive items for this image
m_Selected_Tool = 0;
m_FileFunction = nullptr; // file function parameters
ResetDefaultValues();
for( unsigned ii = 0; ii < arrayDim( m_Aperture_List ); ii++ )
m_Aperture_List[ii] = nullptr;
}
GERBER_FILE_IMAGE::~GERBER_FILE_IMAGE()
{
// Reverse iteration to avoid O(N^2) memcpy in a vector erase downstream.
// It results in a O(N^2) std::find, which is somewhat faster.
for( auto it = GetItems().rbegin(); it < GetItems().rend(); it++ )
delete *it;
m_drawings.clear();
for( unsigned ii = 0; ii < arrayDim( m_Aperture_List ); ii++ )
delete m_Aperture_List[ii];
delete m_FileFunction;
}
void GERBER_FILE_IMAGE::AddMessageToList( const wxString& aMessage )
{
/* Add a message to the message list, but only if there are less than max_messages
* to avoid very long list (can happens if trying to read a non gerber file)
*/
const int max_messages = 50; // Arbitrary but reasonable value.
if( m_messagesList.size() < max_messages )
m_messagesList.Add( aMessage );
else if( m_messagesList.size() == max_messages )
m_messagesList.Add( _( "Too many messages, some are skipped" ) );
}
void GERBER_FILE_IMAGE::SetDrawOffetAndRotation( VECTOR2D aOffsetMM, EDA_ANGLE aRotation )
{
m_DisplayOffset.x = KiROUND( aOffsetMM.x * gerbIUScale.IU_PER_MM );
m_DisplayOffset.y = KiROUND( aOffsetMM.y * gerbIUScale.IU_PER_MM );
m_DisplayRotation = aRotation;
// Clear m_AbsolutePolygon member of Gerber items, because draw coordinates
// are now outdated
for( GERBER_DRAW_ITEM* item : GetItems() )
item->m_AbsolutePolygon.RemoveAllContours();
}
D_CODE* GERBER_FILE_IMAGE::GetDCODEOrCreate( int aDCODE, bool aCreateIfNoExist )
{
unsigned ndx = aDCODE - FIRST_DCODE;
if( ndx < (unsigned) arrayDim( m_Aperture_List ) )
{
// lazily create the D_CODE if it does not exist.
if( aCreateIfNoExist )
{
if( m_Aperture_List[ndx] == nullptr )
m_Aperture_List[ndx] = new D_CODE( ndx + FIRST_DCODE );
}
return m_Aperture_List[ndx];
}
return nullptr;
}
D_CODE* GERBER_FILE_IMAGE::GetDCODE( int aDCODE ) const
{
unsigned ndx = aDCODE - FIRST_DCODE;
if( ndx < (unsigned) arrayDim( m_Aperture_List ) )
return m_Aperture_List[ndx];
return nullptr;
}
APERTURE_MACRO* GERBER_FILE_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 nullptr; // not found
}
void GERBER_FILE_IMAGE::ResetDefaultValues()
{
m_InUse = false;
m_GBRLayerParams.ResetDefaultValues();
m_FileName.Empty();
m_ImageName = wxEmptyString; // Image name from the IN command (deprecated)
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 = nullptr;
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 = VECTOR2I( 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_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 rotation 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: mirror / axe A (default = X)
m_MirrorB = false; // true: mirror / 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-> perhaps deprecated RS274D file
m_Has_MissingDCode = false; // true = some D_Codes are used, but not defined
// perhaps deprecated RS274D 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 = true; // 360 deg circular mode (G75) selected as default
// interpolation disable
m_AsArcG74G75Cmd = false; // false until a G74 or G75 command is found
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_LastCoordIsIJPos = false; // True only after a IJ coordinate is read
m_ArcRadius = 0; // radius of arcs in circular interpol (given by A## command).
// in command like X##Y##A##
m_LastArcDataType = ARC_INFO_TYPE_NONE; // Extra coordinate info type for arcs
// (radius or IJ center coord)
m_LineNum = 0; // line number in file being read
m_Current_File = nullptr; // Gerber file to read
m_PolygonFillMode = false;
m_PolygonFillModeState = 0;
m_Selected_Tool = 0;
m_Last_Pen_Command = 0;
m_Exposure = false;
m_DisplayOffset.x = m_DisplayOffset.y = 0;
m_DisplayRotation = ANGLE_0;
}
/* Function HasNegativeItems
* return true if at least one item must be drawn in background color
* used to optimize screen refresh
*/
bool GERBER_FILE_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 : GetItems() )
{
if( item->GetLayer() != m_GraphicLayer )
continue;
if( item->HasNegativeItems() )
{
m_hasNegativeItems = 1;
break;
}
}
}
}
return m_hasNegativeItems == 1;
}
int GERBER_FILE_IMAGE::GetDcodesCount()
{
int count = 0;
for( unsigned ii = 0; ii < arrayDim( 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;
}
/**
* 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_FILE_IMAGE::StepAndRepeatItem( const GERBER_DRAW_ITEM& aItem )
{
if( GetLayerParams().m_XRepeatCount < 2 && GetLayerParams().m_YRepeatCount < 2 )
return; // Nothing to repeat
// Duplicate item:
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 );
VECTOR2I 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 );
AddItemToList( 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)
* Some are deprecated
*/
void GERBER_FILE_IMAGE::DisplayImageInfo( GERBVIEW_FRAME* aMainFrame )
{
wxString msg;
aMainFrame->ClearMsgPanel();
// Display the Gerber variant (X1 / X2
aMainFrame->AppendMsgPanel( _( "Format" ), m_IsX2_file ? wxT( "X2" ) : wxT( "X1" ) );
// Display Image name (Image specific). IM command (Image Name) is deprecated
// So non empty image name is very rare, probably never found
if( !m_ImageName.IsEmpty() )
aMainFrame->AppendMsgPanel( _( "Image name" ), m_ImageName );
// 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 );
aMainFrame->AppendMsgPanel( _( "Graphic layer" ), msg );
// Display Image rotation (Image specific)
msg.Printf( wxT( "%d" ), m_ImageRotation );
aMainFrame->AppendMsgPanel( _( "Img Rot." ), msg );
// Display Image polarity (Image specific)
msg = m_ImageNegative ? _("Negative") : _("Normal");
aMainFrame->AppendMsgPanel( _( "Polarity" ), msg );
// Display Image justification and offset for justification (Image specific)
msg = m_ImageJustifyXCenter ? _("Center") : _("Normal");
aMainFrame->AppendMsgPanel( _( "X Justify" ), msg );
msg = m_ImageJustifyYCenter ? _("Center") : _("Normal");
aMainFrame->AppendMsgPanel( _( "Y Justify" ), msg );
msg.Printf( wxT( "X=%s Y=%s" ),
aMainFrame->MessageTextFromValue( m_ImageJustifyOffset.x ),
aMainFrame->MessageTextFromValue( m_ImageJustifyOffset.y ) );
aMainFrame->AppendMsgPanel( _( "Image Justify Offset" ), msg );
}
void GERBER_FILE_IMAGE::RemoveAttribute( X2_ATTRIBUTE& aAttribute )
{
/* Called when a %TD command is found
* Remove the attribute specified by the %TD command.
* is no attribute, all current attributes specified by the %TO and the %TA
* commands are cleared.
* if a attribute name is specified (for instance %TD.CN*%) is specified,
* only this attribute is cleared
*/
wxString cmd = aAttribute.GetPrm( 0 );
m_NetAttributeDict.ClearAttribute( &cmd );
if( cmd.IsEmpty() || cmd == wxT( ".AperFunction" ) )
m_AperFunction.Clear();
}
INSPECT_RESULT GERBER_FILE_IMAGE::Visit( INSPECTOR inspector, void* testData,
const std::vector<KICAD_T>& aScanTypes )
{
for( KICAD_T scanType : aScanTypes )
{
if( scanType == GERBER_DRAW_ITEM_T )
{
if( IterateForward( GetItems(), inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
return INSPECT_RESULT::QUIT;
}
}
return INSPECT_RESULT::CONTINUE;
}
|