kicad/gerbview/rs274x.cpp

663 lines
17 KiB
C++
Raw Normal View History

2007-09-25 15:10:01 +00:00
/********************************************************/
/* Routine de lecture d'un fichier GERBER format RS274X */
/********************************************************/
#include "fctsys.h"
#include "common.h"
#include "gerbview.h"
#include "pcbplot.h"
#include "protos.h"
2008-11-07 07:55:28 +00:00
#define CODE( x, y ) ( ((x) << 8) + (y) )
2007-09-25 15:10:01 +00:00
2008-11-08 06:44:07 +00:00
enum RS274X_PARAMETERS
{
2008-11-07 07:55:28 +00:00
FORMAT_STATEMENT = CODE( 'F', 'S' ),
AXIS_SELECT = CODE( 'A', 'S' ),
MIRROR_IMAGE = CODE( 'M', 'I' ),
MODE_OF_UNITS = CODE( 'M', 'O' ),
INCH = CODE( 'I', 'N' ),
MILLIMETER = CODE( 'M', 'M' ),
OFFSET = CODE( 'O', 'F' ),
SCALE_FACTOR = CODE( 'S', 'F' ),
IMAGE_NAME = CODE( 'I', 'N' ),
IMAGE_JUSTIFY = CODE( 'I', 'J' ),
IMAGE_OFFSET = CODE( 'I', 'O' ),
IMAGE_POLARITY = CODE( 'I', 'P' ),
IMAGE_ROTATION = CODE( 'I', 'R' ),
PLOTTER_FILM = CODE( 'P', 'M' ),
INCLUDE_FILE = CODE( 'I', 'F' ),
2008-11-08 06:44:07 +00:00
AP_DEFINITION = CODE( 'A', 'D' ),
AP_MACRO = CODE( 'A', 'M' ),
2008-11-07 07:55:28 +00:00
LAYER_NAME = CODE( 'L', 'N' ),
LAYER_POLARITY = CODE( 'L', 'P' ),
KNOCKOUT = CODE( 'K', 'O' ),
STEP_AND_REPEAT = CODE( 'S', 'P' ),
ROTATE = CODE( 'R', 'O' )
};
2008-11-07 07:55:28 +00:00
/* Local Functions */
2008-11-07 07:55:28 +00:00
/**
* Function ReadXCommand
* reads int two bytes of data and assembles them into an int with the first
* byte in the sequence put into the most significant part of a 16 bit value
* and the second byte put into the least significant part of the 16 bit value.
* @param text A reference to a pointer to read bytes from and to advance as they are read.
* @return int - with 16 bits of data in the ls bits, upper bits zeroed.
2007-09-25 15:10:01 +00:00
*/
static int ReadXCommand( char*& text )
{
2007-09-25 15:10:01 +00:00
int result;
if( text && *text )
2008-11-07 07:55:28 +00:00
result = *text++ << 8;
2007-09-25 15:10:01 +00:00
else
return -1;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
if( text && *text )
2008-11-07 07:55:28 +00:00
result += *text++;
2007-09-25 15:10:01 +00:00
else
return -1;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
return result;
}
2008-11-07 07:55:28 +00:00
/**
* Function ReadInt
2008-11-08 07:06:14 +00:00
* reads an int from an ASCII character buffer. If there is a comma after the
* int, then skip over that.
2008-11-07 07:55:28 +00:00
* @param text A reference to a character pointer from which bytes are read
* and the pointer is advanced for each byte read.
* @param int - The int read in.
*/
2007-09-25 15:10:01 +00:00
static int ReadInt( char*& text )
{
2008-11-08 06:44:07 +00:00
int ret = (int) strtol( text, &text, 10 );
if( *text == ',' )
++text;
return ret;
}
2008-11-07 07:55:28 +00:00
/**
* Function ReadDouble
* reads a double from an ASCII character buffer. If there is a comma after the double,
2008-11-08 07:06:14 +00:00
* then skip over that.
2008-11-07 07:55:28 +00:00
* @param text A reference to a character pointer from which the ASCII double
* is read from and the pointer advanced for each character read.
* @return double
*/
2007-09-25 15:10:01 +00:00
static double ReadDouble( char*& text )
{
2008-11-08 06:44:07 +00:00
double ret = strtod( text, &text );
if( *text == ',' )
++text;
return ret;
}
2007-09-25 15:10:01 +00:00
/****************************************************************************/
2008-11-08 06:44:07 +00:00
bool GERBER::ReadRS274XCommand( WinEDA_GerberFrame* frame, wxDC* DC,
2008-11-07 07:55:28 +00:00
char buff[GERBER_BUFZ], char*& text )
/****************************************************************************/
{
2007-09-25 15:10:01 +00:00
bool ok = true;
int code_command;
text++;
for(;;)
{
while( *text )
{
switch( *text )
{
2008-11-08 06:44:07 +00:00
case '%': // end of command
2007-09-25 15:10:01 +00:00
text++;
m_CommandState = CMD_IDLE;
goto exit; // success completion
case ' ':
case '\r':
case '\n':
text++;
break;
case '*':
text++;
break;
default:
code_command = ReadXCommand( text );
ok = ExecuteRS274XCommand( code_command, buff, text );
if( !ok )
goto exit;
break;
}
}
2008-11-08 06:44:07 +00:00
// end of current line, read another one.
2008-11-07 07:55:28 +00:00
if( fgets( buff, GERBER_BUFZ, m_Current_File ) == NULL )
2007-09-25 15:10:01 +00:00
{
2008-11-08 06:44:07 +00:00
// end of file
2007-09-25 15:10:01 +00:00
ok = false;
break;
}
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
text = buff;
}
exit:
return ok;
}
/*******************************************************************************/
2008-11-08 06:44:07 +00:00
bool GERBER::ExecuteRS274XCommand( int command, char buff[GERBER_BUFZ], char*& text )
/*******************************************************************************/
{
2007-09-25 15:10:01 +00:00
int code;
int xy_seq_len, xy_seq_char;
bool ok = TRUE;
2008-11-07 07:55:28 +00:00
char line[GERBER_BUFZ];
2007-09-25 15:10:01 +00:00
wxString msg;
double fcoord;
double conv_scale = m_GerbMetric ? PCB_INTERNAL_UNIT / 25.4 : PCB_INTERNAL_UNIT;
switch( command )
{
2008-11-07 07:55:28 +00:00
case FORMAT_STATEMENT:
2007-09-25 15:10:01 +00:00
xy_seq_len = 2;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
while( *text != '*' )
{
switch( *text )
{
case ' ':
text++;
break;
case 'L': // No Leading 0
m_NoTrailingZeros = FALSE;
text++;
break;
case 'T': // No trailing 0
m_NoTrailingZeros = TRUE;
text++;
break;
case 'A': // Absolute coord
2008-11-07 07:55:28 +00:00
m_Relative = FALSE;
text++;
2007-09-25 15:10:01 +00:00
break;
case 'I': // Absolute coord
2008-11-07 07:55:28 +00:00
m_Relative = TRUE;
text++;
2007-09-25 15:10:01 +00:00
break;
case 'N': // Sequence code (followed by the number of digits for the X,Y command
text++;
2008-11-07 07:55:28 +00:00
xy_seq_char = *text++;
2007-09-25 15:10:01 +00:00
if( (xy_seq_char >= '0') && (xy_seq_char <= '9') )
xy_seq_len = -'0';
break;
case 'X':
case 'Y': // Valeurs transmises :2 (really xy_seq_len : FIX ME) digits
{
code = *(text++);
char ctmp = *(text++) - '0';
if( code == 'X' )
{
m_FmtScale.x = *text - '0'; // = nb chiffres apres la virgule
m_FmtLen.x = ctmp + m_FmtScale.x; // = nb total de chiffres
}
else
{
m_FmtScale.y = *text - '0';
m_FmtLen.y = ctmp + m_FmtScale.y;
}
text++;
2007-09-25 15:10:01 +00:00
}
break;
case '*':
break;
default:
GetEndOfBlock( buff, text, m_Current_File );
ok = FALSE;
break;
}
}
break;
case AXIS_SELECT:
case MIRROR_IMAGE:
ok = FALSE;
break;
case MODE_OF_UNITS:
code = ReadXCommand( text );
if( code == INCH )
m_GerbMetric = FALSE;
else if( code == MILLIMETER )
m_GerbMetric = TRUE;
conv_scale = m_GerbMetric ? PCB_INTERNAL_UNIT / 25.4 : PCB_INTERNAL_UNIT;
break;
case OFFSET: // command: OFAnnBnn (nn = float number)
m_Offset.x = m_Offset.y = 0;
while( *text != '*' )
{
switch( *text )
{
case 'A': // A axis offset in current unit (inch ou mm)
text++;
fcoord = ReadDouble( text );
m_Offset.x = (int) round( fcoord * conv_scale );
break;
case 'B': // B axis offset in current unit (inch ou mm)
text++;
fcoord = ReadDouble( text );
m_Offset.y = (int) round( fcoord * conv_scale );
break;
}
}
break;
case SCALE_FACTOR:
case IMAGE_JUSTIFY:
case IMAGE_ROTATION:
case IMAGE_OFFSET:
case PLOTTER_FILM:
case LAYER_NAME:
case KNOCKOUT:
case STEP_AND_REPEAT:
case ROTATE:
msg.Printf( _( "Command <%c%c> ignored by Gerbview" ),
(command >> 8) & 0xFF, command & 0xFF );
if( g_DebugLevel > 0 )
wxMessageBox( msg );
break;
case IMAGE_NAME:
m_Name.Empty();
while( *text != '*' )
{
2008-11-07 07:55:28 +00:00
m_Name.Append( *text++ );
2007-09-25 15:10:01 +00:00
}
break;
case IMAGE_POLARITY:
if( strnicmp( text, "NEG", 3 ) == 0 )
m_ImageNegative = TRUE;
else
m_ImageNegative = FALSE;
break;
case LAYER_POLARITY:
if( *text == 'C' )
m_LayerNegative = TRUE;
else
m_LayerNegative = FALSE;
break;
case INCLUDE_FILE:
if( m_FilesPtr >= 10 )
{
ok = FALSE;
DisplayError( NULL, _( "Too many include files!!" ) );
break;
}
2008-11-07 07:55:28 +00:00
strcpy( line, text );
strtok( line, "*%%\n\r" );
2007-09-25 15:10:01 +00:00
m_FilesList[m_FilesPtr] = m_Current_File;
2008-11-07 07:55:28 +00:00
m_Current_File = fopen( line, "rt" );
2007-09-25 15:10:01 +00:00
if( m_Current_File == 0 )
{
wxString msg;
2008-11-07 07:55:28 +00:00
msg.Printf( wxT( "fichier <%s> non trouve" ), line );
2007-09-25 15:10:01 +00:00
DisplayError( NULL, msg, 10 );
ok = FALSE;
m_Current_File = m_FilesList[m_FilesPtr];
break;
}
m_FilesPtr++;
break;
case AP_MACRO:
ok = ReadApertureMacro( buff, text, m_Current_File );
if( !ok )
break;
break;
2008-11-08 06:44:07 +00:00
case AP_DEFINITION:
// input example: %ADD30R,0.081800X0.101500*%
// at this point, text points to 2nd 'D'
if( *text++ != 'D' )
2007-09-25 15:10:01 +00:00
{
2008-11-07 07:55:28 +00:00
ok = FALSE;
break;
2007-09-25 15:10:01 +00:00
}
m_Has_DCode = TRUE;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
code = ReadInt( text );
2008-11-07 07:55:28 +00:00
D_CODE* dcode;
dcode = GetDCODE( code );
2007-09-25 15:10:01 +00:00
if( dcode == NULL )
break;
2008-11-07 07:55:28 +00:00
// at this point, text points to character after the ADD<num>, i.e. R in example above
// if text[0] is one of the usual apertures: (C,R,O,P), there is a comma after it.
if( text[1] == ',' )
2007-09-25 15:10:01 +00:00
{
char stdAperture = *text;
text += 2; // skip "C," for example
2007-09-25 15:10:01 +00:00
dcode->m_Size.x = dcode->m_Size.y =
(int) round( ReadDouble( text ) * conv_scale );
switch( stdAperture )
2007-09-25 15:10:01 +00:00
{
case 'C': // Circle
2008-11-08 06:44:07 +00:00
dcode->m_Shape = APT_CIRCLE;
2007-09-25 15:10:01 +00:00
while( *text == ' ' )
text++;
if( *text == 'X' )
{
text++;
dcode->m_Drill.x = dcode->m_Drill.y =
(int) round( ReadDouble( text ) * conv_scale );
dcode->m_DrillShape = 1;
}
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
while( *text == ' ' )
text++;
if( *text == 'X' )
{
text++;
dcode->m_Drill.y =
(int) round( ReadDouble( text ) * conv_scale );
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
dcode->m_DrillShape = 2;
}
dcode->m_Defined = TRUE;
break;
2008-11-08 06:44:07 +00:00
case 'O': // oval
2007-09-25 15:10:01 +00:00
case 'R': // rect
dcode->m_Shape = (stdAperture == 'O') ? APT_OVAL : APT_RECT;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
while( *text == ' ' )
text++;
if( *text == 'X' )
{
text++;
dcode->m_Size.y =
(int) round( ReadDouble( text ) * conv_scale );
}
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
while( *text == ' ' )
text++;
if( *text == 'X' )
{
text++;
dcode->m_Drill.x = dcode->m_Drill.y =
(int) round( ReadDouble( text ) * conv_scale );
dcode->m_DrillShape = 1;
}
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
while( *text == ' ' )
text++;
if( *text == 'Y' )
{
text++;
dcode->m_Drill.y =
(int) round( ReadDouble( text ) * conv_scale );
dcode->m_DrillShape = 2;
}
dcode->m_Defined = TRUE;
break;
2008-11-08 06:44:07 +00:00
case 'P': // Polygon
dcode->m_Shape = APT_POLYGON;
2007-09-25 15:10:01 +00:00
dcode->m_Defined = TRUE;
break;
}
}
else // text[0] starts an aperture macro name
{
APERTURE_MACRO am_lookup;
while( *text && *text!='*' && *text!=',' )
am_lookup.name.Append( *text++ );
if( *text && *text==',' )
{
while( *text && *text!='*' )
{
double param = ReadDouble( text );
if( *text == 'X' )
++text;
dcode->AppendParam( param );
}
}
// lookup the aperture macro here.
APERTURE_MACRO* pam = FindApertureMacro( am_lookup );
if( !pam )
{
// @todo not found, don't know how to report an error
D( printf("aperture macro %s not found\n", CONV_TO_UTF8(am_lookup.name) );)
ok = false;
break;
}
2008-11-14 07:46:43 +00:00
dcode->m_Shape = APT_MACRO;
D(printf("pam has %d parameters\n", pam->primitives.size() );)
dcode->SetMacro( (APERTURE_MACRO*) pam );
}
2007-09-25 15:10:01 +00:00
break;
default:
ok = FALSE;
break;
}
ok = GetEndOfBlock( buff, text, m_Current_File );
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
return ok;
}
2007-09-25 15:10:01 +00:00
/*****************************************************************/
2008-11-07 07:55:28 +00:00
bool GetEndOfBlock( char buff[GERBER_BUFZ], char*& text, FILE* gerber_file )
/*****************************************************************/
{
2007-09-25 15:10:01 +00:00
for(;;)
{
2008-11-07 07:55:28 +00:00
while( (text < buff + GERBER_BUFZ) && *text )
2007-09-25 15:10:01 +00:00
{
if( *text == '*' )
return TRUE;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
if( *text == '%' )
return TRUE;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
text++;
}
2008-11-07 07:55:28 +00:00
if( fgets( buff, GERBER_BUFZ, gerber_file ) == NULL )
2007-09-25 15:10:01 +00:00
break;
2008-11-07 07:55:28 +00:00
2007-09-25 15:10:01 +00:00
text = buff;
}
return FALSE;
}
/*******************************************************************/
2008-11-08 06:44:07 +00:00
bool GERBER::ReadApertureMacro( char buff[GERBER_BUFZ], char*& text, FILE* gerber_file )
/*******************************************************************/
{
2008-11-14 18:26:56 +00:00
APERTURE_MACRO am;
2007-09-25 15:10:01 +00:00
2008-11-08 06:44:07 +00:00
// read macro name
while( *text )
2007-09-25 15:10:01 +00:00
{
if( *text == '*' )
2008-11-08 06:44:07 +00:00
{
++text;
2007-09-25 15:10:01 +00:00
break;
2008-11-08 06:44:07 +00:00
}
2008-11-07 07:55:28 +00:00
2008-11-08 06:44:07 +00:00
am.name.Append( *text++ );
2007-09-25 15:10:01 +00:00
}
if( g_DebugLevel > 0 )
2008-11-08 06:44:07 +00:00
wxMessageBox( am.name, wxT( "macro name" ) );
2007-09-25 15:10:01 +00:00
2008-11-08 06:44:07 +00:00
for(;;)
2007-09-25 15:10:01 +00:00
{
2008-11-14 18:26:56 +00:00
am.primitives.push_back( AM_PRIMITIVE() );
AM_PRIMITIVE& prim = am.primitives.back();
2008-11-08 06:44:07 +00:00
2007-09-25 15:10:01 +00:00
if( *text == '*' )
2008-11-08 06:44:07 +00:00
++text;
2008-11-07 07:55:28 +00:00
2008-11-08 06:44:07 +00:00
if( *text == '\n' )
++text;
if( !*text )
{
text = buff;
if( fgets( buff, GERBER_BUFZ, gerber_file ) == NULL )
return false;
}
if( *text == '%' )
break; // exit with text still pointing at %
prim.primitive_id = (AM_PRIMITIVE_ID) ReadInt( text );
int paramCount;
switch( prim.primitive_id )
{
case AMP_CIRCLE:
paramCount = 4;
break;
case AMP_LINE2:
case AMP_LINE20:
paramCount = 7;
break;
case AMP_LINE_CENTER:
case AMP_LINE_LOWER_LEFT:
paramCount = 6;
break;
case AMP_EOF:
paramCount = 0;
break;
case AMP_OUTLINE:
paramCount = 4;
break;
case AMP_POLYGON:
paramCount = 4;
break;
case AMP_MOIRE:
paramCount = 9;
break;
case AMP_THERMAL:
paramCount = 6;
break;
default:
// @todo, there needs to be a way of reporting the line number and character offset.
D(printf("Invalid primitive id code %d\n", prim.primitive_id );)
return false;
2008-11-08 06:44:07 +00:00
}
for( int i=0; i<paramCount && *text && *text!='*'; ++i )
{
2008-11-14 18:26:56 +00:00
prim.params.push_back( DCODE_PARAM() );
DCODE_PARAM& param = prim.params.back();
2008-11-08 06:44:07 +00:00
if( *text == '$' )
{
++text;
param.SetIndex( ReadInt( text ) );
2008-11-08 06:44:07 +00:00
}
else
param.SetValue( ReadDouble( text ) );
2008-11-08 06:44:07 +00:00
}
// there are more parameters to read if this is an AMP_OUTLINE
2008-11-08 06:44:07 +00:00
if( prim.primitive_id == AMP_OUTLINE )
{
// params[1] is a count of polygon points, so it must be given
// in advance, i.e. be immediate.
wxASSERT( prim.params[1].IsImmediate() );
paramCount = (int) prim.params[1].GetValue(0) * 2 + 1;
2008-11-08 06:44:07 +00:00
for( int i=0; i<paramCount && *text && *text!='*'; ++i )
{
2008-11-14 18:26:56 +00:00
prim.params.push_back( DCODE_PARAM() );
DCODE_PARAM& param = prim.params.back();
2008-11-08 06:44:07 +00:00
if( *text == '$' )
{
++text;
param.SetIndex( ReadInt( text ) );
2008-11-08 06:44:07 +00:00
}
else
param.SetValue( ReadDouble( text ) );
2008-11-08 06:44:07 +00:00
}
}
2007-09-25 15:10:01 +00:00
}
2008-11-08 06:44:07 +00:00
m_aperture_macros.insert( am );
return true;
}
2008-11-07 07:55:28 +00:00