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.

347 lines
10 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2010-2014 Jean-Pierre Charras jp.charras at wanadoo.fr
  5. * Copyright The 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. #include <math/util.h> // for KiROUND
  25. #include <gerber_file_image.h>
  26. #include <base_units.h>
  27. /* These routines read the text string point from Text.
  28. * On exit, Text points the beginning of the sequence unread
  29. */
  30. // conversion scale from gerber file units to Gerbview internal units
  31. // depending on the gerber file format
  32. // this scale list assumes gerber units are imperial.
  33. // for metric gerber units, the imperial to metric conversion is made in read functions
  34. #define SCALE_LIST_SIZE 9
  35. static double scale_list[SCALE_LIST_SIZE] =
  36. {
  37. 1000.0 * GERB_IU_PER_MM * 0.0254, // x.1 format (certainly useless)
  38. 100.0 * GERB_IU_PER_MM * 0.0254, // x.2 format (certainly useless)
  39. 10.0 * GERB_IU_PER_MM * 0.0254, // x.3 format
  40. 1.0 * GERB_IU_PER_MM * 0.0254, // x.4 format
  41. 0.1 * GERB_IU_PER_MM * 0.0254, // x.5 format
  42. 0.01 * GERB_IU_PER_MM * 0.0254, // x.6 format
  43. 0.001 * GERB_IU_PER_MM * 0.0254, // x.7 format (currently the max allowed precision)
  44. 0.0001 * GERB_IU_PER_MM * 0.0254, // provided, but not used
  45. 0.00001 * GERB_IU_PER_MM * 0.0254, // provided, but not used
  46. };
  47. /**
  48. * Convert a coordinate given in floating point to GerbView's internal units
  49. * (currently = 10 nanometers).
  50. */
  51. int scaletoIU( double aCoord, bool isMetric )
  52. {
  53. int ret;
  54. if( isMetric ) // gerber are units in mm
  55. ret = KiROUND( aCoord * GERB_IU_PER_MM );
  56. else // gerber are units in inches
  57. ret = KiROUND( aCoord * GERB_IU_PER_MM * 25.4 );
  58. return ret;
  59. }
  60. // An useful function used when reading gerber files
  61. static bool IsNumber( char x )
  62. {
  63. return ( ( x >= '0' ) && ( x <='9' ) )
  64. || ( x == '-' ) || ( x == '+' ) || ( x == '.' );
  65. }
  66. VECTOR2I GERBER_FILE_IMAGE::ReadXYCoord( char*& aText, bool aExcellonMode )
  67. {
  68. VECTOR2I pos( 0, 0 );
  69. bool is_float = false;
  70. std::string line;
  71. // Reserve the anticipated length plus an optional sign and decimal
  72. line.reserve( std::max( m_FmtLen.x, m_FmtLen.y ) + 3 );
  73. // Set up return value for case where aText == nullptr
  74. if( !m_Relative )
  75. pos = m_CurrentPos;
  76. if( aText == nullptr )
  77. return pos;
  78. while( *aText && ( ( *aText == 'X' ) || ( *aText == 'Y' ) || ( *aText == 'A' ) ) )
  79. {
  80. double decimal_scale = 1.0;
  81. int nbdigits = 0;
  82. int current_coord = 0;
  83. char type_coord = *aText++;
  84. line.clear();
  85. while( IsNumber( *aText ) )
  86. {
  87. if( *aText == '.' ) // Force decimal format if reading a floating point number
  88. is_float = true;
  89. // count digits only (sign and decimal point are not counted)
  90. if( (*aText >= '0') && (*aText <='9') )
  91. nbdigits++;
  92. line.push_back( *( aText++ ) );
  93. }
  94. double val;
  95. wxString text( line.data() );
  96. text.ToCDouble( &val );
  97. if( is_float )
  98. {
  99. current_coord = scaletoIU( val, m_GerbMetric );
  100. }
  101. else
  102. {
  103. int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y;
  104. if( m_NoTrailingZeros )
  105. {
  106. // no trailing zero format, we need to add missing zeros.
  107. int digit_count = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y;
  108. // Truncate the extra digits if the len is more than expected
  109. // because the conversion to internal units expect exactly
  110. // digit_count digits. Alternatively, add some additional digits
  111. // to pad out to the missing zeros
  112. if( nbdigits < digit_count || ( aExcellonMode && ( nbdigits > digit_count ) ) )
  113. decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
  114. }
  115. double real_scale = scale_list[fmt_scale];
  116. if( m_GerbMetric )
  117. real_scale = real_scale / 25.4;
  118. current_coord = KiROUND( val * real_scale * decimal_scale );
  119. }
  120. if( type_coord == 'X' )
  121. {
  122. pos.x = current_coord;
  123. }
  124. else if( type_coord == 'Y' )
  125. {
  126. pos.y = current_coord;
  127. }
  128. else if( type_coord == 'A' )
  129. {
  130. m_ArcRadius = current_coord;
  131. m_LastArcDataType = ARC_INFO_TYPE_RADIUS;
  132. }
  133. }
  134. if( m_Relative )
  135. pos += m_CurrentPos;
  136. m_CurrentPos = pos;
  137. return pos;
  138. }
  139. VECTOR2I GERBER_FILE_IMAGE::ReadIJCoord( char*& aText )
  140. {
  141. VECTOR2I pos( 0, 0 );
  142. bool is_float = false;
  143. std::string line;
  144. // Reserve the anticipated length plus an optional sign and decimal
  145. line.reserve( std::max( m_FmtLen.x, m_FmtLen.y ) + 3 );
  146. if( aText == nullptr )
  147. return pos;
  148. while( *aText && ( ( *aText == 'I' ) || ( *aText == 'J' ) ) )
  149. {
  150. double decimal_scale = 1.0;
  151. int nbdigits = 0;
  152. int current_coord = 0;
  153. char type_coord = *aText++;
  154. line.clear();
  155. while( IsNumber( *aText ) )
  156. {
  157. if( *aText == '.' ) // Force decimal format if reading a floating point number
  158. is_float = true;
  159. // count digits only (sign and decimal point are not counted)
  160. if( (*aText >= '0') && (*aText <='9') )
  161. nbdigits++;
  162. line.push_back( *( aText++ ) );
  163. }
  164. double val;
  165. wxString text( line.data() );
  166. text.Trim( true ).Trim( false );
  167. text.ToCDouble( &val );
  168. if( is_float )
  169. {
  170. current_coord = scaletoIU( val, m_GerbMetric );
  171. }
  172. else
  173. {
  174. int fmt_scale = ( type_coord == 'I' ) ? m_FmtScale.x : m_FmtScale.y;
  175. if( m_NoTrailingZeros )
  176. {
  177. // no trailing zero format, we need to add missing zeros.
  178. int digit_count = ( type_coord == 'I' ) ? m_FmtLen.x : m_FmtLen.y;
  179. // Truncate the extra digits if the len is more than expected
  180. // because the conversion to internal units expect exactly
  181. // digit_count digits. Alternatively, add some additional digits
  182. // to pad out to the missing zeros
  183. if( nbdigits < digit_count )
  184. decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
  185. }
  186. double real_scale = scale_list[fmt_scale];
  187. if( m_GerbMetric )
  188. real_scale = real_scale / 25.4;
  189. current_coord = KiROUND( val * real_scale * decimal_scale );
  190. }
  191. if( type_coord == 'I' )
  192. {
  193. pos.x = current_coord;
  194. }
  195. else if( type_coord == 'J' )
  196. {
  197. pos.y = current_coord;
  198. }
  199. }
  200. m_IJPos = pos;
  201. m_LastArcDataType = ARC_INFO_TYPE_CENTER;
  202. m_LastCoordIsIJPos = true;
  203. return pos;
  204. }
  205. // Helper functions:
  206. /**
  207. * Read an integer from an ASCII character buffer.
  208. *
  209. * If there is a comma after the integer, then skip over that.
  210. *
  211. * @param text is a reference to a character pointer from which bytes are read
  212. * and the pointer is advanced for each byte read.
  213. * @param aSkipSeparator set to true (default) to skip comma.
  214. * @return The integer read in.
  215. */
  216. int ReadInt( char*& text, bool aSkipSeparator = true )
  217. {
  218. int ret;
  219. // For strtol, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
  220. // However, 'X' is a separator in Gerber strings with numbers.
  221. // We need to detect that
  222. if( strncasecmp( text, "0X", 2 ) == 0 )
  223. {
  224. text++;
  225. ret = 0;
  226. }
  227. else
  228. {
  229. ret = (int) strtol( text, &text, 10 );
  230. }
  231. if( *text == ',' || isspace( *text ) )
  232. {
  233. if( aSkipSeparator )
  234. ++text;
  235. }
  236. return ret;
  237. }
  238. /**
  239. * Read a double precision floating point number from an ASCII character buffer.
  240. *
  241. * If there is a comma after the number, then skip over that.
  242. *
  243. * @param text is a reference to a character pointer from which the ASCII double
  244. * is read from and the pointer advanced for each character read.
  245. * @param aSkipSeparator set to true (default) to skip comma.
  246. * @return number read.
  247. */
  248. double ReadDouble( char*& text, bool aSkipSeparator = true )
  249. {
  250. double ret;
  251. // For strtod, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
  252. // However, 'X' is a separator in Gerber strings with numbers.
  253. // We need to detect that
  254. if( strncasecmp( text, "0X", 2 ) == 0 )
  255. {
  256. text++;
  257. ret = 0.0;
  258. }
  259. else
  260. {
  261. wxString line( text );
  262. auto endpos = line.find_first_not_of( "0123456789.-+eE" );
  263. line.Trim( false );
  264. line.ToCDouble( &ret );
  265. if( endpos != wxString::npos )
  266. {
  267. // Advance the text pointer to the end of the number
  268. text += endpos;
  269. }
  270. else
  271. {
  272. // If no non-number characters found, advance to the end of the string
  273. text += line.length();
  274. }
  275. }
  276. if( *text == ',' || isspace( *text ) )
  277. {
  278. if( aSkipSeparator )
  279. ++text;
  280. }
  281. return ret;
  282. }