/* * 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 * Copyright (C) 1992-2014 KiCad Developers, see change_log.txt for contributors. * * 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 #include #include // for KiROUND #include #include /* These routines read the text string point from Text. * On exit, Text points the beginning of the sequence unread */ // convertion 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 }; /* * Function scale * converts a coordinate given in floating point to Gerbvies 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 ret = KiROUND( aCoord * IU_PER_MILS * 1000.0 ); return ret; } wxPoint GERBER_FILE_IMAGE::ReadXYCoord( char*& Text, bool aExcellonMode ) { wxPoint 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; if( Text == NULL ) return pos; text = line; while( *Text ) { if( (*Text == 'X') || (*Text == 'Y') || (*Text == 'A') ) { type_coord = *Text; Text++; text = line; nbdigits = 0; while( IsNumber( *Text ) ) { if( *Text == '.' ) // Force decimat 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++; *(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; current_coord = KiROUND( current_coord * real_scale ); } if( type_coord == 'X' ) pos.x = current_coord; else if( type_coord == 'Y' ) pos.y = current_coord; else if( type_coord == 'A' ) { m_ArcRadius = current_coord; m_LastArcDataType = ARC_INFO_TYPE_RADIUS; } continue; } else break; } if( m_Relative ) { pos.x += m_CurrentPos.x; pos.y += m_CurrentPos.y; } m_CurrentPos = pos; return pos; } /* Returns the current coordinate type pointed to by InnJnn Text (InnnnJmmmm) * These coordinates are relative, so if coordinate is absent, it's value * defaults to 0 */ wxPoint GERBER_FILE_IMAGE::ReadIJCoord( char*& Text ) { wxPoint pos( 0, 0 ); int type_coord = 0, current_coord, nbdigits; bool is_float = false; char* text; char line[256]; if( Text == NULL ) return pos; text = line; while( *Text ) { if( (*Text == 'I') || (*Text == 'J') ) { type_coord = *Text; Text++; text = line; nbdigits = 0; while( IsNumber( *Text ) ) { if( *Text == '.' ) is_float = true; // count digits only (sign and decimal point are not counted) if( (*Text >= '0') && (*Text <='9') ) nbdigits++; *(text++) = *(Text++); } *text = 0; 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 { int fmt_scale = (type_coord == 'I') ? m_FmtScale.x : m_FmtScale.y; if( m_NoTrailingZeros ) { 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; current_coord = KiROUND( current_coord * real_scale ); } if( type_coord == 'I' ) pos.x = current_coord; else if( type_coord == 'J' ) pos.y = current_coord; continue; } else break; } m_IJPos = pos; m_LastArcDataType = ARC_INFO_TYPE_CENTER; m_LastCoordIsIJPos = true; return pos; } // Helper functions: /** * Function ReadInt * reads an int from an ASCII character buffer. If there is a comma after the * int, then skip over that. * @param text A reference to a character pointer from which bytes are read * and the pointer is advanced for each byte read. * @param aSkipSeparator = true (default) to skip comma * @return int - The int read in. */ 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 if( strncasecmp( text, "0X", 2 ) == 0 ) { text++; ret = 0; } else ret = (int) strtol( text, &text, 10 ); if( *text == ',' || isspace( *text ) ) { if( aSkipSeparator ) ++text; } return ret; } /** * Function ReadDouble * reads a double from an ASCII character buffer. If there is a comma after * the double, then skip over that. * @param text A reference to a character pointer from which the ASCII double * is read from and the pointer advanced for each character read. * @param aSkipSeparator = true (default) to skip comma * @return double */ 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 if( strncasecmp( text, "0X", 2 ) == 0 ) { text++; ret = 0.0; } else ret = strtod( text, &text ); if( *text == ',' || isspace( *text ) ) { if( aSkipSeparator ) ++text; } return ret; }