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.

1158 lines
35 KiB

// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
// Dick Hollenbeck's KiROUND R&D // This provides better project control over rounding to int from double // than wxRound() did. This scheme provides better logging in Debug builds // and it provides for compile time calculation of constants. #include <stdio.h> #include <assert.h> #include <limits.h> //-----<KiROUND KIT>------------------------------------------------------------ /** * KiROUND * rounds a floating point number to an int using * "round halfway cases away from zero". * In Debug build an assert fires if will not fit into an int. */ #if defined( DEBUG ) // DEBUG: a macro to capture line and file, then calls this inline static inline int KiRound( double v, int line, const char* filename ) { v = v < 0 ? v - 0.5 : v + 0.5; if( v > INT_MAX + 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' > 0 ' for int\n", __FUNCTION__, filename, line, v ); } else if( v < INT_MIN - 0.5 ) { printf( "%s: in file %s on line %d, val: %.16g too ' < 0 ' for int\n", __FUNCTION__, filename, line, v ); } return int( v ); } #define KiROUND( v ) KiRound( v, __LINE__, __FILE__ ) #else // RELEASE: a macro so compile can pre-compute constants. #define KiROUND( v ) int( (v) < 0 ? (v) - 0.5 : (v) + 0.5 ) #endif //-----</KiROUND KIT>----------------------------------------------------------- // Only a macro is compile time calculated, an inline function causes a static constructor // in a situation like this. // Therefore the Release build is best done with a MACRO not an inline function. int Computed = KiROUND( 14.3 * 8 ); int main( int argc, char** argv ) { for( double d = double(INT_MAX)-1; d < double(INT_MAX)+8; d += 2.0 ) { int i = KiROUND( d ); printf( "t: %d %.16g\n", i, d ); } return 0; }
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2007-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
  5. * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file rs274x.cpp
  26. */
  27. #include <base_units.h>
  28. #include <math/util.h> // for KiROUND
  29. #include <gerbview.h>
  30. #include <gerber_file_image.h>
  31. #include <ignore.h>
  32. #include <macros.h>
  33. #include <X2_gerber_attributes.h>
  34. #include <gbr_metadata.h>
  35. extern int ReadInt( char*& text, bool aSkipSeparator = true );
  36. extern double ReadDouble( char*& text, bool aSkipSeparator = true );
  37. #define CODE( x, y ) ( ( (x) << 8 ) + (y) )
  38. // See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
  39. // in gerber files, when a coordinate is given (like X78Y600 or I0J80):
  40. // Y and Y are logical coordinates
  41. // A and B are plotter coordinates
  42. // Usually A = X, B = Y
  43. // But we can have A = Y, B = X and/or offset, mirror, scale;
  44. // Also:
  45. // Image is what you must plot (the entire data of the file).
  46. // Layer is just a set of data blocks with their parameters. An image can have more than one
  47. // layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
  48. // to show a file.
  49. enum RS274X_PARAMETERS {
  50. // Directive parameters: single usage recommended
  51. // Must be at the beginning of the file
  52. AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
  53. FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
  54. MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
  55. MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
  56. INCH = CODE( 'I', 'N' ),
  57. MILLIMETER = CODE( 'M', 'M' ),
  58. OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
  59. SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
  60. // Image parameters:
  61. // commands used only once at the beginning of the file, and are deprecated
  62. IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
  63. IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
  64. IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
  65. IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
  66. IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
  67. // Aperture parameters:
  68. // Usually for the whole file
  69. AP_DEFINITION = CODE( 'A', 'D' ),
  70. AP_MACRO = CODE( 'A', 'M' ),
  71. // X2 extension attribute commands
  72. // Mainly are found standard attributes and user attributes
  73. // standard attributes commands are:
  74. // TF (file attribute) TO (net attribute)
  75. // TA (aperture attribute) and TD (delete aperture attribute)
  76. FILE_ATTRIBUTE = CODE( 'T', 'F' ),
  77. // X2 extension Net attribute info
  78. // Net attribute options are:
  79. // TO (net attribute data): TO.CN or TO.P TO.N or TO.C
  80. NET_ATTRIBUTE = CODE( 'T', 'O' ),
  81. // X2 extension Aperture attribute TA
  82. APERTURE_ATTRIBUTE = CODE( 'T', 'A' ),
  83. // TD (delete aperture/object attribute):
  84. // Delete aperture attribute added by %TA or Oblect attribute added b %TO
  85. // TD (delete all) or %TD<attr name> to delete <attr name>.
  86. // eg: TD.P or TD.N or TD.C ...
  87. REMOVE_APERTURE_ATTRIBUTE = CODE( 'T', 'D' ),
  88. // Layer specific parameters
  89. // May be used singly or may be layer specific
  90. // These parameters are at the beginning of the file or layer
  91. // and reset some layer parameters (like interpolation)
  92. KNOCKOUT = CODE( 'K', 'O' ), // Default: off
  93. STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
  94. ROTATE = CODE( 'R', 'O' ), // Default: 0
  95. LOAD_POLARITY = CODE( 'L', 'P' ), //LPC or LPD. Default: Dark (LPD)
  96. LOAD_NAME = CODE( 'L', 'N' ), // Deprecated: equivalent to G04
  97. };
  98. int GERBER_FILE_IMAGE::ReadXCommandID( char*& text )
  99. {
  100. /* reads two bytes of data and assembles them into an int with the first
  101. * byte in the sequence put into the most significant part of a 16 bit value
  102. */
  103. int result;
  104. int currbyte;
  105. if( text && *text )
  106. {
  107. currbyte = *text++;
  108. result = ( currbyte & 0xFF ) << 8;
  109. }
  110. else
  111. return -1;
  112. if( text && *text )
  113. {
  114. currbyte = *text++;
  115. result += currbyte & 0xFF;
  116. }
  117. else
  118. return -1;
  119. return result;
  120. }
  121. bool GERBER_FILE_IMAGE::ReadRS274XCommand( char *aBuff, unsigned int aBuffSize, char*& aText )
  122. {
  123. bool ok = true;
  124. int code_command;
  125. aText++;
  126. for( ; ; )
  127. {
  128. while( *aText )
  129. {
  130. switch( *aText )
  131. {
  132. case '%': // end of command
  133. aText++;
  134. m_CommandState = CMD_IDLE;
  135. goto exit; // success completion
  136. case ' ':
  137. case '\r':
  138. case '\n':
  139. aText++;
  140. break;
  141. case '*':
  142. aText++;
  143. break;
  144. default:
  145. code_command = ReadXCommandID( aText );
  146. ok = ExecuteRS274XCommand( code_command, aBuff, aBuffSize, aText );
  147. if( !ok )
  148. goto exit;
  149. break;
  150. }
  151. }
  152. // end of current line, read another one.
  153. if( fgets( aBuff, aBuffSize, m_Current_File ) == nullptr )
  154. {
  155. // end of file
  156. ok = false;
  157. break;
  158. }
  159. m_LineNum++;
  160. aText = aBuff;
  161. }
  162. exit:
  163. return ok;
  164. }
  165. bool GERBER_FILE_IMAGE::ExecuteRS274XCommand( int aCommand, char* aBuff,
  166. unsigned int aBuffSize, char*& aText )
  167. {
  168. int code;
  169. int seq_len; // not used, just provided
  170. int seq_char;
  171. bool ok = true;
  172. wxString msg;
  173. double fcoord;
  174. bool x_fmt_known = false;
  175. bool y_fmt_known = false;
  176. // conv_scale = scaling factor from inch to Internal Unit
  177. double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
  178. if( m_GerbMetric )
  179. conv_scale /= 25.4;
  180. switch( aCommand )
  181. {
  182. case FORMAT_STATEMENT:
  183. seq_len = 2;
  184. while( *aText != '*' )
  185. {
  186. switch( *aText )
  187. {
  188. case ' ':
  189. aText++;
  190. break;
  191. case 'D': // Non-standard option for all zeros (leading + tailing)
  192. msg.Printf( _( "RS274X: Invalid GERBER format command '%c' at line %d: \"%s\"" ),
  193. 'D', m_LineNum, aBuff );
  194. AddMessageToList( msg );
  195. msg.Printf( _("GERBER file \"%s\" may not display as intended." ),
  196. m_FileName.ToAscii() );
  197. AddMessageToList( msg );
  198. KI_FALLTHROUGH;
  199. case 'L': // No Leading 0
  200. m_NoTrailingZeros = false;
  201. aText++;
  202. break;
  203. case 'T': // No trailing 0
  204. m_NoTrailingZeros = true;
  205. aText++;
  206. break;
  207. case 'A': // Absolute coord
  208. m_Relative = false;
  209. aText++;
  210. break;
  211. case 'I': // Relative coord
  212. m_Relative = true;
  213. aText++;
  214. break;
  215. case 'G':
  216. case 'N': // Sequence code (followed by one digit: the sequence len)
  217. // (sometimes found before the X,Y sequence)
  218. // Obscure option
  219. aText++;
  220. seq_char = *aText++;
  221. if( (seq_char >= '0') && (seq_char <= '9') )
  222. seq_len = seq_char - '0';
  223. break;
  224. case 'M': // Sequence code (followed by one digit: the sequence len)
  225. // (sometimes found after the X,Y sequence)
  226. // Obscure option
  227. aText++;
  228. code = *aText;
  229. if( ( code >= '0' ) && ( code <= '9' ) )
  230. aText++; // skip the digit
  231. break;
  232. case 'X':
  233. case 'Y':
  234. {
  235. code = *(aText++);
  236. char ctmp = *(aText++) - '0';
  237. if( code == 'X' )
  238. {
  239. x_fmt_known = true;
  240. // number of digits after the decimal point (0 to 7 allowed)
  241. m_FmtScale.x = *aText - '0';
  242. m_FmtLen.x = ctmp + m_FmtScale.x;
  243. // m_FmtScale is 0 to 7
  244. // (Old Gerber specification was 0 to 6)
  245. if( m_FmtScale.x < 0 )
  246. m_FmtScale.x = 0;
  247. if( m_FmtScale.x > 7 )
  248. m_FmtScale.x = 7;
  249. }
  250. else
  251. {
  252. y_fmt_known = true;
  253. m_FmtScale.y = *aText - '0';
  254. m_FmtLen.y = ctmp + m_FmtScale.y;
  255. if( m_FmtScale.y < 0 )
  256. m_FmtScale.y = 0;
  257. if( m_FmtScale.y > 7 )
  258. m_FmtScale.y = 7;
  259. }
  260. aText++;
  261. }
  262. break;
  263. case '*':
  264. break;
  265. default:
  266. msg.Printf( wxT( "Unknown id (%c) in FS command" ),
  267. *aText );
  268. AddMessageToList( msg );
  269. GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
  270. ok = false;
  271. break;
  272. }
  273. }
  274. if( !x_fmt_known || !y_fmt_known )
  275. AddMessageToList( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
  276. break;
  277. case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
  278. m_SwapAxis = false;
  279. if( strncasecmp( aText, "AYBX", 4 ) == 0 )
  280. m_SwapAxis = true;
  281. break;
  282. case MIRROR_IMAGE: // command %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
  283. m_MirrorA = m_MirrorB = false;
  284. while( *aText && *aText != '*' )
  285. {
  286. switch( *aText )
  287. {
  288. case 'A': // Mirror A axis ?
  289. aText++;
  290. if( *aText == '1' )
  291. m_MirrorA = true;
  292. break;
  293. case 'B': // Mirror B axis ?
  294. aText++;
  295. if( *aText == '1' )
  296. m_MirrorB = true;
  297. break;
  298. default:
  299. aText++;
  300. break;
  301. }
  302. }
  303. break;
  304. case MODE_OF_UNITS:
  305. code = ReadXCommandID( aText );
  306. if( code == INCH )
  307. m_GerbMetric = false;
  308. else if( code == MILLIMETER )
  309. m_GerbMetric = true;
  310. conv_scale = m_GerbMetric ? gerbIUScale.IU_PER_MILS / 25.4 : gerbIUScale.IU_PER_MILS;
  311. break;
  312. case FILE_ATTRIBUTE: // Command %TF ...
  313. {
  314. X2_ATTRIBUTE dummy;
  315. dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
  316. if( dummy.IsFileFunction() )
  317. {
  318. delete m_FileFunction;
  319. m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy );
  320. // Don't set this until we get a file function; other code expects m_IsX2_file == true
  321. // to mean that we have a valid m_FileFunction
  322. m_IsX2_file = true;
  323. }
  324. else if( dummy.IsFileMD5() )
  325. {
  326. m_MD5_value = dummy.GetPrm( 1 );
  327. }
  328. else if( dummy.IsFilePart() )
  329. {
  330. m_PartString = dummy.GetPrm( 1 );
  331. }
  332. }
  333. break;
  334. case APERTURE_ATTRIBUTE: // Command %TA
  335. {
  336. X2_ATTRIBUTE dummy;
  337. dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
  338. if( dummy.GetAttribute() == wxT( ".AperFunction" ) )
  339. {
  340. m_AperFunction = dummy.GetPrm( 1 );
  341. // A few function values can have other parameters. Add them
  342. for( int ii = 2; ii < dummy.GetPrmCount(); ii++ )
  343. m_AperFunction << wxT( "," ) << dummy.GetPrm( ii );
  344. }
  345. }
  346. break;
  347. case NET_ATTRIBUTE: // Command %TO currently %TO.P %TO.N and %TO.C
  348. {
  349. X2_ATTRIBUTE dummy;
  350. dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
  351. if( dummy.GetAttribute() == wxT( ".N" ) )
  352. {
  353. m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_NET;
  354. m_NetAttributeDict.m_Netname = FormatStringFromGerber( dummy.GetPrm( 1 ) );
  355. }
  356. else if( dummy.GetAttribute() == wxT( ".C" ) )
  357. {
  358. m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_CMP;
  359. m_NetAttributeDict.m_Cmpref = FormatStringFromGerber( dummy.GetPrm( 1 ) );
  360. }
  361. else if( dummy.GetAttribute() == wxT( ".P" ) )
  362. {
  363. m_NetAttributeDict.m_NetAttribType |= GBR_NETLIST_METADATA::GBR_NETINFO_PAD;
  364. m_NetAttributeDict.m_Cmpref = FormatStringFromGerber( dummy.GetPrm( 1 ) );
  365. m_NetAttributeDict.m_Padname.SetField( FormatStringFromGerber( dummy.GetPrm( 2 ) ), true, true );
  366. if( dummy.GetPrmCount() > 3 )
  367. {
  368. m_NetAttributeDict.m_PadPinFunction.SetField(
  369. FormatStringFromGerber( dummy.GetPrm( 3 ) ), true, true );
  370. }
  371. else
  372. {
  373. m_NetAttributeDict.m_PadPinFunction.Clear();
  374. }
  375. }
  376. }
  377. break;
  378. case REMOVE_APERTURE_ATTRIBUTE: // Command %TD ...
  379. {
  380. X2_ATTRIBUTE dummy;
  381. dummy.ParseAttribCmd( m_Current_File, aBuff, aBuffSize, aText, m_LineNum );
  382. RemoveAttribute( dummy );
  383. }
  384. break;
  385. case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
  386. m_Offset.x = m_Offset.y = 0;
  387. while( *aText != '*' )
  388. {
  389. switch( *aText )
  390. {
  391. case 'A': // A axis offset in current unit (inch or mm)
  392. aText++;
  393. fcoord = ReadDouble( aText );
  394. m_Offset.x = KiROUND( fcoord * conv_scale );
  395. break;
  396. case 'B': // B axis offset in current unit (inch or mm)
  397. aText++;
  398. fcoord = ReadDouble( aText );
  399. m_Offset.y = KiROUND( fcoord * conv_scale );
  400. break;
  401. }
  402. }
  403. break;
  404. case SCALE_FACTOR:
  405. m_Scale.x = m_Scale.y = 1.0;
  406. while( *aText != '*' )
  407. {
  408. switch( *aText )
  409. {
  410. case 'A': // A axis scale
  411. aText++;
  412. m_Scale.x = ReadDouble( aText );
  413. break;
  414. case 'B': // B axis scale
  415. aText++;
  416. m_Scale.y = ReadDouble( aText );
  417. break;
  418. }
  419. }
  420. break;
  421. case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
  422. m_ImageOffset.x = m_ImageOffset.y = 0;
  423. while( *aText != '*' )
  424. {
  425. switch( *aText )
  426. {
  427. case 'A': // A axis offset in current unit (inch or mm)
  428. aText++;
  429. fcoord = ReadDouble( aText );
  430. m_ImageOffset.x = KiROUND( fcoord * conv_scale );
  431. break;
  432. case 'B': // B axis offset in current unit (inch or mm)
  433. aText++;
  434. fcoord = ReadDouble( aText );
  435. m_ImageOffset.y = KiROUND( fcoord * conv_scale );
  436. break;
  437. }
  438. }
  439. break;
  440. case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
  441. if( strncasecmp( aText, "0*", 2 ) == 0 )
  442. m_ImageRotation = 0;
  443. else if( strncasecmp( aText, "90*", 3 ) == 0 )
  444. m_ImageRotation = 90;
  445. else if( strncasecmp( aText, "180*", 4 ) == 0 )
  446. m_ImageRotation = 180;
  447. else if( strncasecmp( aText, "270*", 4 ) == 0 )
  448. m_ImageRotation = 270;
  449. else
  450. AddMessageToList( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
  451. break;
  452. case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
  453. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  454. GetLayerParams().m_StepForRepeat.x = 0.0;
  455. GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
  456. GetLayerParams().m_XRepeatCount = 1;
  457. GetLayerParams().m_YRepeatCount = 1; // The repeat count
  458. GetLayerParams().m_StepForRepeatMetric = m_GerbMetric; // the step units
  459. while( *aText && *aText != '*' )
  460. {
  461. switch( *aText )
  462. {
  463. case 'I': // X axis offset
  464. aText++;
  465. GetLayerParams().m_StepForRepeat.x = ReadDouble( aText );
  466. break;
  467. case 'J': // Y axis offset
  468. aText++;
  469. GetLayerParams().m_StepForRepeat.y = ReadDouble( aText );
  470. break;
  471. case 'X': // X axis repeat count
  472. aText++;
  473. GetLayerParams().m_XRepeatCount = ReadInt( aText );
  474. break;
  475. case 'Y': // Y axis offset
  476. aText++;
  477. GetLayerParams().m_YRepeatCount = ReadInt( aText );
  478. break;
  479. default:
  480. aText++;
  481. break;
  482. }
  483. }
  484. break;
  485. case IMAGE_JUSTIFY: // Command IJAnBn*
  486. m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
  487. m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
  488. m_ImageJustifyOffset = VECTOR2I( 0, 0 ); // Image Justify Offset on XY axis (default = 0,0)
  489. while( *aText && *aText != '*' )
  490. {
  491. // IJ command is (for A or B axis) AC or AL or A<coordinate>
  492. switch( *aText )
  493. {
  494. case 'A': // A axis justify
  495. aText++;
  496. if( *aText == 'C' )
  497. {
  498. m_ImageJustifyXCenter = true;
  499. aText++;
  500. }
  501. else if( *aText == 'L' )
  502. {
  503. m_ImageJustifyXCenter = true;
  504. aText++;
  505. }
  506. else
  507. {
  508. m_ImageJustifyOffset.x = KiROUND( ReadDouble( aText ) * conv_scale);
  509. }
  510. break;
  511. case 'B': // B axis justify
  512. aText++;
  513. if( *aText == 'C' )
  514. {
  515. m_ImageJustifyYCenter = true;
  516. aText++;
  517. }
  518. else if( *aText == 'L' )
  519. {
  520. m_ImageJustifyYCenter = true;
  521. aText++;
  522. }
  523. else
  524. {
  525. m_ImageJustifyOffset.y = KiROUND( ReadDouble( aText ) * conv_scale);
  526. }
  527. break;
  528. default:
  529. aText++;
  530. break;
  531. }
  532. }
  533. if( m_ImageJustifyXCenter )
  534. m_ImageJustifyOffset.x = 0;
  535. if( m_ImageJustifyYCenter )
  536. m_ImageJustifyOffset.y = 0;
  537. break;
  538. case KNOCKOUT:
  539. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  540. msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
  541. AddMessageToList( msg );
  542. break;
  543. case ROTATE: // Layer rotation: command like %RO45*%
  544. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  545. m_LocalRotation = ReadDouble( aText ); // Store layer rotation in degrees
  546. break;
  547. case IMAGE_NAME:
  548. m_ImageName.Empty();
  549. while( *aText != '*' )
  550. m_ImageName.Append( *aText++ );
  551. break;
  552. case LOAD_NAME:
  553. // %LN is a (deprecated) equivalentto G04: a comment
  554. while( *aText && *aText != '*' )
  555. aText++; // Skip text
  556. break;
  557. case IMAGE_POLARITY:
  558. if( strncasecmp( aText, "NEG", 3 ) == 0 )
  559. m_ImageNegative = true;
  560. else
  561. m_ImageNegative = false;
  562. break;
  563. case LOAD_POLARITY:
  564. if( *aText == 'C' )
  565. GetLayerParams().m_LayerNegative = true;
  566. else
  567. GetLayerParams().m_LayerNegative = false;
  568. break;
  569. case AP_MACRO: // lines like %AMMYMACRO*
  570. // 5,1,8,0,0,1.08239X$1,22.5*
  571. // %
  572. /*ok = */ReadApertureMacro( aBuff, aBuffSize, aText, m_Current_File );
  573. break;
  574. case AP_DEFINITION:
  575. /* input example: %ADD30R,0.081800X0.101500*%
  576. * Aperture definition has 4 options: C, R, O, P
  577. * (Circle, Rect, Oval, regular Polygon)
  578. * and shapes can have a hole (round or rectangular).
  579. * All optional parameters values start by X
  580. * at this point, text points to 2nd 'D'
  581. */
  582. if( *aText++ != 'D' )
  583. {
  584. ok = false;
  585. break;
  586. }
  587. m_Has_DCode = true;
  588. code = ReadInt( aText );
  589. D_CODE* dcode;
  590. dcode = GetDCODEOrCreate( code );
  591. if( dcode == nullptr )
  592. break;
  593. dcode->m_AperFunction = m_AperFunction;
  594. // at this point, text points to character after the ADD<num>,
  595. // i.e. R in example above. If aText[0] is one of the usual
  596. // apertures: (C,R,O,P), there is a comma after it.
  597. if( aText[1] == ',' )
  598. {
  599. char stdAperture = *aText;
  600. aText += 2; // skip "C," for example
  601. // First parameter is the size X:
  602. dcode->m_Size.x = KiROUND( ReadDouble( aText ) * conv_scale );
  603. dcode->m_Size.y = dcode->m_Size.x;
  604. switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
  605. {
  606. case 'C': // Circle
  607. dcode->m_Shape = APT_CIRCLE;
  608. while( *aText == ' ' )
  609. aText++;
  610. if( *aText == 'X' )
  611. {
  612. aText++;
  613. dcode->m_Drill.x = dcode->m_Drill.y =
  614. KiROUND( ReadDouble( aText ) * conv_scale );
  615. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  616. }
  617. while( *aText == ' ' )
  618. aText++;
  619. if( *aText == 'X' )
  620. {
  621. aText++;
  622. dcode->m_Drill.y =
  623. KiROUND( ReadDouble( aText ) * conv_scale );
  624. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  625. }
  626. dcode->m_Defined = true;
  627. break;
  628. case 'O': // oval
  629. case 'R': // rect
  630. dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
  631. while( *aText == ' ' )
  632. aText++;
  633. if( *aText == 'X' ) // Second parameter: size Y
  634. {
  635. aText++;
  636. dcode->m_Size.y =
  637. KiROUND( ReadDouble( aText ) * conv_scale );
  638. }
  639. while( *aText == ' ' )
  640. aText++;
  641. if( *aText == 'X' ) // third parameter: drill size (or drill size X)
  642. {
  643. aText++;
  644. dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
  645. dcode->m_Drill.y = dcode->m_Drill.x;
  646. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  647. }
  648. while( *aText == ' ' )
  649. aText++;
  650. if( *aText == 'X' ) // fourth parameter: drill size Y
  651. {
  652. aText++;
  653. dcode->m_Drill.y =
  654. KiROUND( ReadDouble( aText ) * conv_scale );
  655. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  656. }
  657. dcode->m_Defined = true;
  658. break;
  659. case 'P':
  660. /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
  661. * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
  662. */
  663. dcode->m_Shape = APT_POLYGON;
  664. while( *aText == ' ' )
  665. aText++;
  666. if( *aText == 'X' )
  667. {
  668. aText++;
  669. dcode->m_EdgesCount = ReadInt( aText );
  670. }
  671. while( *aText == ' ' )
  672. aText++;
  673. if( *aText == 'X' )
  674. {
  675. aText++;
  676. dcode->m_Rotation = EDA_ANGLE( ReadDouble( aText ), DEGREES_T );
  677. }
  678. while( *aText == ' ' )
  679. aText++;
  680. if( *aText == 'X' )
  681. {
  682. aText++;
  683. dcode->m_Drill.x = KiROUND( ReadDouble( aText ) * conv_scale );
  684. dcode->m_Drill.y = dcode->m_Drill.x;
  685. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  686. }
  687. while( *aText == ' ' )
  688. aText++;
  689. if( *aText == 'X' )
  690. {
  691. aText++;
  692. dcode->m_Drill.y = KiROUND( ReadDouble( aText ) * conv_scale );
  693. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  694. }
  695. dcode->m_Defined = true;
  696. break;
  697. }
  698. }
  699. else // aText[0] starts an aperture macro name
  700. {
  701. APERTURE_MACRO am_lookup;
  702. while( *aText && *aText != '*' && *aText != ',' )
  703. am_lookup.name.Append( *aText++ );
  704. // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
  705. // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
  706. if( *aText == ',' )
  707. { // Read aperture macro parameters and store them
  708. aText++; // aText points the first parameter
  709. while( *aText && *aText != '*' )
  710. {
  711. double param = ReadDouble( aText );
  712. dcode->AppendParam( param );
  713. while( isspace( *aText ) )
  714. aText++;
  715. // Skip 'X' separator:
  716. if( *aText == 'X' || *aText == 'x' )
  717. aText++;
  718. }
  719. }
  720. // lookup the aperture macro here.
  721. APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
  722. if( !pam )
  723. {
  724. msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
  725. TO_UTF8( am_lookup.name ) );
  726. AddMessageToList( msg );
  727. ok = false;
  728. break;
  729. }
  730. dcode->m_Shape = APT_MACRO;
  731. dcode->SetMacro( pam );
  732. dcode->m_Defined = true;
  733. }
  734. break;
  735. default:
  736. ok = false;
  737. break;
  738. }
  739. ignore_unused( seq_len );
  740. ok = GetEndOfBlock( aBuff, aBuffSize, aText, m_Current_File );
  741. return ok;
  742. }
  743. bool GERBER_FILE_IMAGE::GetEndOfBlock( char* aBuff, unsigned int aBuffSize, char*& aText, FILE* gerber_file )
  744. {
  745. for( ; ; )
  746. {
  747. while( (aText < aBuff + aBuffSize) && *aText )
  748. {
  749. if( *aText == '*' )
  750. return true;
  751. if( *aText == '%' )
  752. return true;
  753. aText++;
  754. }
  755. if( fgets( aBuff, aBuffSize, gerber_file ) == nullptr )
  756. break;
  757. m_LineNum++;
  758. aText = aBuff;
  759. }
  760. return false;
  761. }
  762. char* GERBER_FILE_IMAGE::GetNextLine( char *aBuff, unsigned int aBuffSize, char* aText, FILE* aFile )
  763. {
  764. for( ; ; )
  765. {
  766. switch (*aText )
  767. {
  768. case ' ': // skip blanks
  769. case '\n':
  770. case '\r': // Skip line terminators
  771. ++aText;
  772. break;
  773. case 0: // End of text found in aBuff: Read a new string
  774. if( fgets( aBuff, aBuffSize, aFile ) == nullptr )
  775. return nullptr;
  776. m_LineNum++;
  777. aText = aBuff;
  778. return aText;
  779. default:
  780. return aText;
  781. }
  782. }
  783. return aText;
  784. }
  785. bool GERBER_FILE_IMAGE::ReadApertureMacro( char *aBuff, unsigned int aBuffSize,
  786. char*& aText,
  787. FILE* gerber_file )
  788. {
  789. wxString msg;
  790. APERTURE_MACRO am;
  791. // read macro name
  792. while( *aText )
  793. {
  794. if( *aText == '*' )
  795. {
  796. ++aText;
  797. break;
  798. }
  799. am.name.Append( *aText++ );
  800. }
  801. // Read aperture macro parameters
  802. for( ; ; )
  803. {
  804. if( *aText == '*' )
  805. ++aText;
  806. aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
  807. if( aText == nullptr ) // End of File
  808. return false;
  809. // aText points the beginning of a new line.
  810. // Test for the last line in aperture macro lis:
  811. // last line is % or *% sometime found.
  812. if( *aText == '*' )
  813. ++aText;
  814. if( *aText == '%' )
  815. break; // exit with aText still pointing at %
  816. int paramCount = 0; // will be set to the minimal parameters count,
  817. // depending on the actual primitive
  818. int primitive_type = AMP_UNKNOWN;
  819. // Test for a valid symbol at the beginning of a description:
  820. // it can be: a parameter declaration like $1=$2/4
  821. // or a digit (macro primitive selection)
  822. // all other symbols are illegal.
  823. if( *aText == '$' ) // local parameter declaration, inside the aperture macro
  824. {
  825. am.m_localparamStack.push_back( AM_PARAM() );
  826. AM_PARAM& param = am.m_localparamStack.back();
  827. aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
  828. if( aText == nullptr) // End of File
  829. return false;
  830. param.ReadParam( aText );
  831. continue;
  832. }
  833. else if( !isdigit(*aText) ) // Ill. symbol
  834. {
  835. msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
  836. am.name, FROM_UTF8( aBuff ) );
  837. AddMessageToList( msg );
  838. primitive_type = AMP_COMMENT;
  839. }
  840. else
  841. primitive_type = ReadInt( aText );
  842. bool is_comment = false;
  843. switch( primitive_type )
  844. {
  845. case AMP_COMMENT: // lines starting by 0 are a comment
  846. paramCount = 0;
  847. is_comment = true;
  848. // Skip comment
  849. while( *aText && ( *aText != '*' ) )
  850. aText++;
  851. break;
  852. case AMP_CIRCLE:
  853. paramCount = 4; // minimal count. can have a optional parameter (rotation)
  854. break;
  855. case AMP_LINE2:
  856. case AMP_LINE20:
  857. paramCount = 7;
  858. break;
  859. case AMP_LINE_CENTER:
  860. case AMP_LINE_LOWER_LEFT:
  861. paramCount = 6;
  862. break;
  863. case AMP_EOF:
  864. paramCount = 0;
  865. break;
  866. case AMP_OUTLINE:
  867. paramCount = 4; // partial count. other parameters are vertices and rotation
  868. // Second parameter is vertice (coordinate pairs) count.
  869. break;
  870. case AMP_POLYGON:
  871. paramCount = 6;
  872. break;
  873. case AMP_MOIRE:
  874. paramCount = 9;
  875. break;
  876. case AMP_THERMAL:
  877. paramCount = 6;
  878. break;
  879. default:
  880. msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line %d: \"%s\"" ),
  881. am.name, primitive_type, m_LineNum, FROM_UTF8( aBuff ) );
  882. AddMessageToList( msg );
  883. return false;
  884. }
  885. if( is_comment )
  886. continue;
  887. AM_PRIMITIVE prim( m_GerbMetric );
  888. prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type;
  889. int ii;
  890. for( ii = 0; ii < paramCount && *aText && *aText != '*'; ++ii )
  891. {
  892. prim.params.push_back( AM_PARAM() );
  893. AM_PARAM& param = prim.params.back();
  894. aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
  895. if( aText == nullptr) // End of File
  896. return false;
  897. param.ReadParam( aText );
  898. }
  899. if( ii < paramCount )
  900. {
  901. // maybe some day we can throw an exception and track a line number
  902. msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient "
  903. "parameters\n" ),
  904. prim.primitive_id, ii );
  905. AddMessageToList( msg );
  906. }
  907. // there are more parameters to read if this is an AMP_OUTLINE
  908. if( prim.primitive_id == AMP_OUTLINE )
  909. {
  910. // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
  911. // Now read all the points, plus trailing rotation in degrees.
  912. // params[1] is a count of polygon points, so it must be given
  913. // in advance, i.e. be immediate.
  914. wxASSERT( prim.params[1].IsImmediate() );
  915. paramCount = (int) prim.params[1].GetValue( nullptr ) * 2 + 1;
  916. for( int jj = 0; jj < paramCount && *aText != '*'; ++jj )
  917. {
  918. prim.params.push_back( AM_PARAM() );
  919. AM_PARAM& param = prim.params.back();
  920. aText = GetNextLine( aBuff, aBuffSize, aText, gerber_file );
  921. if( aText == nullptr ) // End of File
  922. return false;
  923. param.ReadParam( aText );
  924. }
  925. }
  926. // AMP_CIRCLE can have a optional parameter (rotation)
  927. if( prim.primitive_id == AMP_CIRCLE && aText && *aText != '*' )
  928. {
  929. prim.params.push_back( AM_PARAM() );
  930. AM_PARAM& param = prim.params.back();
  931. param.ReadParam( aText );
  932. }
  933. am.primitives.push_back( prim );
  934. }
  935. m_aperture_macros.insert( am );
  936. return true;
  937. }