KiCad PCB EDA Suite
rs274_read_XY_and_IJ_coordinates.cpp
Go to the documentation of this file.
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-2021 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 #include <math/util.h> // for KiROUND
26 
27 #include <gerber_file_image.h>
28 #include <base_units.h>
29 
30 
31 /* These routines read the text string point from Text.
32  * On exit, Text points the beginning of the sequence unread
33  */
34 
35 // conversion scale from gerber file units to Gerbview internal units
36 // depending on the gerber file format
37 // this scale list assumes gerber units are imperial.
38 // for metric gerber units, the imperial to metric conversion is made in read functions
39 #define SCALE_LIST_SIZE 9
40 static double scale_list[SCALE_LIST_SIZE] =
41 {
42  1000.0 * GERB_IU_PER_MM * 0.0254, // x.1 format (certainly useless)
43  100.0 * GERB_IU_PER_MM * 0.0254, // x.2 format (certainly useless)
44  10.0 * GERB_IU_PER_MM * 0.0254, // x.3 format
45  1.0 * GERB_IU_PER_MM * 0.0254, // x.4 format
46  0.1 * GERB_IU_PER_MM * 0.0254, // x.5 format
47  0.01 * GERB_IU_PER_MM * 0.0254, // x.6 format
48  0.001 * GERB_IU_PER_MM * 0.0254, // x.7 format (currently the max allowed precision)
49  0.0001 * GERB_IU_PER_MM * 0.0254, // provided, but not used
50  0.00001 * GERB_IU_PER_MM * 0.0254, // provided, but not used
51 };
52 
53 
58 int scaletoIU( double aCoord, bool isMetric )
59 {
60  int ret;
61 
62  if( isMetric ) // gerber are units in mm
63  ret = KiROUND( aCoord * GERB_IU_PER_MM );
64  else // gerber are units in inches
65  ret = KiROUND( aCoord * GERB_IU_PER_MM * 25.4 );
66 
67  return ret;
68 }
69 
70 
71 wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& aText, bool aExcellonMode )
72 {
73  wxPoint pos( 0, 0 );
74  bool is_float = false;
75 
76  std::string line;
77 
78  // Reserve the anticipated length plus an optional sign and decimal
79  line.reserve( std::max( m_FmtLen.x, m_FmtLen.y ) + 3 );
80 
81  // Set up return value for case where aText == nullptr
82  if( !m_Relative )
83  pos = m_CurrentPos;
84 
85  if( aText == nullptr )
86  return pos;
87 
88  while( *aText && ( ( *aText == 'X' ) || ( *aText == 'Y' ) || ( *aText == 'A' ) ) )
89  {
90  double decimal_scale = 1.0;
91  int nbdigits = 0;
92  int current_coord = 0;
93  char type_coord = *aText++;
94 
95  line.clear();
96 
97  while( IsNumber( *aText ) )
98  {
99  if( *aText == '.' ) // Force decimal format if reading a floating point number
100  is_float = true;
101 
102  // count digits only (sign and decimal point are not counted)
103  if( (*aText >= '0') && (*aText <='9') )
104  nbdigits++;
105 
106  line.push_back( *( aText++ ) );
107  }
108 
109  double val = strtod( line.data(), nullptr );
110 
111  if( is_float )
112  {
113  current_coord = scaletoIU( val, m_GerbMetric );
114  }
115  else
116  {
117  int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y;
118 
119  if( m_NoTrailingZeros )
120  {
121  // no trailing zero format, we need to add missing zeros.
122  int digit_count = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y;
123 
124  // Truncate the extra digits if the len is more than expected
125  // because the conversion to internal units expect exactly
126  // digit_count digits. Alternatively, add some additional digits
127  // to pad out to the missing zeros
128  if( nbdigits < digit_count || ( aExcellonMode && ( nbdigits > digit_count ) ) )
129  decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
130  }
131 
132  double real_scale = scale_list[fmt_scale];
133 
134  if( m_GerbMetric )
135  real_scale = real_scale / 25.4;
136 
137  current_coord = KiROUND( val * real_scale * decimal_scale );
138  }
139 
140  if( type_coord == 'X' )
141  {
142  pos.x = current_coord;
143  }
144  else if( type_coord == 'Y' )
145  {
146  pos.y = current_coord;
147  }
148  else if( type_coord == 'A' )
149  {
150  m_ArcRadius = current_coord;
152  }
153  }
154 
155  if( m_Relative )
156  pos += m_CurrentPos;
157 
158  m_CurrentPos = pos;
159  return pos;
160 }
161 
162 
163 wxPoint GERBER_FILE_IMAGE::ReadIJCoord( char*& aText )
164 {
165  wxPoint pos( 0, 0 );
166  bool is_float = false;
167 
168  std::string line;
169 
170  // Reserve the anticipated length plus an optional sign and decimal
171  line.reserve( std::max( m_FmtLen.x, m_FmtLen.y ) + 3 );
172 
173  if( aText == nullptr )
174  return pos;
175 
176  while( *aText && ( ( *aText == 'I' ) || ( *aText == 'J' ) ) )
177  {
178  double decimal_scale = 1.0;
179  int nbdigits = 0;
180  int current_coord = 0;
181  char type_coord = *aText++;
182 
183  line.clear();
184 
185  while( IsNumber( *aText ) )
186  {
187  if( *aText == '.' ) // Force decimal format if reading a floating point number
188  is_float = true;
189 
190  // count digits only (sign and decimal point are not counted)
191  if( (*aText >= '0') && (*aText <='9') )
192  nbdigits++;
193 
194  line.push_back( *( aText++ ) );
195  }
196 
197  double val = strtod( line.data(), nullptr );
198 
199  if( is_float )
200  {
201  current_coord = scaletoIU( val, m_GerbMetric );
202  }
203  else
204  {
205  int fmt_scale = ( type_coord == 'I' ) ? m_FmtScale.x : m_FmtScale.y;
206 
207  if( m_NoTrailingZeros )
208  {
209  // no trailing zero format, we need to add missing zeros.
210  int digit_count = ( type_coord == 'I' ) ? m_FmtLen.x : m_FmtLen.y;
211 
212  // Truncate the extra digits if the len is more than expected
213  // because the conversion to internal units expect exactly
214  // digit_count digits. Alternatively, add some additional digits
215  // to pad out to the missing zeros
216  if( nbdigits < digit_count )
217  decimal_scale = std::pow<double>( 10, digit_count - nbdigits );
218  }
219 
220  double real_scale = scale_list[fmt_scale];
221 
222  if( m_GerbMetric )
223  real_scale = real_scale / 25.4;
224 
225  current_coord = KiROUND( val * real_scale * decimal_scale );
226  }
227 
228  if( type_coord == 'I' )
229  {
230  pos.x = current_coord;
231  }
232  else if( type_coord == 'J' )
233  {
234  pos.y = current_coord;
235  }
236  }
237 
238  m_IJPos = pos;
240  m_LastCoordIsIJPos = true;
241 
242  return pos;
243 }
244 
245 
246 // Helper functions:
247 
258 int ReadInt( char*& text, bool aSkipSeparator = true )
259 {
260  int ret;
261 
262  // For strtol, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
263  // However, 'X' is a separator in Gerber strings with numbers.
264  // We need to detect that
265  if( strncasecmp( text, "0X", 2 ) == 0 )
266  {
267  text++;
268  ret = 0;
269  }
270  else
271  {
272  ret = (int) strtol( text, &text, 10 );
273  }
274 
275  if( *text == ',' || isspace( *text ) )
276  {
277  if( aSkipSeparator )
278  ++text;
279  }
280 
281  return ret;
282 }
283 
284 
295 double ReadDouble( char*& text, bool aSkipSeparator = true )
296 {
297  double ret;
298 
299  // For strtod, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
300  // However, 'X' is a separator in Gerber strings with numbers.
301  // We need to detect that
302  if( strncasecmp( text, "0X", 2 ) == 0 )
303  {
304  text++;
305  ret = 0.0;
306  }
307  else
308  {
309  ret = strtod( text, &text );
310  }
311 
312  if( *text == ',' || isspace( *text ) )
313  {
314  if( aSkipSeparator )
315  ++text;
316  }
317 
318  return ret;
319 }
320 
int scaletoIU(double aCoord, bool isMetric)
Convert a coordinate given in floating point to GerbView's internal units (currently = 10 nanometers)...
Implementation of conversion functions that require both schematic and board internal units.
wxPoint ReadIJCoord(char *&Text)
Return the current coordinate type pointed to by InnJnn Text (InnnnJmmmm)
bool m_LastCoordIsIJPos
< True if a IJ coord was read (for arcs & circles ).
wxSize m_FmtLen
Image rotation (0, 90, 180, 270 only) in degrees.
double ReadDouble(char *&text, bool aSkipSeparator=true)
Read a double precision floating point number from an ASCII character buffer.
wxSize m_FmtScale
< Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4.
int m_ArcRadius
Identifier for arc data type (IJ (center) or A## (radius)).
static double scale_list[SCALE_LIST_SIZE]
bool m_Relative
< false = absolute Coord, true = relative Coord.
wxPoint ReadXYCoord(char *&aText, bool aExcellonMode=false)
Return the current coordinate type pointed to by XnnYnn Text (XnnnnYmmmm).
LAST_EXTRA_ARC_DATA_TYPE m_LastArcDataType
constexpr double GERB_IU_PER_MM
some define and functions to convert a value in mils, decimils or mm to the internal unit used in pcb...
int ReadInt(char *&text, bool aSkipSeparator=true)
Read an integer from an ASCII character buffer.
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
#define IsNumber(x)