kicad/gerbview/rs274_read_XY_and_IJ_coordi...

361 lines
10 KiB
C++
Raw Normal View History

2014-10-21 18:36:45 +00:00
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2010-2014 Jean-Pierre Charras jp.charras at wanadoo.fr
2021-07-16 20:13:26 +00:00
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
2014-10-21 18:36:45 +00:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <math/util.h> // for KiROUND
#include <gerber_file_image.h>
#include <base_units.h>
/* These routines read the text string point from Text.
* On exit, Text points the beginning of the sequence unread
*/
// conversion scale from gerber file units to Gerbview internal units
// depending on the gerber file format
// this scale list assumes gerber units are imperial.
// for metric gerber units, the imperial to metric conversion is made in read functions
#define SCALE_LIST_SIZE 9
static double scale_list[SCALE_LIST_SIZE] =
{
1000.0 * IU_PER_MILS, // x.1 format (certainly useless)
100.0 * IU_PER_MILS, // x.2 format (certainly useless)
10.0 * IU_PER_MILS, // x.3 format
1.0 * IU_PER_MILS, // x.4 format
0.1 * IU_PER_MILS, // x.5 format
0.01 * IU_PER_MILS, // x.6 format
0.001 * IU_PER_MILS, // x.7 format (currently the max allowed precision)
0.0001 * IU_PER_MILS, // provided, but not used
0.00001 * IU_PER_MILS, // provided, but not used
};
2021-07-16 20:13:26 +00:00
/**
* Convert a coordinate given in floating point to GerbView's internal units
* (currently = 10 nanometers).
*/
int scaletoIU( double aCoord, bool isMetric )
{
int ret;
if( isMetric ) // gerber are units in mm
ret = KiROUND( aCoord * IU_PER_MM );
else // gerber are units in inches
2014-10-21 18:36:45 +00:00
ret = KiROUND( aCoord * IU_PER_MILS * 1000.0 );
return ret;
}
2022-01-01 18:57:44 +00:00
VECTOR2I GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode )
{
2022-01-01 18:57:44 +00:00
VECTOR2I pos;
int type_coord = 0, current_coord, nbdigits;
bool is_float = false;
char* text;
char line[256];
if( m_Relative )
pos.x = pos.y = 0;
else
pos = m_CurrentPos;
2021-07-16 20:13:26 +00:00
if( Text == nullptr )
return pos;
text = line;
2021-07-16 20:13:26 +00:00
while( *Text )
{
2021-07-16 20:13:26 +00:00
if( ( *Text == 'X' ) || ( *Text == 'Y' ) || ( *Text == 'A' ) )
{
type_coord = *Text;
Text++;
text = line;
nbdigits = 0;
while( IsNumber( *Text ) )
{
2021-07-16 20:13:26 +00:00
if( *Text == '.' ) // Force decimal format if reading a floating point number
is_float = true;
// count digits only (sign and decimal point are not counted)
if( (*Text >= '0') && (*Text <='9') )
nbdigits++;
2021-07-16 20:13:26 +00:00
*(text++) = *(Text++);
}
*text = 0;
if( is_float )
{
// When X or Y (or A) values are float numbers, they are given in mm or inches
if( m_GerbMetric ) // units are mm
current_coord = KiROUND( atof( line ) * IU_PER_MILS / 0.0254 );
else // units are inches
current_coord = KiROUND( atof( line ) * IU_PER_MILS * 1000 );
}
else
{
int fmt_scale = (type_coord == 'X') ? m_FmtScale.x : m_FmtScale.y;
if( m_NoTrailingZeros )
{
// no trailing zero format, we need to add missing zeros.
int digit_count = (type_coord == 'X') ? m_FmtLen.x : m_FmtLen.y;
while( nbdigits < digit_count )
{
*(text++) = '0';
nbdigits++;
}
if( aExcellonMode )
{
// Truncate the extra digits if the len is more than expected
// because the conversion to internal units expect exactly
// digit_count digits
while( nbdigits > digit_count )
{
*(text--) = 0;
nbdigits--;
}
}
*text = 0;
}
current_coord = atoi( line );
double real_scale = scale_list[fmt_scale];
if( m_GerbMetric )
real_scale = real_scale / 25.4;
// 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; }
2012-04-19 06:55:45 +00:00
current_coord = KiROUND( current_coord * real_scale );
}
if( type_coord == 'X' )
2021-07-16 20:13:26 +00:00
{
pos.x = current_coord;
2021-07-16 20:13:26 +00:00
}
else if( type_coord == 'Y' )
2021-07-16 20:13:26 +00:00
{
pos.y = current_coord;
2021-07-16 20:13:26 +00:00
}
else if( type_coord == 'A' )
{
m_ArcRadius = current_coord;
m_LastArcDataType = ARC_INFO_TYPE_RADIUS;
}
continue;
}
else
2021-07-16 20:13:26 +00:00
{
break;
2021-07-16 20:13:26 +00:00
}
}
if( m_Relative )
{
pos.x += m_CurrentPos.x;
pos.y += m_CurrentPos.y;
}
m_CurrentPos = pos;
return pos;
}
2022-01-01 18:57:44 +00:00
VECTOR2I GERBER_FILE_IMAGE::ReadIJCoord( char*& Text )
{
2022-01-01 18:57:44 +00:00
VECTOR2I pos( 0, 0 );
int type_coord = 0, current_coord, nbdigits;
bool is_float = false;
char* text;
char line[256];
2021-07-16 20:13:26 +00:00
if( Text == nullptr )
return pos;
text = line;
2021-07-16 20:13:26 +00:00
while( *Text )
{
2021-07-16 20:13:26 +00:00
if( ( *Text == 'I' ) || ( *Text == 'J' ) )
{
type_coord = *Text;
Text++;
text = line;
nbdigits = 0;
2021-07-16 20:13:26 +00:00
while( IsNumber( *Text ) )
{
if( *Text == '.' )
is_float = true;
// count digits only (sign and decimal point are not counted)
2021-07-16 20:13:26 +00:00
if( ( *Text >= '0' ) && ( *Text <= '9' ) )
nbdigits++;
*(text++) = *(Text++);
}
*text = 0;
2021-07-16 20:13:26 +00:00
if( is_float )
{
// When X or Y values are float numbers, they are given in mm or inches
if( m_GerbMetric ) // units are mm
current_coord = KiROUND( atof( line ) * IU_PER_MILS / 0.0254 );
else // units are inches
current_coord = KiROUND( atof( line ) * IU_PER_MILS * 1000 );
}
else
{
2021-07-16 20:13:26 +00:00
int fmt_scale = ( type_coord == 'I' ) ? m_FmtScale.x : m_FmtScale.y;
if( m_NoTrailingZeros )
{
2021-07-16 20:13:26 +00:00
int min_digit = ( type_coord == 'I' ) ? m_FmtLen.x : m_FmtLen.y;
while( nbdigits < min_digit )
{
*(text++) = '0';
nbdigits++;
}
*text = 0;
}
current_coord = atoi( line );
double real_scale = scale_list[fmt_scale];
if( m_GerbMetric )
real_scale = real_scale / 25.4;
// 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; }
2012-04-19 06:55:45 +00:00
current_coord = KiROUND( current_coord * real_scale );
}
2021-07-16 20:13:26 +00:00
if( type_coord == 'I' )
pos.x = current_coord;
else if( type_coord == 'J' )
pos.y = current_coord;
continue;
}
else
2021-07-16 20:13:26 +00:00
{
break;
2021-07-16 20:13:26 +00:00
}
}
m_IJPos = pos;
m_LastArcDataType = ARC_INFO_TYPE_CENTER;
m_LastCoordIsIJPos = true;
return pos;
}
2010-11-30 20:41:35 +00:00
// Helper functions:
/**
2021-07-16 20:13:26 +00:00
* Read an integer from an ASCII character buffer.
*
* If there is a comma after the integer, then skip over that.
*
* @param text is a reference to a character pointer from which bytes are read
* and the pointer is advanced for each byte read.
* @param aSkipSeparator set to true (default) to skip comma.
* @return The integer read in.
2010-11-30 20:41:35 +00:00
*/
int ReadInt( char*& text, bool aSkipSeparator = true )
{
int ret;
// For strtol, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
// However, 'X' is a separator in Gerber strings with numbers.
// We need to detect that
2017-04-08 12:26:32 +00:00
if( strncasecmp( text, "0X", 2 ) == 0 )
{
text++;
ret = 0;
}
else
2021-07-16 20:13:26 +00:00
{
ret = (int) strtol( text, &text, 10 );
2021-07-16 20:13:26 +00:00
}
2010-11-30 20:41:35 +00:00
if( *text == ',' || isspace( *text ) )
{
2010-11-30 20:41:35 +00:00
if( aSkipSeparator )
++text;
}
2010-11-30 20:41:35 +00:00
return ret;
}
/**
2021-07-16 20:13:26 +00:00
* Read a double precision floating point number from an ASCII character buffer.
*
* If there is a comma after the number, then skip over that.
*
* @param text is a reference to a character pointer from which the ASCII double
* is read from and the pointer advanced for each character read.
* @param aSkipSeparator set to true (default) to skip comma.
* @return number read.
2010-11-30 20:41:35 +00:00
*/
double ReadDouble( char*& text, bool aSkipSeparator = true )
{
double ret;
// For strtod, a string starting by 0X or 0x is a valid number in hexadecimal or octal.
// However, 'X' is a separator in Gerber strings with numbers.
// We need to detect that
2017-04-08 12:26:32 +00:00
if( strncasecmp( text, "0X", 2 ) == 0 )
{
text++;
ret = 0.0;
}
else
2021-07-16 20:13:26 +00:00
{
ret = strtod( text, &text );
2021-07-16 20:13:26 +00:00
}
2010-11-30 20:41:35 +00:00
if( *text == ',' || isspace( *text ) )
{
2010-11-30 20:41:35 +00:00
if( aSkipSeparator )
++text;
}
2010-11-30 20:41:35 +00:00
return ret;
}