2011-03-17 19:14:45 +00:00
|
|
|
/*
|
2011-09-30 18:15:37 +00:00
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
2011-03-17 19:14:45 +00:00
|
|
|
*
|
2016-05-24 15:56:52 +00:00
|
|
|
* Copyright (C) 1992-2016 Jean-Pierre Charras <jp.charras at wanadoo.fr>
|
2021-08-01 08:01:03 +00:00
|
|
|
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
2011-03-17 19:14: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
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2011-09-30 18:15:37 +00:00
|
|
|
* Here is a sample of drill files created by Pcbnew, in decimal format:
|
2011-03-16 20:51:20 +00:00
|
|
|
* (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
|
|
|
|
*/
|
2013-09-29 18:24:38 +00:00
|
|
|
/*
|
|
|
|
* Note there are some variant of tool definition:
|
2013-10-04 08:42:09 +00:00
|
|
|
* T1F00S00C0.2 or T1C0.02F00S00 ... Feed Rate and Spindle Speed of Tool 1
|
|
|
|
* Feed Rate and Spindle Speed are just skipped because they are not used in a viewer
|
2013-09-29 18:24:38 +00:00
|
|
|
*/
|
|
|
|
|
2016-06-17 18:00:29 +00:00
|
|
|
/**
|
|
|
|
* @file excellon_read_drill_file.cpp
|
|
|
|
* Functions to read drill files (EXCELLON format) created by Pcbnew
|
|
|
|
* These files use only a subset of EXCELLON commands.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-01-07 17:12:59 +00:00
|
|
|
#include <math/util.h> // for KiROUND
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <gerbview.h>
|
2014-06-27 17:07:42 +00:00
|
|
|
#include <gerbview_frame.h>
|
2018-01-29 12:26:58 +00:00
|
|
|
#include <gerber_file_image.h>
|
|
|
|
#include <gerber_file_image_list.h>
|
|
|
|
#include <excellon_image.h>
|
2021-08-02 08:27:06 +00:00
|
|
|
#include <excellon_defaults.h>
|
2020-10-24 14:45:37 +00:00
|
|
|
#include <macros.h>
|
2021-07-29 09:56:22 +00:00
|
|
|
#include <string_utils.h>
|
2020-10-24 01:38:50 +00:00
|
|
|
#include <locale_io.h>
|
2018-01-29 12:26:58 +00:00
|
|
|
#include <X2_gerber_attributes.h>
|
2017-09-17 22:43:20 +00:00
|
|
|
#include <view/view.h>
|
2021-08-02 08:27:06 +00:00
|
|
|
#include <gerbview_settings.h>
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2012-09-21 17:02:54 +00:00
|
|
|
#include <cmath>
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2021-09-14 18:26:03 +00:00
|
|
|
#include <dialogs/html_message_box.h>
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
// A helper function to calculate the arc center of an arc
|
|
|
|
// known by 2 end points, the radius, and the angle direction (CW or CCW)
|
|
|
|
// Arc angles are <= 180 degrees in circular interpol.
|
|
|
|
static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int& aRadius, bool aRotCCW )
|
|
|
|
{
|
|
|
|
wxPoint center;
|
|
|
|
VECTOR2D end;
|
|
|
|
end.x = double(aEnd.x - aStart.x);
|
|
|
|
end.y = double(aEnd.y - aStart.y);
|
|
|
|
|
|
|
|
// Be sure aRadius/2 > dist between aStart and aEnd
|
|
|
|
double min_radius = end.EuclideanNorm() * 2;
|
|
|
|
|
|
|
|
if( min_radius <= aRadius )
|
|
|
|
{
|
|
|
|
// Adjust the radius and the arc center for a 180 deg arc between end points
|
|
|
|
aRadius = KiROUND( min_radius );
|
|
|
|
center.x = ( aStart.x + aEnd.x + 1 ) / 2;
|
|
|
|
center.y = ( aStart.y + aEnd.y + 1 ) / 2;
|
|
|
|
return center;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* to compute the centers position easily:
|
|
|
|
* rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0).
|
|
|
|
* the X center position is end.x/2
|
|
|
|
* the Y center positions are on the vertical line starting at end.x/2, 0
|
|
|
|
* and solve aRadius^2 = X^2 + Y^2 (2 values)
|
|
|
|
*/
|
|
|
|
double seg_angle = end.Angle(); //in radian
|
|
|
|
VECTOR2D h_segm = end.Rotate( - seg_angle );
|
|
|
|
double cX = h_segm.x/2;
|
|
|
|
double cY1 = sqrt( (double)aRadius*aRadius - cX*cX );
|
|
|
|
double cY2 = -cY1;
|
|
|
|
VECTOR2D center1( cX, cY1 );
|
|
|
|
center1 = center1.Rotate( seg_angle );
|
|
|
|
double arc_angle1 = (end - center1).Angle() - (VECTOR2D(0.0,0.0) - center1).Angle();
|
|
|
|
VECTOR2D center2( cX, cY2 );
|
|
|
|
center2 = center2.Rotate( seg_angle );
|
|
|
|
double arc_angle2 = (end - center2).Angle() - (VECTOR2D(0.0,0.0) - center2).Angle();
|
|
|
|
|
|
|
|
if( !aRotCCW )
|
|
|
|
{
|
|
|
|
if( arc_angle1 < 0.0 )
|
|
|
|
arc_angle1 += 2*M_PI;
|
|
|
|
|
|
|
|
if( arc_angle2 < 0.0 )
|
|
|
|
arc_angle2 += 2*M_PI;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( arc_angle1 > 0.0 )
|
|
|
|
arc_angle1 -= 2*M_PI;
|
|
|
|
|
|
|
|
if( arc_angle2 > 0.0 )
|
|
|
|
arc_angle2 -= 2*M_PI;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Arc angle must be <= 180.0 degrees.
|
|
|
|
// So choose the center that create a arc angle <= 180.0
|
|
|
|
if( std::abs( arc_angle1 ) <= M_PI )
|
|
|
|
{
|
|
|
|
center.x = KiROUND( center1.x );
|
|
|
|
center.y = KiROUND( center1.y );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
center.x = KiROUND( center2.x );
|
|
|
|
center.y = KiROUND( center2.y );
|
|
|
|
}
|
|
|
|
|
|
|
|
return center+aStart;
|
|
|
|
}
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
extern int ReadInt( char*& text, bool aSkipSeparator = true );
|
|
|
|
extern double ReadDouble( char*& text, bool aSkipSeparator = true );
|
2016-05-26 11:57:43 +00:00
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
// See rs274d.cpp:
|
2011-03-16 20:51:20 +00:00
|
|
|
extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
|
|
|
|
APERTURE_T aAperture,
|
|
|
|
int Dcode_index,
|
|
|
|
const wxPoint& aPos,
|
|
|
|
wxSize aSize,
|
|
|
|
bool aLayerNegative );
|
2018-09-22 11:04:20 +00:00
|
|
|
|
|
|
|
extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
|
2011-03-16 20:51:20 +00:00
|
|
|
int Dcode_index,
|
|
|
|
const wxPoint& aStart,
|
|
|
|
const wxPoint& aEnd,
|
|
|
|
wxSize aPenSize,
|
|
|
|
bool aLayerNegative );
|
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index,
|
|
|
|
const wxPoint& aStart, const wxPoint& aEnd,
|
|
|
|
const wxPoint& aRelCenter, wxSize aPenSize,
|
|
|
|
bool aClockwise, bool aMultiquadrant,
|
|
|
|
bool aLayerNegative );
|
|
|
|
|
2018-05-13 16:44:26 +00:00
|
|
|
// Gerber X2 files have a file attribute which specify the type of image
|
2014-11-23 11:41:57 +00:00
|
|
|
// (copper, solder paste ... and sides tpo, bottom or inner copper layers)
|
|
|
|
// Excellon drill files do not have attributes, so, just to identify the image
|
2021-06-09 19:32:58 +00:00
|
|
|
// In gerbview, we add this attribute, similar to a Gerber drill file
|
2014-11-23 11:41:57 +00:00
|
|
|
static const char file_attribute[] = ".FileFunction,Other,Drill*";
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
static EXCELLON_CMD excellonHeaderCmdList[] =
|
|
|
|
{
|
|
|
|
{ "M0", DRILL_M_END, -1 }, // End of Program - No Rewind
|
|
|
|
{ "M00", DRILL_M_END, -1 }, // End of Program - No Rewind
|
2018-09-22 11:04:20 +00:00
|
|
|
{ "M15", DRILL_M_TOOL_DOWN, 0 }, // tool down (starting a routed hole)
|
|
|
|
{ "M16", DRILL_M_TOOL_UP, 0 }, // tool up (ending a routed hole)
|
|
|
|
{ "M17", DRILL_M_TOOL_UP, 0 }, // tool up similar to M16 for a viewer
|
2018-11-26 09:55:36 +00:00
|
|
|
{ "M30", DRILL_M_ENDFILE, -1 }, // End of File (last line of NC drill)
|
2011-03-16 20:51:20 +00:00
|
|
|
{ "M47", DRILL_M_MESSAGE, -1 }, // Operator Message
|
|
|
|
{ "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line)
|
2019-12-17 16:09:32 +00:00
|
|
|
{ "M48", DRILL_M_HEADER, 0 }, // beginning of a header
|
|
|
|
{ "M95", DRILL_M_ENDHEADER, 0 }, // End of the header
|
|
|
|
{ "METRIC", DRILL_METRIC_HEADER, 1 },
|
|
|
|
{ "INCH", DRILL_IMPERIAL_HEADER, 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
|
2011-03-16 20:51:20 +00:00
|
|
|
{ "M97", DRILL_M_CANNEDTEXT, -1 },
|
|
|
|
{ "M98", DRILL_M_CANNEDTEXT, -1 },
|
|
|
|
{ "DETECT", DRILL_DETECT_BROKEN, -1 },
|
2019-12-17 16:09:32 +00:00
|
|
|
{ "ICI", DRILL_INCREMENTALHEADER, 1 },
|
|
|
|
{ "FMAT", DRILL_FMT, 1 }, // Use Format command
|
2021-07-31 18:39:41 +00:00
|
|
|
{ ";FILE_FORMAT",
|
|
|
|
DRILL_FORMAT_ALTIUM, 1 }, // Use Format command
|
|
|
|
{ ";", DRILL_HEADER_SKIP, 0 }, // Other ; hints that we don't implement
|
2019-12-17 16:09:32 +00:00
|
|
|
{ "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 },
|
|
|
|
{ "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop
|
|
|
|
{ "AFS", DRILL_AUTOMATIC_SPEED, 0 }, // Automatic Feeds and Speeds
|
|
|
|
{ "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version
|
2011-03-16 20:51:20 +00:00
|
|
|
{ "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':
|
2019-12-17 16:09:32 +00:00
|
|
|
{ "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information
|
|
|
|
{ "", DRILL_M_UNKNOWN, 0 } // last item in list
|
2011-03-16 20:51:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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
|
2018-09-22 11:04:20 +00:00
|
|
|
{ "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes)
|
|
|
|
{ "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode
|
|
|
|
{ "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode
|
|
|
|
{ "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode
|
2019-08-20 17:22:30 +00:00
|
|
|
{ "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordinates origin)
|
2011-03-16 20:51:20 +00:00
|
|
|
{ "", DRILL_G_UNKNOWN, 0 }, // last item in list
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
|
|
|
|
{
|
|
|
|
wxString msg;
|
2017-09-17 22:43:20 +00:00
|
|
|
int layerId = GetActiveLayer(); // current layer used in GerbView
|
2016-06-17 18:00:29 +00:00
|
|
|
GERBER_FILE_IMAGE_LIST* images = GetGerberLayout()->GetImagesList();
|
2021-08-01 08:01:03 +00:00
|
|
|
GERBER_FILE_IMAGE* gerber_layer = images->GetGbrImage( layerId );
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2021-08-01 08:01:03 +00:00
|
|
|
// If the active layer contains old gerber or nc drill data, remove it
|
2019-03-24 16:32:57 +00:00
|
|
|
if( gerber_layer )
|
2018-02-23 00:31:51 +00:00
|
|
|
Erase_Current_DrawLayer( false );
|
|
|
|
|
2021-05-01 18:23:01 +00:00
|
|
|
std::unique_ptr<EXCELLON_IMAGE> drill_layer_uptr = std::make_unique<EXCELLON_IMAGE>( layerId );
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2021-08-02 08:27:06 +00:00
|
|
|
EXCELLON_DEFAULTS nc_defaults;
|
|
|
|
GERBVIEW_SETTINGS* cfg = static_cast<GERBVIEW_SETTINGS*>( config() );
|
|
|
|
cfg->GetExcellonDefaults( nc_defaults );
|
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
// Read the Excellon drill file:
|
2021-08-02 08:27:06 +00:00
|
|
|
bool success = drill_layer_uptr->LoadFile( aFullFileName, &nc_defaults );
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2016-06-05 12:26:39 +00:00
|
|
|
if( !success )
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
2021-05-01 18:23:01 +00:00
|
|
|
drill_layer_uptr.reset();
|
2020-08-31 13:19:57 +00:00
|
|
|
msg.Printf( _( "File %s not found." ), aFullFileName );
|
|
|
|
ShowInfoBarError( msg );
|
2011-03-16 20:51:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-05-01 18:23:01 +00:00
|
|
|
EXCELLON_IMAGE* drill_layer = drill_layer_uptr.release();
|
|
|
|
|
2019-03-24 16:32:57 +00:00
|
|
|
layerId = images->AddGbrImage( drill_layer, layerId );
|
|
|
|
|
|
|
|
if( layerId < 0 )
|
|
|
|
{
|
|
|
|
delete drill_layer;
|
2020-08-31 13:19:57 +00:00
|
|
|
ShowInfoBarError( _( "No empty layers to load file into." ) );
|
2019-03-24 16:32:57 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
// Display errors list
|
2018-02-23 00:31:51 +00:00
|
|
|
if( drill_layer->GetMessages().size() > 0 )
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
2016-05-24 15:56:52 +00:00
|
|
|
HTML_MESSAGE_BOX dlg( this, _( "Error reading EXCELLON drill file" ) );
|
2018-02-23 00:31:51 +00:00
|
|
|
dlg.ListSet( drill_layer->GetMessages() );
|
2011-03-16 20:51:20 +00:00
|
|
|
dlg.ShowModal();
|
|
|
|
}
|
2017-09-17 22:43:20 +00:00
|
|
|
|
2019-06-13 17:28:55 +00:00
|
|
|
if( GetCanvas() )
|
2017-09-17 22:43:20 +00:00
|
|
|
{
|
2019-12-30 13:23:33 +00:00
|
|
|
for( GERBER_DRAW_ITEM* item : drill_layer->GetItems() )
|
2019-06-13 17:28:55 +00:00
|
|
|
GetCanvas()->GetView()->Add( (KIGFX::VIEW_ITEM*) item );
|
2017-09-17 22:43:20 +00:00
|
|
|
}
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2021-08-01 09:44:45 +00:00
|
|
|
|
|
|
|
void EXCELLON_IMAGE::ResetDefaultValues()
|
|
|
|
{
|
|
|
|
GERBER_FILE_IMAGE::ResetDefaultValues();
|
2021-08-02 08:27:06 +00:00
|
|
|
SelectUnits( false, nullptr ); // Default unit = inch
|
|
|
|
m_hasFormat = false; // will be true if a Altium file containing
|
|
|
|
// the nn:mm file format is read
|
2021-08-01 09:44:45 +00:00
|
|
|
|
|
|
|
// Files using non decimal can use No Trailing zeros or No leading Zeros
|
|
|
|
// Unfortunately, the identifier (INCH,TZ or INCH,LZ for instance) is not
|
|
|
|
// always set in drill files.
|
|
|
|
// The option leading zeros looks like more frequent, so use this default
|
|
|
|
m_NoTrailingZeros = true;
|
|
|
|
}
|
|
|
|
|
2016-06-05 12:26:39 +00:00
|
|
|
/*
|
|
|
|
* 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, can be given in:
|
|
|
|
* decimal format (i.i. floating notation format)
|
|
|
|
* integer 2.4 format in imperial units,
|
|
|
|
* integer 3.2 or 3.3 format (metric units).
|
|
|
|
*/
|
2021-08-02 08:27:06 +00:00
|
|
|
bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName, EXCELLON_DEFAULTS* aDefaults )
|
2016-06-05 12:26:39 +00:00
|
|
|
{
|
2021-06-09 19:32:58 +00:00
|
|
|
// Set the default parameter values:
|
2011-03-16 20:51:20 +00:00
|
|
|
ResetDefaultValues();
|
2016-06-05 12:26:39 +00:00
|
|
|
ClearMessageList();
|
|
|
|
|
2018-11-08 15:48:03 +00:00
|
|
|
m_Current_File = wxFopen( aFullFileName, "rt" );
|
2016-06-05 12:26:39 +00:00
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
if( m_Current_File == nullptr )
|
2016-06-05 12:26:39 +00:00
|
|
|
return false;
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2021-08-02 08:27:06 +00:00
|
|
|
// Initial format setting, usualy defined in file, but not always...
|
|
|
|
m_NoTrailingZeros = aDefaults->m_LeadingZero;
|
|
|
|
m_GerbMetric = aDefaults->m_UnitsMM;
|
|
|
|
|
2018-11-08 15:48:03 +00:00
|
|
|
wxString msg;
|
2011-03-16 20:51:20 +00:00
|
|
|
m_FileName = aFullFileName;
|
|
|
|
|
2014-11-23 11:41:57 +00:00
|
|
|
LOCALE_IO toggleIo;
|
2011-03-16 20:51:20 +00:00
|
|
|
|
|
|
|
// FILE_LINE_READER will close the file.
|
|
|
|
FILE_LINE_READER excellonReader( m_Current_File, m_FileName );
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
while( true )
|
|
|
|
{
|
2021-04-22 21:20:34 +00:00
|
|
|
if( excellonReader.ReadLine() == nullptr )
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
char* line = excellonReader.Line();
|
|
|
|
char* text = StrPurge( line );
|
|
|
|
|
2021-07-31 18:39:41 +00:00
|
|
|
if( *text == 0 ) // Skip empty lines
|
2011-03-16 20:51:20 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE )
|
|
|
|
{
|
2018-09-22 11:04:20 +00:00
|
|
|
Execute_HEADER_And_M_Command( text );
|
2021-08-02 08:27:06 +00:00
|
|
|
|
|
|
|
// Now units (inch/mm) are known, set the coordinate format
|
|
|
|
SelectUnits( m_GerbMetric, aDefaults );
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch( *text )
|
|
|
|
{
|
2021-07-31 18:39:41 +00:00
|
|
|
case ';':
|
2011-03-16 20:51:20 +00:00
|
|
|
case 'M':
|
2018-09-22 11:04:20 +00:00
|
|
|
Execute_HEADER_And_M_Command( text );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
2018-11-08 15:48:03 +00:00
|
|
|
case 'G': // Line type Gxx : command
|
2011-03-16 20:51:20 +00:00
|
|
|
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 );
|
2011-03-17 19:14:45 +00:00
|
|
|
if( *text == '*' ) // command like X35142Y15945J504
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
|
|
|
Execute_Drill_Command( text);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2021-08-01 08:01:03 +00:00
|
|
|
case 'T': // Select Tool command (can also create
|
|
|
|
// the tool with an embedded definition)
|
2011-03-16 20:51:20 +00:00
|
|
|
Select_Tool( text );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '%':
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2018-11-08 15:48:03 +00:00
|
|
|
msg.Printf( "Unexpected symbol 0x%2.2X <%c>", *text, *text );
|
2016-06-05 12:26:39 +00:00
|
|
|
AddMessageToList( msg );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
} // End switch
|
|
|
|
}
|
|
|
|
}
|
2014-11-23 11:41:57 +00:00
|
|
|
|
|
|
|
// Add our file attribute, to identify the drill file
|
|
|
|
X2_ATTRIBUTE dummy;
|
|
|
|
char* text = (char*)file_attribute;
|
2017-09-28 22:38:36 +00:00
|
|
|
int dummyline = 0;
|
2021-04-22 21:20:34 +00:00
|
|
|
dummy.ParseAttribCmd( nullptr, nullptr, 0, text, dummyline );
|
2014-11-23 11:41:57 +00:00
|
|
|
delete m_FileFunction;
|
|
|
|
m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy );
|
|
|
|
|
|
|
|
m_InUse = true;
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
bool EXCELLON_IMAGE::Execute_HEADER_And_M_Command( char*& text )
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
2021-04-22 21:20:34 +00:00
|
|
|
EXCELLON_CMD* cmd = nullptr;
|
2011-03-16 20:51:20 +00:00
|
|
|
wxString msg;
|
|
|
|
|
|
|
|
// Search command in list
|
|
|
|
for( unsigned ii = 0; ; ii++ )
|
|
|
|
{
|
2016-05-24 15:56:52 +00:00
|
|
|
EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
|
2011-03-16 20:51:20 +00:00
|
|
|
int len = candidate->m_Name.size();
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
if( len == 0 ) // End of list reached
|
|
|
|
break;
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
|
|
|
|
{
|
|
|
|
cmd = candidate;
|
|
|
|
text += len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !cmd )
|
|
|
|
{
|
2018-10-11 06:11:10 +00:00
|
|
|
msg.Printf( _( "Unknown Excellon command <%s>" ), text );
|
2016-06-05 12:26:39 +00:00
|
|
|
AddMessageToList( msg );
|
2011-03-16 20:51:20 +00:00
|
|
|
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:
|
2018-11-26 09:55:36 +00:00
|
|
|
case DRILL_M_ENDFILE:
|
|
|
|
// if a route command is in progress, finish it
|
|
|
|
if( m_RouteModeOn )
|
|
|
|
FinishRouteCommand();
|
2011-03-16 20:51:20 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-10-04 08:42:09 +00:00
|
|
|
case DRILL_REWIND_STOP: // End of header. No action in a viewer
|
2011-03-16 20:51:20 +00:00
|
|
|
m_State = READ_PROGRAM_STATE;
|
|
|
|
break;
|
|
|
|
|
2021-07-31 18:39:41 +00:00
|
|
|
case DRILL_FORMAT_ALTIUM:
|
|
|
|
readFileFormat( text );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_HEADER_SKIP:
|
|
|
|
break;
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
case DRILL_M_METRIC:
|
2021-08-02 08:27:06 +00:00
|
|
|
SelectUnits( true, nullptr );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
2018-03-31 16:40:29 +00:00
|
|
|
case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
|
|
|
|
case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
|
2021-08-02 08:27:06 +00:00
|
|
|
SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false, nullptr );
|
2011-03-16 20:51:20 +00:00
|
|
|
|
|
|
|
if( *text != ',' )
|
|
|
|
{
|
2018-11-08 15:48:03 +00:00
|
|
|
// No TZ or LZ specified. Should be a decimal format
|
2021-08-01 09:44:45 +00:00
|
|
|
// but this is not always the case. Use our default setting
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-03-31 16:40:29 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
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:
|
2012-05-10 11:02:44 +00:00
|
|
|
if( *text != ',' )
|
|
|
|
{
|
2018-11-08 15:48:03 +00:00
|
|
|
AddMessageToList( "ICI command has no parameter" );
|
2012-05-10 11:02:44 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
text++; // skip separator
|
|
|
|
// Parameter should be ON or OFF
|
2016-08-16 08:27:09 +00:00
|
|
|
if( strncasecmp( text, "OFF", 3 ) == 0 )
|
2012-05-10 11:02:44 +00:00
|
|
|
m_Relative = false;
|
2016-08-16 08:27:09 +00:00
|
|
|
else if( strncasecmp( text, "ON", 2 ) == 0 )
|
2012-05-10 11:02:44 +00:00
|
|
|
m_Relative = true;
|
|
|
|
else
|
2018-11-08 15:48:03 +00:00
|
|
|
AddMessageToList( "ICI command has incorrect parameter" );
|
2011-03-16 20:51:20 +00:00
|
|
|
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:
|
2016-05-24 15:56:52 +00:00
|
|
|
readToolInformation( text );
|
|
|
|
break;
|
2018-09-22 11:04:20 +00:00
|
|
|
|
|
|
|
case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
|
2021-06-09 19:32:58 +00:00
|
|
|
// Only the last position is useful:
|
2018-09-22 11:04:20 +00:00
|
|
|
if( m_RoutePositions.size() > 1 )
|
|
|
|
m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
|
2018-11-26 09:55:36 +00:00
|
|
|
FinishRouteCommand();
|
2018-09-22 11:04:20 +00:00
|
|
|
break;
|
2016-05-24 15:56:52 +00:00
|
|
|
}
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
while( *text )
|
|
|
|
text++;
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-09-29 18:24:38 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2021-07-31 18:39:41 +00:00
|
|
|
void EXCELLON_IMAGE::readFileFormat( char*& aText )
|
|
|
|
{
|
|
|
|
int mantissaDigits = 0;
|
|
|
|
int characteristicDigits = 0;
|
|
|
|
|
|
|
|
// Example String: ;FILE_FORMAT=4:4
|
|
|
|
// The ;FILE_FORMAT potion will already be stripped off.
|
|
|
|
// Parse the rest strictly as single_digit:single_digit like 4:4 or 2:4
|
|
|
|
// Don't allow anything clever like spaces or multiple digits
|
|
|
|
if( *aText != '=' )
|
|
|
|
return;
|
|
|
|
|
|
|
|
aText++;
|
|
|
|
|
|
|
|
if( !isdigit( *aText ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
characteristicDigits = *aText - '0';
|
|
|
|
aText++;
|
|
|
|
|
|
|
|
if( *aText != ':' )
|
|
|
|
return;
|
|
|
|
|
|
|
|
aText++;
|
|
|
|
|
|
|
|
if( !isdigit( *aText ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
mantissaDigits = *aText - '0';
|
|
|
|
|
|
|
|
m_hasFormat = true;
|
|
|
|
m_FmtLen.x = m_FmtLen.y = characteristicDigits + mantissaDigits;
|
|
|
|
m_FmtScale.x = m_FmtScale.y = mantissaDigits;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
bool EXCELLON_IMAGE::readToolInformation( char*& aText )
|
|
|
|
{
|
|
|
|
// Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
|
|
|
|
// and enter the TCODE param in list (using the D_CODE param management, which
|
|
|
|
// is similar to TCODE params.
|
|
|
|
if( *aText == 'T' ) // This is the beginning of the definition
|
|
|
|
aText++;
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
// Read tool number:
|
|
|
|
int iprm = ReadInt( aText, false );
|
2012-04-18 07:07:13 +00:00
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
// Skip Feed rate and Spindle speed, if any here
|
|
|
|
while( *aText && ( *aText == 'F' || *aText == 'S' ) )
|
|
|
|
{
|
|
|
|
aText++;
|
|
|
|
ReadInt( aText, false );
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
// Read tool shape
|
|
|
|
if( ! *aText )
|
2016-06-05 12:26:39 +00:00
|
|
|
AddMessageToList( wxString:: Format(
|
2016-05-24 15:56:52 +00:00
|
|
|
_( "Tool definition shape not found" ) ) );
|
|
|
|
else if( *aText != 'C' )
|
2016-06-05 12:26:39 +00:00
|
|
|
AddMessageToList( wxString:: Format(
|
2016-05-24 15:56:52 +00:00
|
|
|
_( "Tool definition '%c' not supported" ), *aText ) );
|
|
|
|
if( *aText )
|
|
|
|
aText++;
|
|
|
|
|
|
|
|
//read tool diameter:
|
|
|
|
double dprm = ReadDouble( aText, false );
|
|
|
|
m_Has_DCode = true;
|
|
|
|
|
|
|
|
// Initialize Dcode to handle this Tool
|
|
|
|
// Remember: dcodes are >= FIRST_DCODE
|
2017-09-17 22:43:20 +00:00
|
|
|
D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
if( dcode == nullptr )
|
2016-05-24 15:56:52 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// conv_scale = scaling factor from inch to Internal Unit
|
|
|
|
double conv_scale = IU_PER_MILS * 1000;
|
|
|
|
|
|
|
|
if( m_GerbMetric )
|
|
|
|
conv_scale /= 25.4;
|
|
|
|
|
|
|
|
dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
|
|
|
|
dcode->m_Shape = APT_CIRCLE;
|
|
|
|
dcode->m_Defined = true;
|
2011-03-16 20:51:20 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-17 12:28:51 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text )
|
|
|
|
{
|
|
|
|
D_CODE* tool;
|
|
|
|
GERBER_DRAW_ITEM * gbritem;
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
switch( *text )
|
|
|
|
{
|
|
|
|
case 'X':
|
|
|
|
case 'Y':
|
2018-07-17 12:28:51 +00:00
|
|
|
ReadXYCoord( text, true );
|
2018-09-22 11:04:20 +00:00
|
|
|
|
|
|
|
if( *text == 'I' || *text == 'J' )
|
|
|
|
ReadIJCoord( text );
|
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
2018-07-17 12:28:51 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
case 'G': // G85 is found here for oval holes
|
|
|
|
m_PreviousPos = m_CurrentPos;
|
|
|
|
Execute_EXCELLON_G_Command( text );
|
|
|
|
break;
|
2018-07-17 12:28:51 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
case 0: // E.O.L: execute command
|
2018-09-22 11:04:20 +00:00
|
|
|
if( m_RouteModeOn )
|
|
|
|
{
|
|
|
|
// We are in routing mode, and this is an intermediate point.
|
|
|
|
// So just store it
|
|
|
|
int rmode = 0; // linear routing.
|
|
|
|
|
|
|
|
if( m_Iterpolation == GERB_INTERPOL_ARC_NEG )
|
|
|
|
rmode = ROUTE_CW;
|
|
|
|
else if( m_Iterpolation == GERB_INTERPOL_ARC_POS )
|
|
|
|
rmode = ROUTE_CCW;
|
|
|
|
|
|
|
|
if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
|
|
|
|
{
|
|
|
|
EXCELLON_ROUTE_COORD point( m_CurrentPos, m_IJPos, rmode );
|
|
|
|
m_RoutePositions.push_back( point );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EXCELLON_ROUTE_COORD point( m_CurrentPos, m_ArcRadius, rmode );
|
|
|
|
m_RoutePositions.push_back( point );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
tool = GetDCODE( m_Current_Tool );
|
2011-03-16 20:51:20 +00:00
|
|
|
if( !tool )
|
|
|
|
{
|
|
|
|
wxString msg;
|
2016-05-24 15:56:52 +00:00
|
|
|
msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
|
2016-06-05 12:26:39 +00:00
|
|
|
AddMessageToList( msg );
|
2011-03-16 20:51:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2016-05-26 11:57:43 +00:00
|
|
|
gbritem = new GERBER_DRAW_ITEM( this );
|
2019-12-30 13:23:33 +00:00
|
|
|
AddItemToList( gbritem );
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2016-06-19 09:53:40 +00:00
|
|
|
if( m_SlotOn ) // Oblong hole
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
2016-05-26 11:57:43 +00:00
|
|
|
fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
|
2011-03-16 20:51:20 +00:00
|
|
|
m_PreviousPos, m_CurrentPos,
|
|
|
|
tool->m_Size, false );
|
2016-06-19 09:53:40 +00:00
|
|
|
// the hole is made: reset the slot on command (G85)
|
|
|
|
// (it is needed for each oblong hole)
|
|
|
|
m_SlotOn = false;
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-05-26 11:57:43 +00:00
|
|
|
fillFlashedGBRITEM( gbritem, tool->m_Shape, tool->m_Num_Dcode,
|
|
|
|
m_CurrentPos, tool->m_Size, false );
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
StepAndRepeatItem( *gbritem );
|
|
|
|
m_PreviousPos = m_CurrentPos;
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
text++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool EXCELLON_IMAGE::Select_Tool( char*& text )
|
|
|
|
{
|
2016-05-24 15:56:52 +00:00
|
|
|
// Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
|
|
|
|
// Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
|
|
|
|
// in tool selection command, if the tool is not defined in list,
|
|
|
|
// and the definition is embedded, it will be entered in list
|
|
|
|
char * startline = text; // the tool id starts here.
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
int tool_id = TCodeNumber( text );
|
2011-03-16 20:51:20 +00:00
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
// T0 is legal, but is not a selection tool. it is a special command
|
2011-03-16 20:51:20 +00:00
|
|
|
if( tool_id >= 0 )
|
|
|
|
{
|
2016-05-24 15:56:52 +00:00
|
|
|
int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
|
|
|
|
|
|
|
|
if( dcode_id > (TOOLS_MAX_COUNT - 1) )
|
|
|
|
dcode_id = TOOLS_MAX_COUNT - 1;
|
|
|
|
|
|
|
|
m_Current_Tool = dcode_id;
|
2021-08-01 08:01:03 +00:00
|
|
|
D_CODE* currDcode = GetDCODE( dcode_id );
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2021-08-01 08:01:03 +00:00
|
|
|
// if the tool does not exist, and the definition is embedded, create this tool
|
|
|
|
if( currDcode == nullptr && tool_id > 0 )
|
2016-05-24 15:56:52 +00:00
|
|
|
{
|
|
|
|
text = startline; // text starts at the beginning of the command
|
|
|
|
readToolInformation( text );
|
2017-09-17 22:43:20 +00:00
|
|
|
currDcode = GetDCODE( dcode_id );
|
2016-05-24 15:56:52 +00:00
|
|
|
}
|
|
|
|
|
2021-08-01 08:01:03 +00:00
|
|
|
// If the Tool is still not existing, create a dummy tool
|
|
|
|
if( !currDcode )
|
|
|
|
currDcode = GetDCODEOrCreate( dcode_id, true );
|
|
|
|
|
2016-05-24 15:56:52 +00:00
|
|
|
if( currDcode )
|
|
|
|
currDcode->m_InUse = true;
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
2016-05-24 15:56:52 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
while( *text )
|
|
|
|
text++;
|
|
|
|
|
|
|
|
return tool_id >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text )
|
|
|
|
{
|
2021-04-22 21:20:34 +00:00
|
|
|
EXCELLON_CMD* cmd = nullptr;
|
2011-03-16 20:51:20 +00:00
|
|
|
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:
|
2018-07-17 12:28:51 +00:00
|
|
|
ReadXYCoord( text, true );
|
2011-03-16 20:51:20 +00:00
|
|
|
m_Offset = m_CurrentPos;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_ROUT:
|
|
|
|
m_SlotOn = false;
|
2018-11-26 09:55:36 +00:00
|
|
|
|
|
|
|
if( m_RouteModeOn )
|
|
|
|
FinishRouteCommand();
|
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
m_RouteModeOn = true;
|
|
|
|
m_RoutePositions.clear();
|
|
|
|
m_LastArcDataType = ARC_INFO_TYPE_NONE;
|
|
|
|
ReadXYCoord( text, true );
|
|
|
|
// This is the first point (starting point) of routing
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_DRILL:
|
|
|
|
m_SlotOn = false;
|
2018-11-26 09:55:36 +00:00
|
|
|
|
|
|
|
if( m_RouteModeOn )
|
|
|
|
FinishRouteCommand();
|
|
|
|
|
2018-09-22 11:04:20 +00:00
|
|
|
m_RouteModeOn = false;
|
|
|
|
m_RoutePositions.clear();
|
|
|
|
m_LastArcDataType = ARC_INFO_TYPE_NONE;
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_SLOT:
|
|
|
|
m_SlotOn = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_LINEARMOVE:
|
2018-09-22 11:04:20 +00:00
|
|
|
m_LastArcDataType = ARC_INFO_TYPE_NONE;
|
2011-03-16 20:51:20 +00:00
|
|
|
m_Iterpolation = GERB_INTERPOL_LINEAR_1X;
|
2018-09-22 11:04:20 +00:00
|
|
|
ReadXYCoord( text, true );
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_CWMOVE:
|
|
|
|
m_Iterpolation = GERB_INTERPOL_ARC_NEG;
|
2018-09-22 11:04:20 +00:00
|
|
|
ReadXYCoord( text, true );
|
|
|
|
|
|
|
|
if( *text == 'I' || *text == 'J' )
|
|
|
|
ReadIJCoord( text );
|
|
|
|
|
|
|
|
if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos, m_IJPos, ROUTE_CW );
|
2018-09-22 11:04:20 +00:00
|
|
|
else
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos, m_ArcRadius, ROUTE_CW );
|
2011-03-16 20:51:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DRILL_G_CCWMOVE:
|
|
|
|
m_Iterpolation = GERB_INTERPOL_ARC_POS;
|
2018-09-22 11:04:20 +00:00
|
|
|
ReadXYCoord( text, true );
|
|
|
|
|
|
|
|
if( *text == 'I' || *text == 'J' )
|
|
|
|
ReadIJCoord( text );
|
|
|
|
|
|
|
|
if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos, m_IJPos, ROUTE_CCW );
|
2018-09-22 11:04:20 +00:00
|
|
|
else
|
2019-12-05 15:41:21 +00:00
|
|
|
m_RoutePositions.emplace_back( m_CurrentPos, m_ArcRadius, ROUTE_CCW );
|
2011-03-16 20:51:20 +00:00
|
|
|
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:
|
2018-09-22 11:04:20 +00:00
|
|
|
AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: <%s>" ), FROM_UTF8(gcmd) ) );
|
2011-03-16 20:51:20 +00:00
|
|
|
while( *text )
|
|
|
|
text++;
|
|
|
|
return false;
|
|
|
|
}
|
2018-09-22 11:04:20 +00:00
|
|
|
|
2011-03-16 20:51:20 +00:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
2021-08-02 08:27:06 +00:00
|
|
|
void EXCELLON_IMAGE::SelectUnits( bool aMetric, EXCELLON_DEFAULTS* aDefaults )
|
2011-03-16 20:51:20 +00:00
|
|
|
{
|
2013-09-29 18:24:38 +00:00
|
|
|
/* 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)
|
2016-05-24 15:56:52 +00:00
|
|
|
*
|
|
|
|
* Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
|
2013-09-29 18:24:38 +00:00
|
|
|
* metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
|
2021-08-02 08:27:06 +00:00
|
|
|
*
|
|
|
|
* However some drill files do not use standard values.
|
2011-03-16 20:51:20 +00:00
|
|
|
*/
|
|
|
|
if( aMetric )
|
|
|
|
{
|
|
|
|
m_GerbMetric = true;
|
2021-07-31 18:39:41 +00:00
|
|
|
|
|
|
|
if( !m_hasFormat )
|
|
|
|
{
|
2021-08-02 08:27:06 +00:00
|
|
|
if( aDefaults )
|
|
|
|
{
|
|
|
|
// number of digits in mantissa
|
|
|
|
m_FmtScale.x = m_FmtScale.y = aDefaults->m_MmMantissaLen;
|
|
|
|
// number of digits (mantissa+integer)
|
|
|
|
m_FmtLen.x = m_FmtLen.y = aDefaults->m_MmIntegerLen
|
|
|
|
+ aDefaults->m_MmMantissaLen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_FmtScale.x = m_FmtScale.y = FMT_MANTISSA_MM;
|
|
|
|
m_FmtLen.x = m_FmtLen.y = FMT_INTEGER_MM + FMT_MANTISSA_MM;
|
|
|
|
}
|
2021-07-31 18:39:41 +00:00
|
|
|
}
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_GerbMetric = false;
|
2021-07-31 18:39:41 +00:00
|
|
|
|
|
|
|
if( !m_hasFormat )
|
|
|
|
{
|
2021-08-02 08:27:06 +00:00
|
|
|
if( aDefaults )
|
|
|
|
{
|
|
|
|
m_FmtScale.x = m_FmtScale.y = aDefaults->m_InchMantissaLen;
|
|
|
|
m_FmtLen.x = m_FmtLen.y = aDefaults->m_InchIntegerLen
|
|
|
|
+ aDefaults->m_InchMantissaLen;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_FmtScale.x = m_FmtScale.y = FMT_MANTISSA_INCH;
|
|
|
|
m_FmtLen.x = m_FmtLen.y = FMT_INTEGER_INCH + FMT_MANTISSA_INCH;
|
|
|
|
}
|
2021-07-31 18:39:41 +00:00
|
|
|
}
|
2011-03-16 20:51:20 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-26 09:55:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
void EXCELLON_IMAGE::FinishRouteCommand()
|
|
|
|
{
|
|
|
|
// Ends a route command started by M15 ot G01, G02 or G03 command
|
|
|
|
// if a route command is not in progress, do nothing
|
|
|
|
|
|
|
|
if( !m_RouteModeOn )
|
|
|
|
return;
|
|
|
|
|
|
|
|
D_CODE* tool = GetDCODE( m_Current_Tool );
|
|
|
|
|
|
|
|
if( !tool )
|
|
|
|
{
|
|
|
|
AddMessageToList( wxString::Format( "Unknown tool code %d", m_Current_Tool ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
|
|
|
|
{
|
|
|
|
GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
|
|
|
|
|
|
|
|
if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
|
|
|
|
{
|
|
|
|
fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
|
|
|
|
m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
|
|
|
|
tool->m_Size, false );
|
|
|
|
}
|
|
|
|
else // circular (cw or ccw) routing
|
|
|
|
{
|
|
|
|
bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
|
|
|
|
int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
|
|
|
|
wxPoint center;
|
|
|
|
|
|
|
|
if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
|
|
|
|
center = wxPoint( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
|
|
|
|
else
|
|
|
|
center = computeCenter( m_RoutePositions[ii-1].GetPos(),
|
|
|
|
m_RoutePositions[ii].GetPos(), radius, rot_ccw );
|
|
|
|
|
|
|
|
fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
|
|
|
|
m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
|
|
|
|
center - m_RoutePositions[ii-1].GetPos(),
|
|
|
|
tool->m_Size, not rot_ccw , true,
|
|
|
|
false );
|
|
|
|
}
|
|
|
|
|
2019-12-30 13:23:33 +00:00
|
|
|
AddItemToList( gbritem );
|
2018-11-26 09:55:36 +00:00
|
|
|
|
|
|
|
StepAndRepeatItem( *gbritem );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_RoutePositions.clear();
|
|
|
|
m_RouteModeOn = false;
|
|
|
|
}
|