From de37bbad1714235e456fc2da0e200ddc1320dd65 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 17 Oct 2010 14:32:35 +0200 Subject: [PATCH] Gerbview: fixed an issue with some gerber files. Added Image Justify support (partial support). --- gerbview/class_GERBER.cpp | 45 ++++ gerbview/class_GERBER.h | 10 + gerbview/class_gerber_draw_item.cpp | 13 +- gerbview/class_gerber_draw_item.h | 2 +- gerbview/edit.cpp | 6 + .../test-image-justify-with-offset.gbr | 19 ++ .../gerber_test_files/test-image-offset.gbr | 18 ++ gerbview/rs274x.cpp | 214 +++++++++++++----- 8 files changed, 263 insertions(+), 64 deletions(-) create mode 100644 gerbview/gerber_test_files/test-image-justify-with-offset.gbr create mode 100644 gerbview/gerber_test_files/test-image-offset.gbr diff --git a/gerbview/class_GERBER.cpp b/gerbview/class_GERBER.cpp index 8bbbd48805..d33ee21d66 100644 --- a/gerbview/class_GERBER.cpp +++ b/gerbview/class_GERBER.cpp @@ -151,6 +151,9 @@ void GERBER_IMAGE::ResetDefaultValues() m_FileName.Empty(); m_ImageName = wxT( "no name" ); // Image name from the IN command m_ImageNegative = false; // true = Negative image + m_ImageJustifyOffset = wxPoint(0,0); // Image justify Offset + m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false) + m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false) m_GerbMetric = false; // false = Inches (default), true = metric m_Relative = false; // false = absolute Coord, // true = relative Coord @@ -271,3 +274,45 @@ void GERBER_IMAGE::StepAndRepeatItem( const GERBER_DRAW_ITEM& aItem ) } } } + + +/** Function DisplayInfo + * has knowledge about the frame and how and where to put status information + * about this object into the frame's message panel. + * Display info about Image Parameters. + */ +void GERBER_IMAGE::DisplayImageInfo( void ) +{ + wxString msg; + + m_Parent->ClearMsgPanel(); + + // Display Image name + m_Parent->AppendMsgPanel( _( "Image name" ), m_ImageName, BROWN ); + + // Display graphic layer number + msg.Printf( wxT( "%d" ), m_GraphicLayer + 1 ); + m_Parent->AppendMsgPanel( _( "Graphic layer" ), msg, BROWN ); + + // This next info can be see as debug info, so it can be disabled + + // Display rotation + msg.Printf( wxT( "%d" ), m_ImageRotation / 10 ); + m_Parent->AppendMsgPanel( _( "Rotation" ), msg, CYAN ); + + // Display Image justification; + msg = m_ImageJustifyXCenter ? _("Center") : _("Normal"); + m_Parent->AppendMsgPanel( _( "X Justify" ), msg, DARKRED ); + + msg = m_ImageJustifyYCenter ? _("Center") : _("Normal"); + m_Parent->AppendMsgPanel( _( "Y Justify" ), msg, DARKRED ); + + if( g_UserUnit == INCHES ) + msg.Printf( wxT( "X=%f Y=%f" ), (double) m_ImageJustifyOffset.x/10000, + (double) m_ImageJustifyOffset.y/10000 ); + else + msg.Printf( wxT( "X=%f Y=%f" ), (double) m_ImageJustifyOffset.x*2.54/1000, + (double) m_ImageJustifyOffset.y*2.54/1000 ); + m_Parent->AppendMsgPanel( _( "Image Justify Offset" ), msg, CYAN ); +} + diff --git a/gerbview/class_GERBER.h b/gerbview/class_GERBER.h index 2a766bb069..024b33b4f1 100644 --- a/gerbview/class_GERBER.h +++ b/gerbview/class_GERBER.h @@ -86,6 +86,9 @@ public: wxString m_ImageName; // Image name, from IN * command int m_GraphicLayer; // Graphic layer Number bool m_ImageNegative; // true = Negative image + bool m_ImageJustifyXCenter; // Image Justify Center on X axis (default = false) + bool m_ImageJustifyYCenter; // Image Justify Center on Y axis (default = false) + wxPoint m_ImageJustifyOffset; // Image Justify Offset on XY axis (default = 0,0) bool m_GerbMetric; // false = Inches, true = metric bool m_Relative; // false = absolute Coord, true = relative Coord bool m_NoTrailingZeros; // true: remove tailing zeros. @@ -236,6 +239,13 @@ public: * @param aItem = the item to repeat */ void StepAndRepeatItem( const GERBER_DRAW_ITEM& aItem ); + + /** Function DisplayImageInfo + * has knowledge about the frame and how and where to put status information + * about this object into the frame's message panel. + * Display info about Image Parameters. + */ + void DisplayImageInfo( void ); }; diff --git a/gerbview/class_gerber_draw_item.cpp b/gerbview/class_gerber_draw_item.cpp index 76bcc7a8e5..356fc2bfa5 100644 --- a/gerbview/class_gerber_draw_item.cpp +++ b/gerbview/class_gerber_draw_item.cpp @@ -120,7 +120,7 @@ wxPoint GERBER_DRAW_ITEM::GetABPosition( const wxPoint& aXYPosition ) * For instance: Rotation must be made after or before mirroring ? * Note: if something is changed here, GetYXPosition must reflect changes */ - wxPoint abPos = aXYPosition; + wxPoint abPos = aXYPosition + m_imageParams->m_ImageJustifyOffset; if( m_swapAxis ) EXCHG( abPos.x, abPos.y ); @@ -165,7 +165,7 @@ wxPoint GERBER_DRAW_ITEM::GetXYPosition( const wxPoint& aABPosition ) xyPos -= m_layerOffset + m_imageParams->m_ImageOffset; if( m_swapAxis ) EXCHG( xyPos.x, xyPos.y ); - return xyPos; + return xyPos - m_imageParams->m_ImageJustifyOffset; } @@ -458,7 +458,7 @@ void GERBER_DRAW_ITEM::DrawGbrPoly( EDA_Rect* aClipBox, /** Function DisplayInfo * has knowledge about the frame and how and where to put status information * about this object into the frame's message panel. - * Display info about the track segment only, and does not calculate the full track length + * Display info about this GERBER item * @param frame A WinEDA_DrawFrame in which to print status information. */ void GERBER_DRAW_ITEM::DisplayInfo( WinEDA_DrawFrame* frame ) @@ -473,13 +473,6 @@ void GERBER_DRAW_ITEM::DisplayInfo( WinEDA_DrawFrame* frame ) msg.Printf( wxT( "%d" ), m_DCode ); frame->AppendMsgPanel( _( "D Code" ), msg, RED ); - // Display Image name - if( m_imageParams ) - { - msg = m_imageParams->m_ImageName; - frame->AppendMsgPanel( _( "Image name" ), msg, BROWN ); - } - // Display graphic layer number msg.Printf( wxT( "%d" ), GetLayer() + 1 ); frame->AppendMsgPanel( _( "Graphic layer" ), msg, BROWN ); diff --git a/gerbview/class_gerber_draw_item.h b/gerbview/class_gerber_draw_item.h index a96f3559fa..ad3d41298d 100644 --- a/gerbview/class_gerber_draw_item.h +++ b/gerbview/class_gerber_draw_item.h @@ -209,7 +209,7 @@ public: * has knowledge about the frame and how and where to put status information * about this object into the frame's message panel. * Is virtual from EDA_BaseStruct. - * Display info about the track segment and the full track length + * Display info about this GERBER item * @param frame A WinEDA_DrawFrame in which to print status information. */ void DisplayInfo( WinEDA_DrawFrame* frame ); diff --git a/gerbview/edit.cpp b/gerbview/edit.cpp index bca791f288..a23c696d3c 100644 --- a/gerbview/edit.cpp +++ b/gerbview/edit.cpp @@ -37,6 +37,12 @@ void WinEDA_GerberFrame::OnLeftClick( wxDC* DC, const wxPoint& MousePos ) { DrawStruct = GerberGeneralLocateAndDisplay(); GetScreen()->SetCurItem( DrawStruct ); + if( DrawStruct == NULL ) + { + GERBER_IMAGE* gerber = g_GERBER_List[getActiveLayer() ]; + if( gerber ) + gerber->DisplayImageInfo( ); + } } } diff --git a/gerbview/gerber_test_files/test-image-justify-with-offset.gbr b/gerbview/gerber_test_files/test-image-justify-with-offset.gbr new file mode 100644 index 0000000000..6e71d62f4e --- /dev/null +++ b/gerbview/gerber_test_files/test-image-justify-with-offset.gbr @@ -0,0 +1,19 @@ +G04 Test image justify 1* +G04 Crosshairs should be justified to the X axis * +G04 and 0.5 inches offset from Y axis * +G04 Handcoded by Julian Lamb * +%MOIN*% +%FSLAX23Y23*% +%IJB.5*% +%ADD10C,0.050*% + +G04 Crosshairs * +X-1000Y0D02* +G54D10* +X1000Y0D01* + +X0Y-1000D02* +G54D10* +X0Y1000D01* + +M02* diff --git a/gerbview/gerber_test_files/test-image-offset.gbr b/gerbview/gerber_test_files/test-image-offset.gbr new file mode 100644 index 0000000000..11242cfc9c --- /dev/null +++ b/gerbview/gerber_test_files/test-image-offset.gbr @@ -0,0 +1,18 @@ +G04 Test image offset * +G04 Crosshairs should be centered on 0,0 in final rendering* +G04 Handcoded by Julian Lamb * +%MOIN*% +%FSLAX23Y23*% +%IOA-2.0B-1.0*% +%ADD10C,0.050*% + +G04 Crosshairs to be on 0,0 * +X1000Y1000D02* +G54D10* +X3000Y1000D01* + +X2000Y0D02* +G54D10* +X2000Y2000D01* + +M02* diff --git a/gerbview/rs274x.cpp b/gerbview/rs274x.cpp index a436e68055..5f91e75325 100644 --- a/gerbview/rs274x.cpp +++ b/gerbview/rs274x.cpp @@ -11,6 +11,9 @@ #define CODE( x, y ) ( ( (x) << 8 ) + (y) ) +// Helper function to read a primitive macro param (TODO: make it DCODE_PARAM function) +static bool ReadMacroParam( DCODE_PARAM& aParam, char*& aText ); + // See rs274xrevd_e.pdf, table 1: RS-274X parameters order of entry // in gerber files, when a coordinate is given (like X78Y600 or I0J80): // Y and Y are logical coordinates @@ -94,14 +97,16 @@ static int ReadXCommand( char*& text ) * 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 int - The int read in. + * @param aSkipSeparator = true (default) to skip comma + * @return int - The int read in. */ -static int ReadInt( char*& text ) +static int ReadInt( char*& text, bool aSkipSeparator = true ) { int ret = (int) strtol( text, &text, 10 ); if( *text == ',' || isspace( *text ) ) - ++text; + if( aSkipSeparator ) + ++text; return ret; } @@ -113,14 +118,16 @@ static int ReadInt( char*& text ) * 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 */ -static double ReadDouble( char*& text ) +static double ReadDouble( char*& text, bool aSkipSeparator = true ) { double ret = strtod( text, &text ); if( *text == ',' || isspace( *text ) ) - ++text; + if( aSkipSeparator ) + ++text; return ret; } @@ -427,11 +434,68 @@ bool GERBER_IMAGE::ExecuteRS274XCommand( int command, } break; - case IMAGE_JUSTIFY: - case PLOTTER_FILM: + case IMAGE_JUSTIFY: // Command IJAnBn* + m_ImageJustifyXCenter = false; // Image Justify Center on X axis (default = false) + m_ImageJustifyYCenter = false; // Image Justify Center on Y axis (default = false) + m_ImageJustifyOffset = wxPoint(0,0); // Image Justify Offset on XY axis (default = 0,0) + while( *text && *text != '*' ) + { + // IJ command is (for A or B axis) AC or AL or A + switch( *text ) + { + case 'A': // A axis justify + text++; + if( *text == 'C' ) + { + m_ImageJustifyXCenter = true; + text++; + } + else if( *text == 'L' ) + { + m_ImageJustifyXCenter = true; + text++; + } + else m_ImageJustifyOffset.x = wxRound( ReadDouble( text ) * conv_scale); + break; + + case 'B': // B axis justify + text++; + if( *text == 'C' ) + { + m_ImageJustifyYCenter = true; + text++; + } + else if( *text == 'L' ) + { + m_ImageJustifyYCenter = true; + text++; + } + else m_ImageJustifyOffset.y = wxRound( ReadDouble( text ) * conv_scale); + break; + default: + text++; + break; + } + } + if( m_ImageJustifyXCenter ) + m_ImageJustifyOffset.x = 0; + if( m_ImageJustifyYCenter ) + m_ImageJustifyOffset.y = 0; + break; + case KNOCKOUT: - msg.Printf( _( "RS274X: Command \"%c%c\" ignored by Gerbview" ), - (command >> 8) & 0xFF, command & 0xFF ); + msg = _( "RS274X: Command KNOCKOUT ignored by Gerbview" ) ; + ReportMessage( msg ); + break; + + case PLOTTER_FILM: // Command PF + // This is an info about film that must be used to plot this file + // Has no meaning here. We just display this string + msg = ( "Plotter Film info:
" ); + while( *text != '*' ) + { + msg.Append( *text++ ); + } ReportMessage( msg ); break; @@ -498,10 +562,10 @@ bool GERBER_IMAGE::ExecuteRS274XCommand( int command, m_FilesPtr++; break; - case AP_MACRO: + case AP_MACRO: // lines like %AMMYMACRO* + // 5,1,8,0,0,1.08239X$1,22.5* + // % ok = ReadApertureMacro( buff, text, m_Current_File ); - if( !ok ) - break; break; case AP_DEFINITION: @@ -729,24 +793,39 @@ bool GetEndOfBlock( char buff[GERBER_BUFZ], char*& text, FILE* gerber_file ) return FALSE; } - -static bool CheckForLineEnd( char buff[GERBER_BUFZ], char*& text, FILE* fp ) +/** function GetNextLine + * test for an end of line + * if an end of line is found: + * read a new line + * @param aBuff[GERBER_BUFZ] = buffer to fill with a new line + * @param aText = pointer to the last useful char in aBuff + * on return: points the beginning of the next line. + * @param aFile = the opened GERBER file to read + * @return a pointer to the beginning of the next line or NULL if end of file +*/ +static char* GetNextLine( char aBuff[GERBER_BUFZ], char* aText, FILE* aFile ) { - while( *text == '\n' || *text == '\r' || !*text ) + for( ; ; ) { - if( *text == '\n' || *text == '\r' ) - ++text; - - if( !*text ) + switch (*aText ) { - if( fgets( buff, GERBER_BUFZ, fp ) == NULL ) - return false; + case ' ': // skip blanks + case '\n': + case '\r': // Skip line terminators + ++aText; + break; - text = buff; + case 0: // End of text found in aBuff: Read a new string + if( fgets( aBuff, GERBER_BUFZ, aFile ) == NULL ) + return NULL; + aText = aBuff; + return aText; + + default: + return aText; } } - - return true; + return aText; } @@ -769,24 +848,28 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], am.name.Append( *text++ ); } + // Read aperture macro parameters for( ; ; ) { - AM_PRIMITIVE prim( m_GerbMetric ); - if( *text == '*' ) ++text; - if( !CheckForLineEnd( buff, text, gerber_file ) ) + text = GetNextLine( buff, text, gerber_file ); // Get next line + if( text == NULL ) // End of File return false; + // text points the beginning of a new line. + + // Test for the last line in aperture macro lis: + // last line is % or *% sometime found. + if( *text == '*' ) + ++text; if( *text == '%' ) break; // exit with text still pointing at % - prim.primitive_id = (AM_PRIMITIVE_ID) ReadInt( text ); - - int paramCount; - - switch( prim.primitive_id ) + int paramCount = 0; + int primitive_type = ReadInt( text ); + switch( primitive_type ) { case AMP_CIRCLE: paramCount = 4; @@ -823,30 +906,26 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], break; default: - // @todo, there needs to be a way of reporting the line number - msg.Printf( wxT( "RS274X: Invalid primitive id code %d\n" ), prim.primitive_id ); + msg.Printf( wxT( "RS274X: Aperture Macro \"%s\": Invalid primitive id code %d, line: \"%s\"" ), + GetChars(am.name), primitive_type, CONV_FROM_UTF8(buff) ); ReportMessage( msg ); return false; } + AM_PRIMITIVE prim( m_GerbMetric ); + prim.primitive_id = (AM_PRIMITIVE_ID) primitive_type; int i; - for( i = 0; i < paramCount && *text != '*'; ++i ) + for( i = 0; i < paramCount && *text && *text != '*'; ++i ) { prim.params.push_back( DCODE_PARAM() ); DCODE_PARAM& param = prim.params.back(); - if( !CheckForLineEnd( buff, text, gerber_file ) ) + text = GetNextLine( buff, text, gerber_file ); + if( text == NULL) // End of File return false; - - if( *text == '$' ) - { - ++text; - param.SetIndex( ReadInt( text ) ); - } - else - param.SetValue( ReadDouble( text ) ); + ReadMacroParam( param, text ); } if( i < paramCount ) @@ -856,6 +935,7 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], "RS274X: read macro descr type %d: read %d parameters, insufficient parameters\n" ), prim.primitive_id, i ); ReportMessage( msg ); + } // there are more parameters to read if this is an AMP_OUTLINE if( prim.primitive_id == AMP_OUTLINE ) @@ -875,16 +955,10 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], DCODE_PARAM& param = prim.params.back(); - if( !CheckForLineEnd( buff, text, gerber_file ) ) + text = GetNextLine( buff, text, gerber_file ); + if( text == NULL ) // End of File return false; - - if( *text == '$' ) - { - ++text; - param.SetIndex( ReadInt( text ) ); - } - else - param.SetValue( ReadDouble( text ) ); + ReadMacroParam( param, text ); } } @@ -895,3 +969,37 @@ bool GERBER_IMAGE::ReadApertureMacro( char buff[GERBER_BUFZ], return true; } + +/** Function ReadMacroParam + * Read one aperture macro parameter + * a parameter can be: + * a number + * a reference to an aperture definition parameter value: $1 ot $3 ... + * a parameter definition can be complex and have operators between numbers and/or other parameter + * like $1+3 or $2x2.. + * Parameters are separated by a comma ( of finish by *) + * Return if a param is read, or false + */ +static bool ReadMacroParam( DCODE_PARAM& aParam, char*& aText ) +{ + bool found = false; + if( *aText == '$' ) // value defined later, in aperture description + { + ++aText; + aParam.SetIndex( ReadInt( aText, false ) ); + found = true; + } + else + { + aParam.SetValue( ReadDouble( aText, false ) ); + found = true; + } + + // Skip extra characters and separator + while( *aText && (*aText != ',') && (*aText != '*') ) + aText++; + if( *aText == ',' ) + aText++; + + return found; +}