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.

1007 lines
30 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
// 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
// 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. * @file rs274x.cpp
  3. */
  4. #include <fctsys.h>
  5. #include <common.h>
  6. #include <macros.h>
  7. #include <base_units.h>
  8. #include <gerbview.h>
  9. #include <class_GERBER.h>
  10. extern int ReadInt( char*& text, bool aSkipSeparator = true );
  11. extern double ReadDouble( char*& text, bool aSkipSeparator = true );
  12. extern bool GetEndOfBlock( char buff[GERBER_BUFZ], char*& text, FILE* gerber_file );
  13. #define CODE( x, y ) ( ( (x) << 8 ) + (y) )
  14. // See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry
  15. // in gerber files, when a coordinate is given (like X78Y600 or I0J80):
  16. // Y and Y are logical coordinates
  17. // A and B are plotter coordiantes
  18. // Usually A = X, B = Y
  19. // But we can have A = Y, B = X and/or offset, mirror, scale;
  20. // Also:
  21. // Image is what you must plot (the entire data of the file).
  22. // Layer is just a set of data blocks with their parameters. An image can have more than one
  23. // layer so a gerber layer is not like a board layer or the graphic layers used in GerbView
  24. // to show a file.
  25. enum RS274X_PARAMETERS {
  26. // Directive parameters: single usage recommended
  27. // Must be at the beginning of the file
  28. AXIS_SELECT = CODE( 'A', 'S' ), // Default: A=X, B=Y
  29. FORMAT_STATEMENT = CODE( 'F', 'S' ), // no default: this command must exists
  30. MIRROR_IMAGE = CODE( 'M', 'I' ), // Default: mo mirror
  31. MODE_OF_UNITS = CODE( 'M', 'O' ), // Default: inch
  32. INCH = CODE( 'I', 'N' ),
  33. MILLIMETER = CODE( 'M', 'M' ),
  34. OFFSET = CODE( 'O', 'F' ), // Default: A = 0, B = 0
  35. SCALE_FACTOR = CODE( 'S', 'F' ), // Default: A = 1.0, B = 1.0
  36. // Image parameters:
  37. // commands used only once at the beginning of the file
  38. IMAGE_JUSTIFY = CODE( 'I', 'J' ), // Default: no justification
  39. IMAGE_NAME = CODE( 'I', 'N' ), // Default: void
  40. IMAGE_OFFSET = CODE( 'I', 'O' ), // Default: A = 0, B = 0
  41. IMAGE_POLARITY = CODE( 'I', 'P' ), // Default: Positive
  42. IMAGE_ROTATION = CODE( 'I', 'R' ), // Default: 0
  43. PLOTTER_FILM = CODE( 'P', 'M' ),
  44. // Aperture parameters:
  45. // Usually for the whole file
  46. AP_DEFINITION = CODE( 'A', 'D' ),
  47. AP_MACRO = CODE( 'A', 'M' ),
  48. // Layer specific parameters
  49. // May be used singly or may be layer specfic
  50. // theses parameters are at the beginning of the file or layer
  51. // and reset some layer parameters (like interpolation)
  52. LAYER_NAME = CODE( 'L', 'N' ), // Default: Positive
  53. LAYER_POLARITY = CODE( 'L', 'P' ),
  54. KNOCKOUT = CODE( 'K', 'O' ), // Default: off
  55. STEP_AND_REPEAT = CODE( 'S', 'R' ), // Default: A = 1, B = 1
  56. ROTATE = CODE( 'R', 'O' ), // Default: 0
  57. // Miscellaneous parameters:
  58. INCLUDE_FILE = CODE( 'I', 'F' )
  59. };
  60. /**
  61. * Function ReadXCommand
  62. * reads in two bytes of data and assembles them into an int with the first
  63. * byte in the sequence put into the most significant part of a 16 bit value
  64. * and the second byte put into the least significant part of the 16 bit value.
  65. * @param text A reference to a pointer to read bytes from and to advance as
  66. * they are read.
  67. * @return int - with 16 bits of data in the ls bits, upper bits zeroed.
  68. */
  69. static int ReadXCommand( char*& text )
  70. {
  71. int result;
  72. if( text && *text )
  73. result = *text++ << 8;
  74. else
  75. return -1;
  76. if( text && *text )
  77. result += *text++;
  78. else
  79. return -1;
  80. return result;
  81. }
  82. bool GERBER_IMAGE::ReadRS274XCommand( char buff[GERBER_BUFZ], char*& text )
  83. {
  84. bool ok = true;
  85. int code_command;
  86. text++;
  87. for( ; ; )
  88. {
  89. while( *text )
  90. {
  91. switch( *text )
  92. {
  93. case '%': // end of command
  94. text++;
  95. m_CommandState = CMD_IDLE;
  96. goto exit; // success completion
  97. case ' ':
  98. case '\r':
  99. case '\n':
  100. text++;
  101. break;
  102. case '*':
  103. text++;
  104. break;
  105. default:
  106. code_command = ReadXCommand( text );
  107. ok = ExecuteRS274XCommand( code_command, buff, text );
  108. if( !ok )
  109. goto exit;
  110. break;
  111. }
  112. }
  113. // end of current line, read another one.
  114. if( fgets( buff, GERBER_BUFZ, m_Current_File ) == NULL )
  115. {
  116. // end of file
  117. ok = false;
  118. break;
  119. }
  120. text = buff;
  121. }
  122. exit:
  123. return ok;
  124. }
  125. bool GERBER_IMAGE::ExecuteRS274XCommand( int command,
  126. char buff[GERBER_BUFZ],
  127. char*& text )
  128. {
  129. int code;
  130. int seq_len; // not used, just provided
  131. int seq_char;
  132. bool ok = true;
  133. char line[GERBER_BUFZ];
  134. wxString msg;
  135. double fcoord;
  136. bool x_fmt_known = false;
  137. bool y_fmt_known = false;
  138. // conv_scale = scaling factor from inch to Internal Unit
  139. double conv_scale = IU_PER_MILS * 1000;
  140. if( m_GerbMetric )
  141. conv_scale /= 25.4;
  142. // DBG( printf( "%22s: Command <%c%c>\n", __func__, (command >> 8) & 0xFF, command & 0xFF ); )
  143. switch( command )
  144. {
  145. case FORMAT_STATEMENT:
  146. seq_len = 2;
  147. while( *text != '*' )
  148. {
  149. switch( *text )
  150. {
  151. case ' ':
  152. text++;
  153. break;
  154. case 'L': // No Leading 0
  155. m_DecimalFormat = false;
  156. m_NoTrailingZeros = false;
  157. text++;
  158. break;
  159. case 'T': // No trailing 0
  160. m_DecimalFormat = false;
  161. m_NoTrailingZeros = true;
  162. text++;
  163. break;
  164. case 'A': // Absolute coord
  165. m_Relative = false;
  166. text++;
  167. break;
  168. case 'I': // Relative coord
  169. m_Relative = true;
  170. text++;
  171. break;
  172. case 'G':
  173. case 'N': // Sequence code (followed by one digit: the sequence len)
  174. // (sometimes found before the X,Y sequence)
  175. // Obscure option
  176. text++;
  177. seq_char = *text++;
  178. if( (seq_char >= '0') && (seq_char <= '9') )
  179. seq_len = seq_char - '0';
  180. break;
  181. case 'D':
  182. case 'M': // Sequence code (followed by one digit: the sequence len)
  183. // (sometimes found after the X,Y sequence)
  184. // Obscure option
  185. code = *text++;
  186. if( ( *text >= '0' ) && ( *text<= '9' ) )
  187. text++; // skip the digit
  188. else if( code == 'D' )
  189. // Decimal format: sometimes found, but not really documented
  190. m_DecimalFormat = true;
  191. break;
  192. case 'X':
  193. case 'Y':
  194. {
  195. code = *(text++);
  196. char ctmp = *(text++) - '0';
  197. if( code == 'X' )
  198. {
  199. x_fmt_known = true;
  200. // number of digits after the decimal point (0 to 6 allowed)
  201. m_FmtScale.x = *text - '0';
  202. m_FmtLen.x = ctmp + m_FmtScale.x;
  203. // m_FmtScale is 0 to 6
  204. if( m_FmtScale.x < 0 )
  205. m_FmtScale.x = 0;
  206. if( m_FmtScale.x > 6 )
  207. m_FmtScale.x = 6;
  208. }
  209. else
  210. {
  211. y_fmt_known = true;
  212. m_FmtScale.y = *text - '0';
  213. m_FmtLen.y = ctmp + m_FmtScale.y;
  214. if( m_FmtScale.y < 0 )
  215. m_FmtScale.y = 0;
  216. if( m_FmtScale.y > 6 )
  217. m_FmtScale.y = 6;
  218. }
  219. text++;
  220. }
  221. break;
  222. case '*':
  223. break;
  224. default:
  225. msg.Printf( wxT( "Unknown id (%c) in FS command" ),
  226. *text );
  227. ReportMessage( msg );
  228. GetEndOfBlock( buff, text, m_Current_File );
  229. ok = false;
  230. break;
  231. }
  232. }
  233. if( !x_fmt_known || !y_fmt_known )
  234. ReportMessage( wxT( "RS274X: Format Statement (FS) without X or Y format" ) );
  235. break;
  236. case AXIS_SELECT: // command ASAXBY*% or %ASAYBX*%
  237. m_SwapAxis = false;
  238. if( strnicmp( text, "AYBX", 4 ) == 0 )
  239. m_SwapAxis = true;
  240. break;
  241. case MIRROR_IMAGE: // commanf %MIA0B0*%, %MIA0B1*%, %MIA1B0*%, %MIA1B1*%
  242. m_MirrorA = m_MirrorB = 0;
  243. while( *text && *text != '*' )
  244. {
  245. switch( *text )
  246. {
  247. case 'A': // Mirror A axis ?
  248. text++;
  249. if( *text == '1' )
  250. m_MirrorA = true;
  251. break;
  252. case 'B': // Mirror B axis ?
  253. text++;
  254. if( *text == '1' )
  255. m_MirrorB = true;
  256. break;
  257. default:
  258. text++;
  259. break;
  260. }
  261. }
  262. break;
  263. case MODE_OF_UNITS:
  264. code = ReadXCommand( text );
  265. if( code == INCH )
  266. m_GerbMetric = false;
  267. else if( code == MILLIMETER )
  268. m_GerbMetric = true;
  269. conv_scale = m_GerbMetric ? IU_PER_MILS / 25.4 : IU_PER_MILS;
  270. break;
  271. case OFFSET: // command: OFAnnBnn (nn = float number) = layer Offset
  272. m_Offset.x = m_Offset.y = 0;
  273. while( *text != '*' )
  274. {
  275. switch( *text )
  276. {
  277. case 'A': // A axis offset in current unit (inch or mm)
  278. text++;
  279. fcoord = ReadDouble( text );
  280. m_Offset.x = KiROUND( fcoord * conv_scale );
  281. break;
  282. case 'B': // B axis offset in current unit (inch or mm)
  283. text++;
  284. fcoord = ReadDouble( text );
  285. m_Offset.y = KiROUND( fcoord * conv_scale );
  286. break;
  287. }
  288. }
  289. break;
  290. case SCALE_FACTOR:
  291. m_Scale.x = m_Scale.y = 1.0;
  292. while( *text != '*' )
  293. {
  294. switch( *text )
  295. {
  296. case 'A': // A axis scale
  297. text++;
  298. m_Scale.x = ReadDouble( text );
  299. break;
  300. case 'B': // B axis scale
  301. text++;
  302. m_Scale.y = ReadDouble( text );
  303. break;
  304. }
  305. }
  306. break;
  307. case IMAGE_OFFSET: // command: IOAnnBnn (nn = float number) = Image Offset
  308. m_ImageOffset.x = m_ImageOffset.y = 0;
  309. while( *text != '*' )
  310. {
  311. switch( *text )
  312. {
  313. case 'A': // A axis offset in current unit (inch or mm)
  314. text++;
  315. fcoord = ReadDouble( text );
  316. m_ImageOffset.x = KiROUND( fcoord * conv_scale );
  317. break;
  318. case 'B': // B axis offset in current unit (inch or mm)
  319. text++;
  320. fcoord = ReadDouble( text );
  321. m_ImageOffset.y = KiROUND( fcoord * conv_scale );
  322. break;
  323. }
  324. }
  325. break;
  326. case IMAGE_ROTATION: // command IR0* or IR90* or IR180* or IR270*
  327. if( strnicmp( text, "0*", 2 ) == 0 )
  328. m_ImageRotation = 0;
  329. if( strnicmp( text, "90*", 2 ) == 0 )
  330. m_ImageRotation = 90;
  331. if( strnicmp( text, "180*", 2 ) == 0 )
  332. m_ImageRotation = 180;
  333. if( strnicmp( text, "270*", 2 ) == 0 )
  334. m_ImageRotation = 270;
  335. else
  336. ReportMessage( _( "RS274X: Command \"IR\" rotation value not allowed" ) );
  337. break;
  338. case STEP_AND_REPEAT: // command SR, like %SRX3Y2I5.0J2*%
  339. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  340. GetLayerParams().m_StepForRepeat.x = 0.0;
  341. GetLayerParams().m_StepForRepeat.x = 0.0; // offset for Step and Repeat command
  342. GetLayerParams().m_XRepeatCount = 1;
  343. GetLayerParams().m_YRepeatCount = 1; // The repeat count
  344. GetLayerParams().m_StepForRepeatMetric = m_GerbMetric; // the step units
  345. while( *text && *text != '*' )
  346. {
  347. switch( *text )
  348. {
  349. case 'I': // X axis offset
  350. text++;
  351. GetLayerParams().m_StepForRepeat.x = ReadDouble( text );
  352. break;
  353. case 'J': // Y axis offset
  354. text++;
  355. GetLayerParams().m_StepForRepeat.y = ReadDouble( text );
  356. break;
  357. case 'X': // X axis repeat count
  358. text++;
  359. GetLayerParams().m_XRepeatCount = ReadInt( text );
  360. break;
  361. case 'Y': // Y axis offset
  362. text++;
  363. GetLayerParams().m_YRepeatCount = ReadInt( text );
  364. break;
  365. default:
  366. text++;
  367. break;
  368. }
  369. }
  370. break;
  371. case IMAGE_JUSTIFY: // Command IJAnBn*
  372. m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false)
  373. m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false)
  374. m_ImageJustifyOffset = wxPoint(0,0); // Image Justify Offset on XY axis (default = 0,0)
  375. while( *text && *text != '*' )
  376. {
  377. // IJ command is (for A or B axis) AC or AL or A<coordinate>
  378. switch( *text )
  379. {
  380. case 'A': // A axis justify
  381. text++;
  382. if( *text == 'C' )
  383. {
  384. m_ImageJustifyXCenter = true;
  385. text++;
  386. }
  387. else if( *text == 'L' )
  388. {
  389. m_ImageJustifyXCenter = true;
  390. text++;
  391. }
  392. else m_ImageJustifyOffset.x = KiROUND( ReadDouble( text ) * conv_scale);
  393. break;
  394. case 'B': // B axis justify
  395. text++;
  396. if( *text == 'C' )
  397. {
  398. m_ImageJustifyYCenter = true;
  399. text++;
  400. }
  401. else if( *text == 'L' )
  402. {
  403. m_ImageJustifyYCenter = true;
  404. text++;
  405. }
  406. else m_ImageJustifyOffset.y = KiROUND( ReadDouble( text ) * conv_scale);
  407. break;
  408. default:
  409. text++;
  410. break;
  411. }
  412. }
  413. if( m_ImageJustifyXCenter )
  414. m_ImageJustifyOffset.x = 0;
  415. if( m_ImageJustifyYCenter )
  416. m_ImageJustifyOffset.y = 0;
  417. break;
  418. case KNOCKOUT:
  419. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  420. msg = _( "RS274X: Command KNOCKOUT ignored by GerbView" ) ;
  421. ReportMessage( msg );
  422. break;
  423. case PLOTTER_FILM: // Command PF <string>
  424. // This is an info about film that must be used to plot this file
  425. // Has no meaning here. We just display this string
  426. msg = wxT( "Plotter Film info:<br>" );
  427. while( *text != '*' )
  428. {
  429. msg.Append( *text++ );
  430. }
  431. ReportMessage( msg );
  432. break;
  433. case ROTATE: // Layer rotation: command like %RO45*%
  434. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  435. m_LocalRotation =ReadDouble( text ); // Store layer rotation in degrees
  436. break;
  437. case IMAGE_NAME:
  438. m_ImageName.Empty();
  439. while( *text != '*' )
  440. {
  441. m_ImageName.Append( *text++ );
  442. }
  443. break;
  444. case LAYER_NAME:
  445. m_Iterpolation = GERB_INTERPOL_LINEAR_1X; // Start a new Gerber layer
  446. GetLayerParams( ).m_LayerName.Empty();
  447. while( *text != '*' )
  448. {
  449. GetLayerParams( ).m_LayerName.Append( *text++ );
  450. }
  451. break;
  452. case IMAGE_POLARITY:
  453. if( strnicmp( text, "NEG", 3 ) == 0 )
  454. m_ImageNegative = true;
  455. else
  456. m_ImageNegative = false;
  457. DBG( printf( "%22s: IMAGE_POLARITY m_ImageNegative=%s\n", __func__,
  458. m_ImageNegative ? "true" : "false" ); )
  459. break;
  460. case LAYER_POLARITY:
  461. if( *text == 'C' )
  462. GetLayerParams().m_LayerNegative = true;
  463. else
  464. GetLayerParams().m_LayerNegative = false;
  465. DBG( printf( "%22s: LAYER_POLARITY m_LayerNegative=%s\n", __func__,
  466. GetLayerParams().m_LayerNegative ? "true" : "false" ); )
  467. break;
  468. case INCLUDE_FILE:
  469. if( m_FilesPtr >= INCLUDE_FILES_CNT_MAX )
  470. {
  471. ok = false;
  472. ReportMessage( _( "Too many include files!!" ) );
  473. break;
  474. }
  475. strcpy( line, text );
  476. strtok( line, "*%%\n\r" );
  477. m_FilesList[m_FilesPtr] = m_Current_File;
  478. m_Current_File = fopen( line, "rt" );
  479. if( m_Current_File == 0 )
  480. {
  481. msg.Printf( wxT( "include file <%s> not found." ), line );
  482. ReportMessage( msg );
  483. ok = false;
  484. m_Current_File = m_FilesList[m_FilesPtr];
  485. break;
  486. }
  487. m_FilesPtr++;
  488. break;
  489. case AP_MACRO: // lines like %AMMYMACRO*
  490. // 5,1,8,0,0,1.08239X$1,22.5*
  491. // %
  492. ok = ReadApertureMacro( buff, text, m_Current_File );
  493. break;
  494. case AP_DEFINITION:
  495. /* input example: %ADD30R,0.081800X0.101500*%
  496. * Aperture definition has 4 options: C, R, O, P
  497. * (Circle, Rect, Oval, regular Polygon)
  498. * and shapes can have a hole (round or rectangular).
  499. * All optional parameters values start by X
  500. * at this point, text points to 2nd 'D'
  501. */
  502. if( *text++ != 'D' )
  503. {
  504. ok = false;
  505. break;
  506. }
  507. m_Has_DCode = true;
  508. code = ReadInt( text );
  509. D_CODE* dcode;
  510. dcode = GetDCODE( code );
  511. if( dcode == NULL )
  512. break;
  513. // at this point, text points to character after the ADD<num>,
  514. // i.e. R in example above. If text[0] is one of the usual
  515. // apertures: (C,R,O,P), there is a comma after it.
  516. if( text[1] == ',' )
  517. {
  518. char stdAperture = *text;
  519. text += 2; // skip "C," for example
  520. dcode->m_Size.x = KiROUND( ReadDouble( text ) * conv_scale );
  521. dcode->m_Size.y = dcode->m_Size.x;
  522. switch( stdAperture ) // Aperture desceiption has optional parameters. Read them
  523. {
  524. case 'C': // Circle
  525. dcode->m_Shape = APT_CIRCLE;
  526. while( *text == ' ' )
  527. text++;
  528. if( *text == 'X' )
  529. {
  530. text++;
  531. dcode->m_Drill.x = dcode->m_Drill.y =
  532. KiROUND( ReadDouble( text ) * conv_scale );
  533. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  534. }
  535. while( *text == ' ' )
  536. text++;
  537. if( *text == 'X' )
  538. {
  539. text++;
  540. dcode->m_Drill.y =
  541. KiROUND( ReadDouble( text ) * conv_scale );
  542. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  543. }
  544. dcode->m_Defined = true;
  545. break;
  546. case 'O': // oval
  547. case 'R': // rect
  548. dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
  549. while( *text == ' ' )
  550. text++;
  551. if( *text == 'X' )
  552. {
  553. text++;
  554. dcode->m_Size.y =
  555. KiROUND( ReadDouble( text ) * conv_scale );
  556. }
  557. while( *text == ' ' )
  558. text++;
  559. if( *text == 'X' )
  560. {
  561. text++;
  562. dcode->m_Drill.x = KiROUND( ReadDouble( text ) * conv_scale );
  563. dcode->m_Drill.y = dcode->m_Drill.x;
  564. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  565. }
  566. while( *text == ' ' )
  567. text++;
  568. if( *text == 'X' )
  569. {
  570. text++;
  571. dcode->m_Drill.y =
  572. KiROUND( ReadDouble( text ) * conv_scale );
  573. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  574. }
  575. dcode->m_Defined = true;
  576. break;
  577. case 'P':
  578. /* Regular polygon: a command line like %ADD12P,0.040X10X25X0.025X0.025X0.0150*%
  579. * params are: <diameter>, X<edge count>, X<Rotation>, X<X hole dim>, X<Y hole dim>
  580. */
  581. dcode->m_Shape = APT_POLYGON;
  582. while( *text == ' ' )
  583. text++;
  584. if( *text == 'X' )
  585. {
  586. text++;
  587. dcode->m_EdgesCount = ReadInt( text );
  588. }
  589. while( *text == ' ' )
  590. text++;
  591. if( *text == 'X' )
  592. {
  593. text++;
  594. dcode->m_Rotation = ReadDouble( text );
  595. }
  596. while( *text == ' ' )
  597. text++;
  598. if( *text == 'X' )
  599. {
  600. text++;
  601. dcode->m_Drill.x = KiROUND( ReadDouble( text ) * conv_scale );
  602. dcode->m_Drill.y = dcode->m_Drill.x =
  603. dcode->m_DrillShape = APT_DEF_ROUND_HOLE;
  604. }
  605. while( *text == ' ' )
  606. text++;
  607. if( *text == 'X' )
  608. {
  609. text++;
  610. dcode->m_Drill.y = KiROUND( ReadDouble( text ) * conv_scale );
  611. dcode->m_DrillShape = APT_DEF_RECT_HOLE;
  612. }
  613. dcode->m_Defined = true;
  614. break;
  615. }
  616. }
  617. else // text[0] starts an aperture macro name
  618. {
  619. APERTURE_MACRO am_lookup;
  620. while( *text && *text != '*' && *text != ',' )
  621. am_lookup.name.Append( *text++ );
  622. // When an aperture definition is like %AMLINE2* 22,1,$1,$2,0,0,-45*
  623. // the ADDxx<MACRO_NAME> command has parameters, like %ADD14LINE2,0.8X0.5*%
  624. if( *text == ',' )
  625. { // Read aperture macro parameters and store them
  626. text++; // text points the first parameter
  627. while( *text && *text != '*' )
  628. {
  629. double param = ReadDouble( text );
  630. dcode->AppendParam( param );
  631. while( isspace( *text ) ) text++;
  632. if( *text == 'X' )
  633. ++text;
  634. }
  635. }
  636. // lookup the aperture macro here.
  637. APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
  638. if( !pam )
  639. {
  640. msg.Printf( wxT( "RS274X: aperture macro %s not found\n" ),
  641. TO_UTF8( am_lookup.name ) );
  642. ReportMessage( msg );
  643. ok = false;
  644. break;
  645. }
  646. dcode->m_Shape = APT_MACRO;
  647. dcode->SetMacro( (APERTURE_MACRO*) pam );
  648. }
  649. break;
  650. default:
  651. ok = false;
  652. break;
  653. }
  654. (void) seq_len; // quiet g++, or delete the unused variable.
  655. ok = GetEndOfBlock( buff, text, m_Current_File );
  656. return ok;
  657. }
  658. bool GetEndOfBlock( char buff[GERBER_BUFZ], char*& text, FILE* gerber_file )
  659. {
  660. for( ; ; )
  661. {
  662. while( (text < buff + GERBER_BUFZ) && *text )
  663. {
  664. if( *text == '*' )
  665. return true;
  666. if( *text == '%' )
  667. return true;
  668. text++;
  669. }
  670. if( fgets( buff, GERBER_BUFZ, gerber_file ) == NULL )
  671. break;
  672. text = buff;
  673. }
  674. return false;
  675. }
  676. /**
  677. * Function GetNextLine
  678. * test for an end of line
  679. * if an end of line is found:
  680. * read a new line
  681. * @param aBuff = buffer (size = GERBER_BUFZ) to fill with a new line
  682. * @param aText = pointer to the last useful char in aBuff
  683. * on return: points the beginning of the next line.
  684. * @param aFile = the opened GERBER file to read
  685. * @return a pointer to the beginning of the next line or NULL if end of file
  686. */
  687. static char* GetNextLine( char aBuff[GERBER_BUFZ], char* aText, FILE* aFile )
  688. {
  689. for( ; ; )
  690. {
  691. switch (*aText )
  692. {
  693. case ' ': // skip blanks
  694. case '\n':
  695. case '\r': // Skip line terminators
  696. ++aText;
  697. break;
  698. case 0: // End of text found in aBuff: Read a new string
  699. if( fgets( aBuff, GERBER_BUFZ, aFile ) == NULL )
  700. return NULL;
  701. aText = aBuff;
  702. return aText;
  703. default:
  704. return aText;
  705. }
  706. }
  707. return aText;
  708. }
  709. bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ],
  710. char*& text,
  711. FILE* gerber_file )
  712. {
  713. wxString msg;
  714. APERTURE_MACRO am;
  715. // read macro name
  716. while( *text )
  717. {
  718. if( *text == '*' )
  719. {
  720. ++text;
  721. break;
  722. }
  723. am.name.Append( *text++ );
  724. }
  725. // Read aperture macro parameters
  726. for( ; ; )
  727. {
  728. if( *text == '*' )
  729. ++text;
  730. text = GetNextLine( buff, text, gerber_file ); // Get next line
  731. if( text == NULL ) // End of File
  732. return false;
  733. // text points the beginning of a new line.
  734. // Test for the last line in aperture macro lis:
  735. // last line is % or *% sometime found.
  736. if( *text == '*' )
  737. ++text;
  738. if( *text == '%' )
  739. break; // exit with text still pointing at %
  740. int paramCount = 0;
  741. int primitive_type = AMP_UNKNOWN;
  742. // Test for a valid symbol at the beginning of a description:
  743. // it can be: a parameter declaration like $1=$2/4
  744. // or a digit (macro primitive selection)
  745. // all other symbols are illegal.
  746. if( *text == '$' ) // local parameter declaration, inside the aperture macro
  747. {
  748. am.m_localparamStack.push_back( AM_PARAM() );
  749. AM_PARAM& param = am.m_localparamStack.back();
  750. text = GetNextLine( buff, text, gerber_file );
  751. if( text == NULL) // End of File
  752. return false;
  753. param.ReadParam( text );
  754. continue;
  755. }
  756. else if( !isdigit(*text) ) // Ill. symbol
  757. {
  758. msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": ill. symbol, line: \"%s\"" ),
  759. GetChars( am.name ), GetChars( FROM_UTF8( buff ) ) );
  760. ReportMessage( msg );
  761. primitive_type = AMP_COMMENT;
  762. }
  763. else
  764. primitive_type = ReadInt( text );
  765. switch( primitive_type )
  766. {
  767. case AMP_COMMENT: // lines starting by 0 are a comment
  768. paramCount = 0;
  769. // Skip comment
  770. while( *text && (*text != '*') )
  771. text++;
  772. break;
  773. case AMP_CIRCLE:
  774. paramCount = 4;
  775. break;
  776. case AMP_LINE2:
  777. case AMP_LINE20:
  778. paramCount = 7;
  779. break;
  780. case AMP_LINE_CENTER:
  781. case AMP_LINE_LOWER_LEFT:
  782. paramCount = 6;
  783. break;
  784. case AMP_EOF:
  785. paramCount = 0;
  786. break;
  787. case AMP_OUTLINE:
  788. paramCount = 4;
  789. break;
  790. case AMP_POLYGON:
  791. paramCount = 6;
  792. break;
  793. case AMP_MOIRE:
  794. paramCount = 9;
  795. break;
  796. case AMP_THERMAL:
  797. paramCount = 6;
  798. break;
  799. default:
  800. // @todo, there needs to be a way of reporting the line number
  801. msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line: \"%s\"" ),
  802. GetChars( am.name ), primitive_type, GetChars( FROM_UTF8( buff ) ) );
  803. ReportMessage( msg );
  804. return false;
  805. }
  806. AM_PRIMITIVE prim( m_GerbMetric );
  807. prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type;
  808. int i;
  809. for( i = 0; i < paramCount && *text && *text != '*'; ++i )
  810. {
  811. prim.params.push_back( AM_PARAM() );
  812. AM_PARAM& param = prim.params.back();
  813. text = GetNextLine( buff, text, gerber_file );
  814. if( text == NULL) // End of File
  815. return false;
  816. param.ReadParam( text );
  817. }
  818. if( i < paramCount )
  819. {
  820. // maybe some day we can throw an exception and track a line number
  821. msg.Printf( wxT( "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n" ),
  822. prim.primitive_id, i );
  823. ReportMessage( msg );
  824. }
  825. // there are more parameters to read if this is an AMP_OUTLINE
  826. if( prim.primitive_id == AMP_OUTLINE )
  827. {
  828. // so far we have read [0]:exposure, [1]:#points, [2]:X start, [3]: Y start
  829. // Now read all the points, plus trailing rotation in degrees.
  830. // params[1] is a count of polygon points, so it must be given
  831. // in advance, i.e. be immediate.
  832. wxASSERT( prim.params[1].IsImmediate() );
  833. paramCount = (int) prim.params[1].GetValue( 0 ) * 2 + 1;
  834. for( int i = 0; i < paramCount && *text != '*'; ++i )
  835. {
  836. prim.params.push_back( AM_PARAM() );
  837. AM_PARAM& param = prim.params.back();
  838. text = GetNextLine( buff, text, gerber_file );
  839. if( text == NULL ) // End of File
  840. return false;
  841. param.ReadParam( text );
  842. }
  843. }
  844. am.primitives.push_back( prim );
  845. }
  846. m_aperture_macros.insert( am );
  847. return true;
  848. }