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.

328 lines
9.6 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 (C) 1992-2023 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 = strtod( line.data(), nullptr );
  95. if( is_float )
  96. {
  97. current_coord = scaletoIU( val, m_GerbMetric );
  98. }
  99. else
  100. {
  101. int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y;
  102. if( m_NoTrailingZeros )
  103. {
  104. // no trailing zero format, we need to add missing zeros.
  105. int digit_count = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y;
  106. // Truncate the extra digits if the len is more than expected
  107. // because the conversion to internal units expect exactly
  108. // digit_count digits. Alternatively, add some additional digits
  109. // to pad out to the missing zeros
  110. if( nbdigits < digit_count || ( aExcellonMode && ( nbdigits > digit_count ) ) )
  111. decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
  112. }
  113. double real_scale = scale_list[fmt_scale];
  114. if( m_GerbMetric )
  115. real_scale = real_scale / 25.4;
  116. current_coord = KiROUND( val * real_scale * decimal_scale );
  117. }
  118. if( type_coord == 'X' )
  119. {
  120. pos.x = current_coord;
  121. }
  122. else if( type_coord == 'Y' )
  123. {
  124. pos.y = current_coord;
  125. }
  126. else if( type_coord == 'A' )
  127. {
  128. m_ArcRadius = current_coord;
  129. m_LastArcDataType = ARC_INFO_TYPE_RADIUS;
  130. }
  131. }
  132. if( m_Relative )
  133. pos += m_CurrentPos;
  134. m_CurrentPos = pos;
  135. return pos;
  136. }
  137. VECTOR2I GERBER_FILE_IMAGE::ReadIJCoord( char*& aText )
  138. {
  139. VECTOR2I pos( 0, 0 );
  140. bool is_float = false;
  141. std::string line;
  142. // Reserve the anticipated length plus an optional sign and decimal
  143. line.reserve( std::max( m_FmtLen.x, m_FmtLen.y ) + 3 );
  144. if( aText == nullptr )
  145. return pos;
  146. while( *aText && ( ( *aText == 'I' ) || ( *aText == 'J' ) ) )
  147. {
  148. double decimal_scale = 1.0;
  149. int nbdigits = 0;
  150. int current_coord = 0;
  151. char type_coord = *aText++;
  152. line.clear();
  153. while( IsNumber( *aText ) )
  154. {
  155. if( *aText == '.' ) // Force decimal format if reading a floating point number
  156. is_float = true;
  157. // count digits only (sign and decimal point are not counted)
  158. if( (*aText >= '0') && (*aText <='9') )
  159. nbdigits++;
  160. line.push_back( *( aText++ ) );
  161. }
  162. double val = strtod( line.data(), nullptr );
  163. if( is_float )
  164. {
  165. current_coord = scaletoIU( val, m_GerbMetric );
  166. }
  167. else
  168. {
  169. int fmt_scale = ( type_coord == 'I' ) ? m_FmtScale.x : m_FmtScale.y;
  170. if( m_NoTrailingZeros )
  171. {
  172. // no trailing zero format, we need to add missing zeros.
  173. int digit_count = ( type_coord == 'I' ) ? m_FmtLen.x : m_FmtLen.y;
  174. // Truncate the extra digits if the len is more than expected
  175. // because the conversion to internal units expect exactly
  176. // digit_count digits. Alternatively, add some additional digits
  177. // to pad out to the missing zeros
  178. if( nbdigits < digit_count )
  179. decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
  180. }
  181. double real_scale = scale_list[fmt_scale];
  182. if( m_GerbMetric )
  183. real_scale = real_scale / 25.4;
  184. current_coord = KiROUND( val * real_scale * decimal_scale );
  185. }
  186. if( type_coord == 'I' )
  187. {
  188. pos.x = current_coord;
  189. }
  190. else if( type_coord == 'J' )
  191. {
  192. pos.y = current_coord;
  193. }
  194. }
  195. m_IJPos = pos;
  196. m_LastArcDataType = ARC_INFO_TYPE_CENTER;
  197. m_LastCoordIsIJPos = true;
  198. return pos;
  199. }
  200. // Helper functions:
  201. /**
  202. * Read an integer from an ASCII character buffer.
  203. *
  204. * If there is a comma after the integer, then skip over that.
  205. *
  206. * @param text is a reference to a character pointer from which bytes are read
  207. * and the pointer is advanced for each byte read.
  208. * @param aSkipSeparator set to true (default) to skip comma.
  209. * @return The integer read in.
  210. */
  211. int ReadInt( char*& text, bool aSkipSeparator = true )
  212. {
  213. int ret;
  214. // For strtol, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
  215. // However, 'X' is a separator in Gerber strings with numbers.
  216. // We need to detect that
  217. if( strncasecmp( text, "0X", 2 ) == 0 )
  218. {
  219. text++;
  220. ret = 0;
  221. }
  222. else
  223. {
  224. ret = (int) strtol( text, &text, 10 );
  225. }
  226. if( *text == ',' || isspace( *text ) )
  227. {
  228. if( aSkipSeparator )
  229. ++text;
  230. }
  231. return ret;
  232. }
  233. /**
  234. * Read a double precision floating point number from an ASCII character buffer.
  235. *
  236. * If there is a comma after the number, then skip over that.
  237. *
  238. * @param text is a reference to a character pointer from which the ASCII double
  239. * is read from and the pointer advanced for each character read.
  240. * @param aSkipSeparator set to true (default) to skip comma.
  241. * @return number read.
  242. */
  243. double ReadDouble( char*& text, bool aSkipSeparator = true )
  244. {
  245. double ret;
  246. // For strtod, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
  247. // However, 'X' is a separator in Gerber strings with numbers.
  248. // We need to detect that
  249. if( strncasecmp( text, "0X", 2 ) == 0 )
  250. {
  251. text++;
  252. ret = 0.0;
  253. }
  254. else
  255. {
  256. ret = strtod( text, &text );
  257. }
  258. if( *text == ',' || isspace( *text ) )
  259. {
  260. if( aSkipSeparator )
  261. ++text;
  262. }
  263. return ret;
  264. }