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.

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