You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
15 KiB

  1. /**
  2. * @file gerber_file_image.cpp
  3. * a GERBER class handle for a given layer info about used D_CODES and how the layer is drawn
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 1992-2019 Jean-Pierre Charras jp.charras at wanadoo.fr
  9. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. #include <convert_to_biu.h>
  29. #include <gerbview.h>
  30. #include <gerbview_frame.h>
  31. #include <gerber_file_image.h>
  32. #include <macros.h>
  33. #include <X2_gerber_attributes.h>
  34. #include <algorithm>
  35. #include <map>
  36. #include <core/arraydim.h>
  37. /**
  38. * Function scaletoIU
  39. * converts a distance given in floating point to our internal units
  40. */
  41. extern int scaletoIU( double aCoord, bool isMetric ); // defined it rs274d_read_XY_and_IJ_coordinates.cpp
  42. /* Format Gerber: NOTES:
  43. * Tools and D_CODES
  44. * tool number (identification of shapes)
  45. * 1 to 999
  46. *
  47. * D_CODES:
  48. * D01 ... D9 = action codes:
  49. * D01 = activating light (lower pen) when di placement
  50. * D02 = light extinction (lift pen) when di placement
  51. * D03 Flash
  52. * D09 = VAPE Flash
  53. * D10 ... = Identification Tool (Opening)
  54. *
  55. * For tools:
  56. * DCode min = D10
  57. * DCode max = 999
  58. */
  59. GERBER_LAYER::GERBER_LAYER()
  60. {
  61. ResetDefaultValues();
  62. }
  63. GERBER_LAYER::~GERBER_LAYER()
  64. {
  65. }
  66. void GERBER_LAYER::ResetDefaultValues()
  67. {
  68. m_LayerName = wxT( "no name" ); // Layer name from the LN command
  69. m_LayerNegative = false; // true = Negative Layer
  70. m_StepForRepeat.x = m_StepForRepeat.y = 0; // X and Y offsets for Step and Repeat command
  71. m_XRepeatCount = 1; // The repeat count on X axis
  72. m_YRepeatCount = 1; // The repeat count on Y axis
  73. m_StepForRepeatMetric = false; // false = Inches, true = metric
  74. }
  75. GERBER_FILE_IMAGE::GERBER_FILE_IMAGE( int aLayer ) :
  76. EDA_ITEM( nullptr, GERBER_IMAGE_T )
  77. {
  78. m_GraphicLayer = aLayer; // Graphic layer Number
  79. m_IsVisible = true; // must be drawn
  80. m_PositiveDrawColor = WHITE; // The color used to draw positive items for this image
  81. m_Selected_Tool = 0;
  82. m_FileFunction = nullptr; // file function parameters
  83. ResetDefaultValues();
  84. for( unsigned ii = 0; ii < arrayDim( m_Aperture_List ); ii++ )
  85. m_Aperture_List[ii] = nullptr;
  86. }
  87. GERBER_FILE_IMAGE::~GERBER_FILE_IMAGE()
  88. {
  89. for( auto item : GetItems() )
  90. delete item;
  91. m_drawings.clear();
  92. for( unsigned ii = 0; ii < arrayDim( m_Aperture_List ); ii++ )
  93. {
  94. delete m_Aperture_List[ii];
  95. }
  96. delete m_FileFunction;
  97. }
  98. D_CODE* GERBER_FILE_IMAGE::GetDCODEOrCreate( int aDCODE, bool aCreateIfNoExist )
  99. {
  100. unsigned ndx = aDCODE - FIRST_DCODE;
  101. if( ndx < (unsigned) arrayDim( m_Aperture_List ) )
  102. {
  103. // lazily create the D_CODE if it does not exist.
  104. if( aCreateIfNoExist )
  105. {
  106. if( m_Aperture_List[ndx] == nullptr )
  107. m_Aperture_List[ndx] = new D_CODE( ndx + FIRST_DCODE );
  108. }
  109. return m_Aperture_List[ndx];
  110. }
  111. return nullptr;
  112. }
  113. D_CODE* GERBER_FILE_IMAGE::GetDCODE( int aDCODE ) const
  114. {
  115. unsigned ndx = aDCODE - FIRST_DCODE;
  116. if( ndx < (unsigned) arrayDim( m_Aperture_List ) )
  117. {
  118. return m_Aperture_List[ndx];
  119. }
  120. return nullptr;
  121. }
  122. APERTURE_MACRO* GERBER_FILE_IMAGE::FindApertureMacro( const APERTURE_MACRO& aLookup )
  123. {
  124. APERTURE_MACRO_SET::iterator iter = m_aperture_macros.find( aLookup );
  125. if( iter != m_aperture_macros.end() )
  126. {
  127. APERTURE_MACRO* pam = (APERTURE_MACRO*) &(*iter);
  128. return pam;
  129. }
  130. return nullptr; // not found
  131. }
  132. void GERBER_FILE_IMAGE::ResetDefaultValues()
  133. {
  134. m_InUse = false;
  135. m_GBRLayerParams.ResetDefaultValues();
  136. m_FileName.Empty();
  137. m_ImageName = wxT( "no name" ); // Image name from the IN command
  138. m_ImageNegative = false; // true = Negative image
  139. m_IsX2_file = false; // true only if a %TF, %TA or %TD command
  140. delete m_FileFunction; // file function parameters
  141. m_FileFunction = nullptr;
  142. m_MD5_value.Empty(); // MD5 value found in a %TF.MD5 command
  143. m_PartString.Empty(); // string found in a %TF.Part command
  144. m_hasNegativeItems = -1; // set to uninitialized
  145. m_ImageJustifyOffset = wxPoint(0,0); // Image justify Offset
  146. m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
  147. m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
  148. m_GerbMetric = false; // false = Inches (default), true = metric
  149. m_Relative = false; // false = absolute Coord,
  150. // true = relative Coord
  151. m_NoTrailingZeros = false; // true: trailing zeros deleted
  152. m_ImageOffset.x = m_ImageOffset.y = 0; // Coord Offset, from IO command
  153. m_ImageRotation = 0; // Allowed 0, 90, 180, 270 (in degree)
  154. m_LocalRotation = 0.0; // Layer rotation from RO command (in 0.1 degree)
  155. m_Offset.x = 0;
  156. m_Offset.y = 0; // Coord Offset, from OF command
  157. m_Scale.x = m_Scale.y = 1.0; // scale (A and B) this layer
  158. m_MirrorA = false; // true: mirror / axe A (default = X)
  159. m_MirrorB = false; // true: mirror / axe B (default = Y)
  160. m_SwapAxis = false; // false if A = X, B = Y; true if A =Y, B = Y
  161. m_Has_DCode = false; // true = DCodes in file
  162. // false = no DCode-> perhaps deprecated RS274D file
  163. m_Has_MissingDCode = false; // true = some D_Codes are used, but not defined
  164. // perhaps deprecated RS274D file
  165. m_FmtScale.x = m_FmtScale.y = 4; // Initialize default format to 3.4 => 4
  166. m_FmtLen.x = m_FmtLen.y = 3 + 4; // Initialize default format len = 3+4
  167. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Linear, 90 arc, Circ.
  168. m_360Arc_enbl = true; // 360 deg circular mode (G75) selected as default
  169. // interpolation disable
  170. m_AsArcG74G75Cmd = false; // false until a G74 or G75 command is found
  171. m_Current_Tool = 0; // Current Dcode selected
  172. m_CommandState = 0; // State of the current command
  173. m_CurrentPos.x = m_CurrentPos.y = 0; // current specified coord
  174. m_PreviousPos.x = m_PreviousPos.y = 0; // last specified coord
  175. m_IJPos.x = m_IJPos.y = 0; // current centre coord for
  176. // plot arcs & circles
  177. m_LastCoordIsIJPos = false; // True only after a IJ coordinate is read
  178. m_ArcRadius = 0; // radius of arcs in circular interpol (given by A## command).
  179. // in command like X##Y##A##
  180. m_LastArcDataType = ARC_INFO_TYPE_NONE; // Extra coordinate info type for arcs
  181. // (radius or IJ center coord)
  182. m_LineNum = 0; // line number in file being read
  183. m_Current_File = nullptr; // Gerber file to read
  184. m_PolygonFillMode = false;
  185. m_PolygonFillModeState = 0;
  186. m_Selected_Tool = 0;
  187. m_Last_Pen_Command = 0;
  188. m_Exposure = false;
  189. }
  190. /* Function HasNegativeItems
  191. * return true if at least one item must be drawn in background color
  192. * used to optimize screen refresh
  193. */
  194. bool GERBER_FILE_IMAGE::HasNegativeItems()
  195. {
  196. if( m_hasNegativeItems < 0 ) // negative items are not yet searched: find them if any
  197. {
  198. if( m_ImageNegative ) // A negative layer is expected having always negative objects.
  199. m_hasNegativeItems = 1;
  200. else
  201. {
  202. m_hasNegativeItems = 0;
  203. for( GERBER_DRAW_ITEM* item : GetItems() )
  204. {
  205. if( item->GetLayer() != m_GraphicLayer )
  206. continue;
  207. if( item->HasNegativeItems() )
  208. {
  209. m_hasNegativeItems = 1;
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. return m_hasNegativeItems == 1;
  216. }
  217. int GERBER_FILE_IMAGE::GetDcodesCount()
  218. {
  219. int count = 0;
  220. for( unsigned ii = 0; ii < arrayDim( m_Aperture_List ); ii++ )
  221. {
  222. if( m_Aperture_List[ii] )
  223. if( m_Aperture_List[ii]->m_InUse || m_Aperture_List[ii]->m_Defined )
  224. ++count;
  225. }
  226. return count;
  227. }
  228. void GERBER_FILE_IMAGE::InitToolTable()
  229. {
  230. for( int count = 0; count < TOOLS_MAX_COUNT; count++ )
  231. {
  232. if( m_Aperture_List[count] == nullptr )
  233. continue;
  234. m_Aperture_List[count]->m_Num_Dcode = count + FIRST_DCODE;
  235. m_Aperture_List[count]->Clear_D_CODE_Data();
  236. }
  237. m_aperture_macros.clear();
  238. }
  239. /**
  240. * Function StepAndRepeatItem
  241. * Gerber format has a command Step an Repeat
  242. * This function must be called when reading a gerber file and
  243. * after creating a new gerber item that must be repeated
  244. * (i.e when m_XRepeatCount or m_YRepeatCount are > 1)
  245. * @param aItem = the item to repeat
  246. */
  247. void GERBER_FILE_IMAGE::StepAndRepeatItem( const GERBER_DRAW_ITEM& aItem )
  248. {
  249. if( GetLayerParams().m_XRepeatCount < 2 &&
  250. GetLayerParams().m_YRepeatCount < 2 )
  251. return; // Nothing to repeat
  252. // Duplicate item:
  253. for( int ii = 0; ii < GetLayerParams().m_XRepeatCount; ii++ )
  254. {
  255. for( int jj = 0; jj < GetLayerParams().m_YRepeatCount; jj++ )
  256. {
  257. // the first gerber item already exists (this is the template)
  258. // create duplicate only if ii or jj > 0
  259. if( jj == 0 && ii == 0 )
  260. continue;
  261. GERBER_DRAW_ITEM* dupItem = new GERBER_DRAW_ITEM( aItem );
  262. wxPoint move_vector;
  263. move_vector.x = scaletoIU( ii * GetLayerParams().m_StepForRepeat.x,
  264. GetLayerParams().m_StepForRepeatMetric );
  265. move_vector.y = scaletoIU( jj * GetLayerParams().m_StepForRepeat.y,
  266. GetLayerParams().m_StepForRepeatMetric );
  267. dupItem->MoveXY( move_vector );
  268. AddItemToList( dupItem );
  269. }
  270. }
  271. }
  272. /**
  273. * Function DisplayImageInfo
  274. * has knowledge about the frame and how and where to put status information
  275. * about this object into the frame's message panel.
  276. * Display info about Image Parameters.
  277. * These parameters are valid for the entire file, and must set only once
  278. * (If more than once, only the last value is used)
  279. */
  280. void GERBER_FILE_IMAGE::DisplayImageInfo( GERBVIEW_FRAME* aMainFrame )
  281. {
  282. wxString msg;
  283. aMainFrame->ClearMsgPanel();
  284. // Display Image name (Image specific)
  285. aMainFrame->AppendMsgPanel( _( "Image name" ), m_ImageName );
  286. // Display graphic layer number used to draw this Image
  287. // (not a Gerber parameter but is also image specific)
  288. msg.Printf( wxT( "%d" ), m_GraphicLayer + 1 );
  289. aMainFrame->AppendMsgPanel( _( "Graphic layer" ), msg );
  290. // Display Image rotation (Image specific)
  291. msg.Printf( wxT( "%d" ), m_ImageRotation );
  292. aMainFrame->AppendMsgPanel( _( "Img Rot." ), msg );
  293. // Display Image polarity (Image specific)
  294. msg = m_ImageNegative ? _("Negative") : _("Normal");
  295. aMainFrame->AppendMsgPanel( _( "Polarity" ), msg );
  296. // Display Image justification and offset for justification (Image specific)
  297. msg = m_ImageJustifyXCenter ? _("Center") : _("Normal");
  298. aMainFrame->AppendMsgPanel( _( "X Justify" ), msg );
  299. msg = m_ImageJustifyYCenter ? _("Center") : _("Normal");
  300. aMainFrame->AppendMsgPanel( _( "Y Justify" ), msg );
  301. switch( aMainFrame->GetUserUnits() )
  302. {
  303. case EDA_UNITS::MILS:
  304. msg.Printf( wxT( "X=%f Y=%f" ), Iu2Mils( m_ImageJustifyOffset.x ),
  305. Iu2Mils( m_ImageJustifyOffset.y ) );
  306. break;
  307. case EDA_UNITS::INCHES:
  308. msg.Printf( wxT( "X=%f Y=%f" ), Iu2Mils( m_ImageJustifyOffset.x ) / 1000.0,
  309. Iu2Mils( m_ImageJustifyOffset.y ) / 1000.0 );
  310. break;
  311. case EDA_UNITS::MILLIMETRES:
  312. msg.Printf( wxT( "X=%f Y=%f" ), Iu2Millimeter( m_ImageJustifyOffset.x ),
  313. Iu2Millimeter( m_ImageJustifyOffset.y ) );
  314. break;
  315. default:
  316. wxASSERT_MSG( false, "Invalid unit" );
  317. }
  318. aMainFrame->AppendMsgPanel( _( "Image Justify Offset" ), msg );
  319. }
  320. void GERBER_FILE_IMAGE::RemoveAttribute( X2_ATTRIBUTE& aAttribute )
  321. {
  322. /* Called when a %TD command is found
  323. * Remove the attribute specified by the %TD command.
  324. * is no attribute, all current attributes specified by the %TO and the %TA
  325. * commands are cleared.
  326. * if a attribute name is specified (for instance %TD.CN*%) is specified,
  327. * only this attribute is cleared
  328. */
  329. wxString cmd = aAttribute.GetPrm( 0 );
  330. m_NetAttributeDict.ClearAttribute( &cmd );
  331. if( cmd.IsEmpty() || cmd == ".AperFunction" )
  332. m_AperFunction.Clear();
  333. }
  334. SEARCH_RESULT GERBER_FILE_IMAGE::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
  335. {
  336. KICAD_T stype;
  337. SEARCH_RESULT result = SEARCH_RESULT::CONTINUE;
  338. const KICAD_T* p = scanTypes;
  339. bool done = false;
  340. while( !done )
  341. {
  342. stype = *p;
  343. switch( stype )
  344. {
  345. case GERBER_IMAGE_T:
  346. case GERBER_LAYOUT_T:
  347. ++p;
  348. break;
  349. case GERBER_DRAW_ITEM_T:
  350. result = IterateForward( GetItems(), inspector, testData, p );
  351. ++p;
  352. break;
  353. case EOT:
  354. default: // catch EOT or ANY OTHER type here and return.
  355. done = true;
  356. break;
  357. }
  358. if( result == SEARCH_RESULT::QUIT )
  359. break;
  360. }
  361. return result;
  362. }