diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 83ae6f96df..7ae3a702f0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,12 @@ KiCad ChangeLog 2010 Please add newer entries at the top, list the date and your name with email address. +2011-Mar-16, UPDATE Jean-Pierre Charras +================================================================================ +Gerbview: + Added: read Excellon files created by Pcbnew. + The full Excellon command set is not supported, but drill files created by Pcbnew are supported. + 2011-Feb-05, UPDATE Jean-Pierre Charras ================================================================================ CvPcb: diff --git a/gerbview/CMakeLists.txt b/gerbview/CMakeLists.txt index 686129201c..31e1f04177 100644 --- a/gerbview/CMakeLists.txt +++ b/gerbview/CMakeLists.txt @@ -34,6 +34,7 @@ set(GERBVIEW_SRCS dummy_functions.cpp draw_gerber_screen.cpp events_called_functions.cpp + excellon_read_drill_file.cpp export_to_pcbnew.cpp files.cpp gerbview.cpp diff --git a/gerbview/class_GERBER.h b/gerbview/class_GERBER.h index e7ff3f1dc9..5391cc7034 100644 --- a/gerbview/class_GERBER.h +++ b/gerbview/class_GERBER.h @@ -74,8 +74,8 @@ private: */ class GERBER_IMAGE { - GERBVIEW_FRAME* m_Parent; // the parent GERBVIEW_FRAME (used to display messages...) - D_CODE* m_Aperture_List[TOOLS_MAX_COUNT]; ///< Dcode (Aperture) List for this layer (max 999) + GERBVIEW_FRAME* m_Parent; // the parent GERBVIEW_FRAME (used to display messages...) + D_CODE* m_Aperture_List[TOOLS_MAX_COUNT]; ///< Dcode (Aperture) List for this layer (max 999) bool m_Exposure; ///< whether an aperture macro tool is flashed on or off BOARD* m_Pcb; @@ -142,7 +142,16 @@ public: ~GERBER_IMAGE(); void Clear_GERBER_IMAGE(); int ReturnUsedDcodeNumber(); - void ResetDefaultValues(); + virtual void ResetDefaultValues(); + + /** + * Function GetParent + * @return the GERBVIEW_FRAME parent of this GERBER_IMAGE + */ + GERBVIEW_FRAME* GetParent() + { + return m_Parent; + } /** * Function GetLayerParams diff --git a/gerbview/class_excellon.h b/gerbview/class_excellon.h new file mode 100644 index 0000000000..9711f7dd3c --- /dev/null +++ b/gerbview/class_excellon.h @@ -0,0 +1,288 @@ +/********************/ +/* class_excellon.h */ +/********************/ + +#ifndef CLASS_EXCELLON_H +#define CLASS_EXCELLON_H + + +enum drill_M_code_t { + DRILL_M_UNKNOWN, + DRILL_M_END, + DRILL_M_ENDREWIND, + DRILL_M_MESSAGE, + DRILL_M_LONGMESSAGE, + DRILL_M_HEADER, + DRILL_M_ENDHEADER, + DRILL_M_BEGINPATTERN, + DRILL_M_ENDPATTERN, + DRILL_M_CANNEDTEXT, + DRILL_M_TIPCHECK, + DRILL_M_METRIC, + DRILL_M_IMPERIAL, + DRILL_METRICHEADER, + DRILL_IMPERIALHEADER, + DRILL_DETECT_BROKEN, + DRILL_INCREMENTALHEADER, + DRILL_REWIND_STOP, + DRILL_TOOL_CHANGE_STOP, + DRILL_AUTOMATIC_SPEED, + DRILL_AXIS_VERSION, + DRILL_RESET_CMD, + DRILL_AUTOMATIC_TOOL_CHANGE, + DRILL_FMT, + DRILL_SKIP, + DRILL_TOOL_INFORMATION +}; + + +enum drill_G_code_t { + DRILL_G_UNKNOWN, + DRILL_G_ABSOLUTE, + DRILL_G_INCREMENTAL, + DRILL_G_ZEROSET, + DRILL_G_ROUT, + DRILL_G_DRILL, + DRILL_G_SLOT, + DRILL_G_ZERO_SET, + DRILL_G_LINEARMOVE, + DRILL_G_CWMOVE, + DRILL_G_CCWMOVE +}; + +// Helper struct to analyse Excellon commands +struct EXCELLON_CMD +{ + string m_Name; // key string + int m_Code; // internal code, used as id in functions + int m_asParams; // 0 = no param, -1 = skip params, 1 = read params +}; + + +/* EXCELLON_IMAGE handle a drill image + * It is derived from GERBER_IMAGE because there is a lot of likeness + * between EXCELLON files and GERBER files + * DCode aperture are also similat to T Codes. + * So we can reuse GERBER_IMAGE to handle EXCELLON_IMAGE with very few new functions + */ + +class EXCELLON_IMAGE : public GERBER_IMAGE +{ +private: + enum excellon_state { + READ_HEADER_STATE, // When we are in this state, we are reading header + READ_PROGRAM_STATE // When we are in this state, we are reading drill data + }; + excellon_state m_State; // state of excellon file analysis + bool m_SlotOn; // true during an oval driil definition + +public: EXCELLON_IMAGE( GERBVIEW_FRAME* aParent, int layer ) : + GERBER_IMAGE( aParent, layer ) + { + m_State = READ_HEADER_STATE; + m_SlotOn = false; + } + + + ~EXCELLON_IMAGE() {}; + + virtual void ResetDefaultValues() + { + GERBER_IMAGE::ResetDefaultValues(); + SelectUnits( false ); + } + + + bool Read_EXCELLON_File( FILE* aFile, const wxString& aFullFileName ); + +private: + bool Execute_HEADER_Command( char*& text ); + bool Select_Tool( char*& text ); + bool Execute_EXCELLON_G_Command( char*& text ); + bool Execute_Drill_Command( char*& text ); + + int ReturnTCodeNumber( char*& Text ) + { + return ReturnDCodeNumber( Text ); + } + + + void SelectUnits( bool aMetric ); +}; + + +/* + * EXCELLON commands are given here. + * Pcbnew uses only few excellon commands + */ + +/* + * see http://www.excellon.com/manuals/program.htm + */ + +/* coordintes units: + * Coordinates are measured either in inch or metric (millimeters). + * Inch coordinates are in six digits (00.0000) with increments as small as 0.0001 (1/10,000). + * Metric coordinates can be measured in microns (thousandths of a millimeter) + * in one of the following three ways: + * Five digit 10 micron resolution (000.00) + * Six digit 10 micron resolution (0000.00) + * Six digit micron resolution (000.000) + * + * Leading and trailing zeros: + * Excellon (CNC-7) uses inches in six digits and metric in five or six digits. + * The zeros to the left of the coordinate are called leading zeros (LZ). + * The zeros to right of the coordinate are called trailing zeros (TZ). + * The CNC-7 uses leading zeros unless you specify otherwise through a part program. + * You can do so with the INCH/METRIC command. + * With leading zeros, the leading zeros must always be included. + * Trailing zeros are unneeded and may be left off. + * For trailing zeros, the reverse of the above is true. + */ + +/* + * EXCELLON Commands Used in a Header + * The following table provides you with a list of commands which + * are the most used in a part program header. + * COMMAND DESCRIPTION + * AFS Automatic Feeds and Speeds + * ATC Automatic Tool Change + * BLKD Delete all Blocks starting with a slash (/) + * CCW Clockwise or Counter-clockwise Routing + * CP Cutter Compensation + * DETECT Broken Tool Detection + * DN Down Limit Set + * DTMDIST Maximum Rout Distance Before Toolchange + * EXDA Extended Drill Area + * FMAT Format 1 or 2 + * FSB Turns the Feed/Speed Buttons off + * HPCK Home Pulse Check + * ICI Incremental Input of Part Program Coordinates + * INCH Measure Everything in Inches + * METRIC Measure Everything in Metric + * M48 Beginning of Part Program Header + * M95 End of Header + * NCSL NC Slope Enable/Disable + * OM48 Override Part Program Header + * OSTOP Optional Stop Switch + * OTCLMP Override Table Clamp + * PCKPARAM Set up pecking tool,depth,infeed and retract parameters + * PF Floating Pressure Foot Switch + * PPR Programmable Plunge Rate Enable + * PVS Pre-vacuum Shut-off Switch + * R,C Reset Clocks + * R,CP Reset Program Clocks + * R,CR Reset Run Clocks + * R,D Reset All Cutter Distances + * R,H Reset All Hit Counters + * R,T Reset Tool Data + * SBK Single Block Mode Switch + * SG Spindle Group Mode + * SIXM Input From External Source + * T Tool Information + * TCST Tool Change Stop + * UP Upper Limit Set + * VER Selection of X and Y Axis Version + * Z Zero Set + * ZA Auxiliary Zero + * ZC Zero Correction + * ZS Zero Preset + * Z+# or Z-# Set Depth Offset + * % Rewind Stop + * #/#/# Link Tool for Automatic Tool Change + * / Clear Tool Linking + */ + +/* + * Beyond The Header: The Part Program Body + * COMMAND DESCRIPTION + * A# Arc Radius + * B# Retract Rate + * C# Tool Diameter + * F# Table Feed Rate;Z Axis Infeed Rate + * G00X#Y# Route Mode + * G01 Linear (Straight Line) Mode + * G02 Circular CW Mode + * G03 Circular CCW Mode + * G04 X# Variable Dwell + * G05 Drill Mode + * G07 Override current tool feed or speed + * G32X#Y#A# Routed Circle Canned Cycle + * CW G33X#Y#A# Routed Circle Canned Cycle + * CCW G34,#(,#) Select Vision Tool + * G35(X#Y#) Single Point Vision Offset (Relative to Work Zero) + * G36(X#Y#) Multipoint Vision Translation (Relative to Work Zero) + * G37 Cancel Vision Translation or Offset (From G35 or G36) + * G38(X#Y#) Vision Corrected Single Hole Drilling (Relative to Work Zero) + * G39(X#Y#) Vision System Autocalibration + * G40 Cutter Compensation Off + * G41 Cutter Compensation Left + * G42 Cutter Compensation Right + * G45(X#Y#) Single Point Vision Offset (Relative to G35 or G36) + * G46(X#Y#) Multipoint Vision Translation (Relative to G35 or G36) + * G47 Cancel Vision Translation or Offset (From G45 or G46) + * G48(X#Y#) Vision Corrected Single Hole Drilling (Relative to G35 or G36) + * G82(G81) Dual In Line Package + * G83 Eight Pin L Pack + * G84 Circle + * G85 Slot + * G87 Routed Step Slot Canned Cycle + * G90 Absolute Mode + * G91 Incremental Input Mode + * G93X#Y# Zero Set + * H# Maximum hit count + * I#J# Arc Center Offset + * M00(X#Y#) End of Program - No Rewind + * M01 End of Pattern + * M02X#Y# Repeat Pattern Offset + * M06(X#Y#) Optional Stop + * M08 End of Step and Repeat + * M09(X#Y#) Stop for Inspection + * M14 Z Axis Route Position With Depth Controlled Contouring + * M15 Z Axis Route Position + * M16 Retract With Clamping + * M17 Retract Without Clamping + * M18 Command tool tip check + * M25 Beginning of Pattern + * M30(X#Y#) End of Program Rewind + * M45,long message\ Long Operator message on multiple\ part program lines + * M47,text Operator Message + * M50,# Vision Step and Repeat Pattern Start + * M51,# Vision Step and Repeat Rewind + * M52(#) Vision Step and Repeat Offset Counter Control + * M02XYM70 Swap Axes + * M60 Reference Scaling enable + * M61 Reference Scaling disable + * M62 Turn on peck drilling + * M63 Turn off peck drilling + * M71 Metric Measuring Mode + * M72 Inch Measuring Mode + * M02XYM80 Mirror Image X Axis + * M02XYM90 Mirror Image Y Axis + * M97,text Canned Text + * M98,text Canned Text + * M99,subprogram User Defined Stored Pattern + * P#X#(Y#) Repeat Stored Pattern + * R#M02X#Y# Repeat Pattern (S&R) + * R#(X#Y#) Repeat Hole + * S# Spindle RPM + * T# Tool Selection; Cutter Index + * Z+# or Z-# Depth Offset + * % Beginning of Pattern (see M25 command) + * / Block Delete + */ + +/* + * Example of a Header + * COMMAND PURPOSE + * M48 The beginning of a header + * INCH,LZ Use the inch measuring system with leading zeros + * VER,1 Use Version 1 X and Y axis layout + * FMAT,2 Use Format 2 commands + * 1/2/3 Link tools 1, 2, and 3 + * T1C.04F200S65 Set Tool 1 for 0.040" with infeed rate of 200 inch/min Speed of 65,000 RPM + * DETECT,ON Detect broken tools + * M95 End of the header + */ + +#endif // CLASS_EXCELLON_H diff --git a/gerbview/excellon_read_drill_file.cpp b/gerbview/excellon_read_drill_file.cpp new file mode 100644 index 0000000000..d1769bf3a9 --- /dev/null +++ b/gerbview/excellon_read_drill_file.cpp @@ -0,0 +1,595 @@ +/********************************/ +/* excellon_read_drill_file.cpp */ +/********************************/ + +/* + * Functions to read drill files (EXCELLON format) created by PcbNew + * These files use only a subset of EXCELLON commands. + * Here is a sample, in decimal format: + * (Note: coordinates formats are same as Gerber, and T commands are near Gerber D commands). + * M48 + * ;DRILL file {PCBnew (2011-03-14 BZR 2894)-testing} date 15/03/2011 14:23:22 + * ;FORMAT={-:-/ absolute / inch / decimal} + * FMAT,2 + * INCH,TZ + * T1C0.02 + * T2C0.032 + * % + * G90 + * G05 + * M72 + * T1 + * X1.580Y-1.360 + * X1.580Y-4.860 + * X8.680Y-1.360 + * X8.680Y-4.860 + * T2 + * X2.930Y-3.560 + * X5.280Y-2.535 + * X5.405Y-2.610 + * X5.620Y-2.900 + * T0 + * M30 + */ +#include "fctsys.h" +#include "common.h" +#include "confirm.h" + +#include "gerbview.h" +#include "trigo.h" +#include "macros.h" +#include "class_gerber_draw_item.h" +#include "class_GERBER.h" +#include "class_excellon.h" +#include "kicad_string.h" + +#include + +#include "dialog_load_error.h" + +extern int ReadInt( char*& text, bool aSkipSeparator = true ); +extern double ReadDouble( char*& text, bool aSkipSeparator = true ); +extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, + APERTURE_T aAperture, + int Dcode_index, + int aLayer, + const wxPoint& aPos, + wxSize aSize, + bool aLayerNegative ); +void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, + int Dcode_index, + int aLayer, + const wxPoint& aStart, + const wxPoint& aEnd, + wxSize aPenSize, + bool aLayerNegative ); + +static EXCELLON_CMD excellonHeaderCmdList[] = +{ + { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind + { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind + { "M30", DRILL_M_ENDREWIND, -1 }, // End of Program Rewind + { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message + { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line) + { "M48", DRILL_M_HEADER, 0 }, // beginning of a header + { "M95", DRILL_M_ENDHEADER, 0 }, // End of the header + { "METRIC", DRILL_METRICHEADER, 1 }, + { "INCH", DRILL_IMPERIALHEADER, 1 }, + { "M71", DRILL_M_METRIC, 1 }, + { "M72", DRILL_M_IMPERIAL, 1 }, + { "M25", DRILL_M_BEGINPATTERN, 0 }, // Beginning of Pattern + { "M01", DRILL_M_ENDPATTERN, 0 }, // End of Pattern + { "M97", DRILL_M_CANNEDTEXT, -1 }, + { "M98", DRILL_M_CANNEDTEXT, -1 }, + { "DETECT", DRILL_DETECT_BROKEN, -1 }, + { "ICI", DRILL_INCREMENTALHEADER, 1 }, + { "FMAT", DRILL_FMT, 1 }, // Use Format command + { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 }, + { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop + { "AFS", DRILL_AUTOMATIC_SPEED }, // Automatic Feeds and Speeds + { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version + { "R", DRILL_RESET_CMD, -1 }, // Reset commands + { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header + { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header + // Keep this item after all commands starting by 'T': + { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information + { "", DRILL_M_UNKNOWN, 0 } // last item in list +}; + +static EXCELLON_CMD excellon_G_CmdList[] = +{ + { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode + { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode + { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode + { "G00", DRILL_G_ROUT, 1 }, // Route Mode + { "G05", DRILL_G_DRILL, 0 }, // Drill Mode + { "G85", DRILL_G_SLOT, 0 }, // Drill Mode slot (oval holes) + { "G01", DRILL_G_LINEARMOVE, 0 }, // Linear (Straight Line) Mode + { "G02", DRILL_G_CWMOVE, 0 }, // Circular CW Mode + { "G03", DRILL_G_CCWMOVE, 0 }, // Circular CCW Mode + { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordintes origin) + { "", DRILL_G_UNKNOWN, 0 }, // last item in list +}; + + +/* + * Read a EXCELLON file. + * Gerber classes are used because there is likeness between Gerber files + * and Excellon files + * DCode can easily store T code (tool size) as round (or oval) shape + * Drill commands are similar to flashed gerber items + * Routing commands are similar to Gerber polygons + * coordinates have the same format as Gerber. + */ +bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName ) +{ + wxString msg; + int layer = getActiveLayer(); // current layer used in gerbview + + if( g_GERBER_List[layer] == NULL ) + { + g_GERBER_List[layer] = new EXCELLON_IMAGE( this, layer ); + } + + EXCELLON_IMAGE* drill_Layer = (EXCELLON_IMAGE*) g_GERBER_List[layer]; + ClearMessageList(); + + /* Read the gerber file */ + FILE * file = wxFopen( aFullFileName, wxT( "rt" ) ); + if( file == NULL ) + { + msg.Printf( _( "File %s not found" ), GetChars( aFullFileName ) ); + DisplayError( this, msg, 10 ); + return false; + } + + wxString path = wxPathOnly( aFullFileName ); + if( path != wxEmptyString ) + wxSetWorkingDirectory( path ); + + bool success = drill_Layer->Read_EXCELLON_File( file, aFullFileName ); + + // Display errors list + if( m_Messages.size() > 0 ) + { + DIALOG_LOAD_ERROR dlg( this ); + dlg.ListSet( m_Messages ); + dlg.ShowModal(); + } + return success; +} + +bool EXCELLON_IMAGE::Read_EXCELLON_File( FILE * aFile, + const wxString & aFullFileName ) +{ + /* Set the gerber scale: */ + ResetDefaultValues(); + + m_FileName = aFullFileName; + m_Current_File = aFile; + + SetLocaleTo_C_standard(); + + // FILE_LINE_READER will close the file. + if( m_Current_File == NULL ) + { + wxMessageBox( wxT("NULL!"), m_FileName ); + return false; + } + + FILE_LINE_READER excellonReader( m_Current_File, m_FileName ); + while( true ) + { + if( excellonReader.ReadLine() == 0 ) + break; + + char* line = excellonReader.Line(); + char* text = StrPurge( line ); + + if( *text == ';' ) // comment: skip line + continue; + + if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE ) + { + Execute_HEADER_Command( text ); + } + else + { + switch( *text ) + { + case 'M': + Execute_HEADER_Command( text ); + break; + + case 'G': /* Line type Gxx : command */ + Execute_EXCELLON_G_Command( text ); + break; + + case 'X': + case 'Y': // command like X12550Y19250 + Execute_Drill_Command(text); + break; + + case 'I': + case 'J': /* Auxiliary Move command */ + m_IJPos = ReadIJCoord( text ); + if( *text == '*' ) // command like X35142Y15945J504* + { + Execute_Drill_Command( text); + } + break; + + case 'T': // Tool command + Select_Tool( text ); + break; + + case '%': + break; + + default: + { + wxString msg; + msg.Printf( wxT( "Unexpected symbol <%c>" ), *text ); + if( GetParent() ) + GetParent()->ReportMessage( msg ); + } + break; + } // End switch + } + } + SetLocaleTo_Default(); + return true; +} + + +bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) +{ + EXCELLON_CMD* cmd = NULL; + int iprm; + double dprm; + D_CODE* dcode; + wxString msg; + + // Search command in list + EXCELLON_CMD* candidate; + + for( unsigned ii = 0; ; ii++ ) + { + candidate = &excellonHeaderCmdList[ii]; + int len = candidate->m_Name.size(); + if( len == 0 ) // End of list reached + break; + if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. + { + cmd = candidate; + text += len; + break; + } + } + + if( !cmd ) + { + msg.Printf( wxT( "Unknown Excellon command <%s>" ), text ); + ReportMessage( msg ); + while( *text ) + text++; + + return false; + } + + // Execute command + // some do nothing + switch( cmd->m_Code ) + { + case DRILL_SKIP: + case DRILL_M_UNKNOWN: + break; + + case DRILL_M_END: + break; + + case DRILL_M_ENDREWIND: + break; + + case DRILL_M_MESSAGE: + break; + + case DRILL_M_LONGMESSAGE: + break; + + case DRILL_M_HEADER: + m_State = READ_HEADER_STATE; + break; + + case DRILL_M_ENDHEADER: + m_State = READ_PROGRAM_STATE; + break; + + case DRILL_REWIND_STOP: // TODO: what this command really is ? + m_State = READ_PROGRAM_STATE; + break; + + case DRILL_M_METRIC: + SelectUnits( true ); + break; + + case DRILL_METRICHEADER: // command like METRIC,TZ or METRIC,LZ + SelectUnits( true ); + if( *text != ',' ) + { + ReportMessage( _( "METRIC command has no parameter" ) ); + break; + } + text++; // skip separator + if( *text == 'T' ) + m_NoTrailingZeros = false; + else + m_NoTrailingZeros = true; + break; + + case DRILL_M_IMPERIAL: + SelectUnits( false ); + break; + + case DRILL_IMPERIALHEADER: // command like INCH,TZ or INCH,LZ + SelectUnits( false ); + if( *text != ',' ) + { + ReportMessage( _( "INCH command has no parameter" ) ); + break; + } + text++; // skip separator + if( *text == 'T' ) + m_NoTrailingZeros = false; + else + m_NoTrailingZeros = true; + break; + + case DRILL_M_BEGINPATTERN: + break; + + case DRILL_M_ENDPATTERN: + break; + + case DRILL_M_CANNEDTEXT: + break; + + case DRILL_M_TIPCHECK: + break; + + case DRILL_DETECT_BROKEN: + break; + + case DRILL_INCREMENTALHEADER: + m_Relative = true; + break; + + case DRILL_TOOL_CHANGE_STOP: + break; + + case DRILL_AUTOMATIC_SPEED: + break; + + case DRILL_AXIS_VERSION: + break; + + case DRILL_RESET_CMD: + break; + + case DRILL_AUTOMATIC_TOOL_CHANGE: + break; + + case DRILL_FMT: + break; + + case DRILL_TOOL_INFORMATION: + + // Read a tool definition like T1C0.02: + // Read tool number: + iprm = ReadInt( text, false ); + + // Read tool shape + if( *text != 'C' ) + ReportMessage( _( "Tool definition <%c> not supported" ) ); + if( *text ) + text++; + + //read tool diameter: + dprm = ReadDouble( text, false ); + m_Has_DCode = true; + + // Initialize Dcode to handle this Tool + dcode = GetDCODE( iprm + FIRST_DCODE ); // Remember: dcodes are >= FIRST_DCODE + if( dcode == NULL ) + break; + double conv_scale = m_GerbMetric ? PCB_INTERNAL_UNIT / 25.4 : PCB_INTERNAL_UNIT; + dcode->m_Size.x = dcode->m_Size.y = wxRound( dprm * conv_scale ); + dcode->m_Shape = APT_CIRCLE; + break; + } + + while( *text ) + text++; + + return true; +} + + +bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text ) +{ + D_CODE* tool; + GERBER_DRAW_ITEM * gbritem; + while( true ) + { + switch( *text ) + { + case 'X': + ReadXYCoord( text ); + break; + case 'Y': + ReadXYCoord( text ); + break; + case 'G': // G85 is found here for oval holes + m_PreviousPos = m_CurrentPos; + Execute_EXCELLON_G_Command( text ); + break; + case 0: // E.O.L: execute command + tool = GetDCODE( m_Current_Tool, false ); + if( !tool ) + { + wxString msg; + msg.Printf( _( "Tool <%d> not defined" ), m_Current_Tool ); + ReportMessage( msg ); + return false; + } + gbritem = new GERBER_DRAW_ITEM( GetParent()->GetBoard(), this ); + GetParent()->GetBoard()->m_Drawings.Append( gbritem ); + if( m_SlotOn ) // Oval hole + { + fillLineGBRITEM( gbritem, + tool->m_Num_Dcode, GetParent()->getActiveLayer(), + m_PreviousPos, m_CurrentPos, + tool->m_Size, false ); + } + else + { + fillFlashedGBRITEM( gbritem, tool->m_Shape, + tool->m_Num_Dcode, GetParent()->getActiveLayer(), + m_CurrentPos, + tool->m_Size, false ); + } + StepAndRepeatItem( *gbritem ); + m_PreviousPos = m_CurrentPos; + return true; + break; + + default: + text++; + break; + } + } + + return true; +} + + +bool EXCELLON_IMAGE::Select_Tool( char*& text ) +{ + int tool_id = ReturnTCodeNumber( text ); + + if( tool_id >= 0 ) + { + tool_id += FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE + if( tool_id > (TOOLS_MAX_COUNT - 1) ) + tool_id = TOOLS_MAX_COUNT - 1; + m_Current_Tool = tool_id; + D_CODE* pt_Dcode = GetDCODE( tool_id , false ); + if( pt_Dcode ) + pt_Dcode->m_InUse = true; + } + while( *text ) + text++; + + return tool_id >= 0; +} + + +bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text ) +{ + EXCELLON_CMD* cmd = NULL; + bool success = false; + int id = DRILL_G_UNKNOWN; + + // Search command in list + EXCELLON_CMD* candidate; + char * gcmd = text; // gcmd points the G command, for error messages. + + for( unsigned ii = 0; ; ii++ ) + { + candidate = &excellon_G_CmdList[ii]; + int len = candidate->m_Name.size(); + if( len == 0 ) // End of list reached + break; + if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. + { + cmd = candidate; + text += len; + success = true; + id = cmd->m_Code; + break; + } + } + + switch( id ) + { + case DRILL_G_ZERO_SET: + ReadXYCoord( text ); + m_Offset = m_CurrentPos; + break; + + case DRILL_G_ROUT: + m_SlotOn = false; + m_PolygonFillMode = true; + break; + + case DRILL_G_DRILL: + m_SlotOn = false; + m_PolygonFillMode = false; + break; + + case DRILL_G_SLOT: + m_SlotOn = true; + break; + + case DRILL_G_LINEARMOVE: + m_Iterpolation = GERB_INTERPOL_LINEAR_1X; + break; + + case DRILL_G_CWMOVE: + m_Iterpolation = GERB_INTERPOL_ARC_NEG; + break; + + case DRILL_G_CCWMOVE: + m_Iterpolation = GERB_INTERPOL_ARC_POS; + break; + + case DRILL_G_ABSOLUTE: + m_Relative = false; // false = absolute coord + break; + + case DRILL_G_INCREMENTAL: + m_Relative = true; // true = relative coord + break; + + case DRILL_G_UNKNOWN: + default: + { + wxString msg; + msg.Printf( _( "Unknown Excellon G Code: <%s>" ), GetChars(FROM_UTF8(gcmd)) ); + ReportMessage( msg ); + while( *text ) + text++; + return false; + } + } + return success; +} + +void EXCELLON_IMAGE::SelectUnits( bool aMetric ) +{ + /* Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution (00.0000) + * metric: Default fmt = 3.2 for X and Y axis: 5 digits, 1 micron resolution (00.000) + */ + if( aMetric ) + { + m_GerbMetric = true; + m_FmtScale.x = m_FmtScale.y = 3; // number of digits in mantissa: here 2 + m_FmtLen.x = m_FmtLen.y = 5; // number of digits: here 3+2 + } + else + { + m_GerbMetric = false; + m_FmtScale.x = m_FmtScale.y = 4; // number of digits in mantissa: here 4 + m_FmtLen.x = m_FmtLen.y = 6; // number of digits: here 2+4 + } +} diff --git a/gerbview/files.cpp b/gerbview/files.cpp index 20004ff8b8..92618a04d0 100644 --- a/gerbview/files.cpp +++ b/gerbview/files.cpp @@ -73,7 +73,8 @@ clear an existing layer to load any new layers." ), NB_LAYERS ); break; case ID_GERBVIEW_LOAD_DRILL_FILE: - DisplayError( this, _( "Not yet available..." ) ); + LoadExcellonFiles( wxEmptyString ); + DrawPanel->Refresh(); break; case ID_GERBVIEW_LOAD_DCODE_FILE: @@ -195,6 +196,88 @@ bool GERBVIEW_FRAME::LoadGerberFiles( const wxString& aFullFileName ) return true; } +bool GERBVIEW_FRAME::LoadExcellonFiles( const wxString& aFullFileName ) +{ + wxString filetypes; + wxArrayString filenamesList; + wxFileName filename = aFullFileName; + wxString currentPath; + + if( !filename.IsOk() ) + { + filetypes = _( "Drill files (.drl)" ); + filetypes << wxT("|"); + filetypes += wxT(";*.drl;*.DRL" ); + filetypes << wxT("|"); + /* All filetypes */ + filetypes += AllFilesWildcard; + + /* Use the current working directory if the file name path does not exist. */ + if( filename.DirExists() ) + currentPath = filename.GetPath(); + else + currentPath = wxGetCwd(); + + wxFileDialog dlg( this, + _( "Open Drill File" ), + currentPath, + filename.GetFullName(), + filetypes, + wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE | wxFD_CHANGE_DIR ); + + if( dlg.ShowModal() == wxID_CANCEL ) + return false; + + dlg.GetPaths( filenamesList ); + currentPath = wxGetCwd(); + } + else + { + wxFileName filename = aFullFileName; + filenamesList.Add( aFullFileName ); + currentPath = filename.GetPath(); + } + + // Read gerber files: each file is loaded on a new gerbview layer + int layer = getActiveLayer(); + + for( unsigned ii = 0; ii < filenamesList.GetCount(); ii++ ) + { + wxFileName filename = filenamesList[ii]; + + if( !filename.IsAbsolute() ) + filename.SetPath( currentPath ); + + GetScreen()->SetFileName( filename.GetFullPath() ); + + setActiveLayer( layer, false ); + + if( Read_EXCELLON_File( filename.GetFullPath() ) ) + { + layer = getNextAvailableLayer( layer ); + + if( layer == NO_AVAILABLE_LAYERS ) + { + wxString msg = wxT( "No more empty layers are available. The remaining gerber " ); + msg += wxT( "files will not be loaded." ); + wxMessageBox( msg ); + break; + } + + setActiveLayer( layer, false ); + } + } + + Zoom_Automatique( false ); + g_SaveTime = time( NULL ); + + // Synchronize layers tools with actual active layer: + setActiveLayer( getActiveLayer() ); + syncLayerBox(); + + return true; +} + /* * Read a DCode file (not used with RX274X files , just with RS274D old files). diff --git a/gerbview/gerbview_frame.h b/gerbview/gerbview_frame.h index 3619c0feff..a2e0232789 100644 --- a/gerbview/gerbview_frame.h +++ b/gerbview/gerbview_frame.h @@ -407,6 +407,17 @@ public: GERBVIEW_FRAME( wxWindow* father, const wxString& title, bool Read_GERBER_File( const wxString& GERBER_FullFileName, const wxString& D_Code_FullFileName ); + /** + * function LoadDrllFiles + * Load a drill (EXCELLON) file or many files. + * @param aFileName - void string or file name with full path to open or empty string to + * open a new file. In this case one one file is loaded + * if void string: user will be prompted for filename(s) + * @return true if file was opened successfully. + */ + bool LoadExcellonFiles( const wxString& aFileName ); + bool Read_EXCELLON_File( const wxString& aFullFileName ); + void GeneralControl( wxDC* aDC, const wxPoint& aPosition, int aHotKey = 0 ); /** diff --git a/gerbview/menubar.cpp b/gerbview/menubar.cpp index 4705300f0f..d0949fa929 100644 --- a/gerbview/menubar.cpp +++ b/gerbview/menubar.cpp @@ -39,10 +39,9 @@ void GERBVIEW_FRAME::ReCreateMenuBar( void ) filesMenu->Append( ID_GERBVIEW_LOAD_DCODE_FILE, _( "Load DCodes" ), _( "Load D-Codes File" ), FALSE ); -#if 0 // TODO + filesMenu->Append( ID_GERBVIEW_LOAD_DRILL_FILE, _( "Load EXCELLON Drill File" ), _( "Load excellon drill file" ), FALSE ); -#endif filesMenu->Append( ID_NEW_BOARD, _( "&Clear All" ), _( "Clear all layers. All data will be deleted" ), FALSE ); diff --git a/gerbview/rs274d.cpp b/gerbview/rs274d.cpp index d3ddf04e96..5c6c700d90 100644 --- a/gerbview/rs274d.cpp +++ b/gerbview/rs274d.cpp @@ -85,7 +85,7 @@ * @param aSize The diameter of the round flash * @param aLayerNegative = true if the current layer is negative */ -static void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, +void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, APERTURE_T aAperture, int Dcode_index, int aLayer, @@ -138,7 +138,7 @@ static void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, * @param aPenSize The size of the flash. Note rectangular shapes are legal. * @param aLayerNegative = true if the current layer is negative */ -static void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, +void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index, int aLayer, const wxPoint& aStart, @@ -725,10 +725,6 @@ bool GERBER_IMAGE::Execute_DCODE_Command( char*& text, int D_commande ) case 2: // code D2: exposure OFF (i.e. "move to") m_Exposure = false; - -// D( printf( "Move to %d,%d to %d,%d\n", -// m_PreviousPos.x, m_PreviousPos.y, -// m_CurrentPos.x, m_CurrentPos.y ); ) m_PreviousPos = m_CurrentPos; break; @@ -743,9 +739,6 @@ bool GERBER_IMAGE::Execute_DCODE_Command( char*& text, int D_commande ) gbritem = new GERBER_DRAW_ITEM( pcb, this ); pcb->m_Drawings.Append( gbritem ); - -// D( printf( "Add flashed dcode %d layer %d at %d %d\n", dcode, activeLayer, -// m_CurrentPos.x, m_CurrentPos.y ); ) fillFlashedGBRITEM( gbritem, aperture, dcode, activeLayer, m_CurrentPos, size, GetLayerParams().m_LayerNegative );