KiCad PCB EDA Suite
excellon_read_drill_file.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 1992-2016 Jean-Pierre Charras <jp.charras at wanadoo.fr>
5  * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 /*
26  * Here is a sample of drill files created by Pcbnew, in decimal format:
27  * (Note: coordinates formats are same as Gerber, and T commands are near Gerber D commands).
28  * M48
29  * ;DRILL file {PCBnew (2011-03-14 BZR 2894)-testing} date 15/03/2011 14:23:22
30  * ;FORMAT={-:-/ absolute / inch / decimal}
31  * FMAT,2
32  * INCH,TZ
33  * T1C0.02
34  * T2C0.032
35  * %
36  * G90
37  * G05
38  * M72
39  * T1
40  * X1.580Y-1.360
41  * X1.580Y-4.860
42  * X8.680Y-1.360
43  * X8.680Y-4.860
44  * T2
45  * X2.930Y-3.560
46  * X5.280Y-2.535
47  * X5.405Y-2.610
48  * X5.620Y-2.900
49  * T0
50  * M30
51  */
52  /*
53  * Note there are some variant of tool definition:
54  * T1F00S00C0.2 or T1C0.02F00S00 ... Feed Rate and Spindle Speed of Tool 1
55  * Feed Rate and Spindle Speed are just skipped because they are not used in a viewer
56  */
57 
65 #include <fctsys.h>
66 #include <common.h>
67 #include <confirm.h>
68 
69 #include <gerbview.h>
70 #include <gerbview_frame.h>
71 #include <gerber_file_image.h>
72 #include <gerber_file_image_list.h>
73 #include <excellon_image.h>
74 #include <kicad_string.h>
75 #include <X2_gerber_attributes.h>
76 #include <view/view.h>
77 
78 #include <cmath>
79 
80 #include <html_messagebox.h>
81 
82 // Default format for dimensions: they are the default values, not the actual values
83 // number of digits in mantissa:
84 static const int fmtMantissaMM = 3;
85 static const int fmtMantissaInch = 4;
86 // number of digits, integer part:
87 static const int fmtIntegerMM = 3;
88 static const int fmtIntegerInch = 2;
89 
90 // A helper function to calculate the arc center of an arc
91 // known by 2 end points, the radius, and the angle direction (CW or CCW)
92 // Arc angles are <= 180 degrees in circular interpol.
93 static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int& aRadius, bool aRotCCW )
94 {
95  wxPoint center;
96  VECTOR2D end;
97  end.x = double(aEnd.x - aStart.x);
98  end.y = double(aEnd.y - aStart.y);
99 
100  // Be sure aRadius/2 > dist between aStart and aEnd
101  double min_radius = end.EuclideanNorm() * 2;
102 
103  if( min_radius <= aRadius )
104  {
105  // Adjust the radius and the arc center for a 180 deg arc between end points
106  aRadius = KiROUND( min_radius );
107  center.x = ( aStart.x + aEnd.x + 1 ) / 2;
108  center.y = ( aStart.y + aEnd.y + 1 ) / 2;
109  return center;
110  }
111 
112  /* to compute the centers position easily:
113  * rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0).
114  * the X center position is end.x/2
115  * the Y center positions are on the vertical line starting at end.x/2, 0
116  * and solve aRadius^2 = X^2 + Y^2 (2 values)
117  */
118  double seg_angle = end.Angle(); //in radian
119  VECTOR2D h_segm = end.Rotate( - seg_angle );
120  double cX = h_segm.x/2;
121  double cY1 = sqrt( (double)aRadius*aRadius - cX*cX );
122  double cY2 = -cY1;
123  VECTOR2D center1( cX, cY1 );
124  center1 = center1.Rotate( seg_angle );
125  double arc_angle1 = (end - center1).Angle() - (VECTOR2D(0.0,0.0) - center1).Angle();
126  VECTOR2D center2( cX, cY2 );
127  center2 = center2.Rotate( seg_angle );
128  double arc_angle2 = (end - center2).Angle() - (VECTOR2D(0.0,0.0) - center2).Angle();
129 
130  if( !aRotCCW )
131  {
132  if( arc_angle1 < 0.0 )
133  arc_angle1 += 2*M_PI;
134 
135  if( arc_angle2 < 0.0 )
136  arc_angle2 += 2*M_PI;
137  }
138  else
139  {
140  if( arc_angle1 > 0.0 )
141  arc_angle1 -= 2*M_PI;
142 
143  if( arc_angle2 > 0.0 )
144  arc_angle2 -= 2*M_PI;
145  }
146 
147  // Arc angle must be <= 180.0 degrees.
148  // So choose the center that create a arc angle <= 180.0
149  if( std::abs( arc_angle1 ) <= M_PI )
150  {
151  center.x = KiROUND( center1.x );
152  center.y = KiROUND( center1.y );
153  }
154  else
155  {
156  center.x = KiROUND( center2.x );
157  center.y = KiROUND( center2.y );
158  }
159 
160  return center+aStart;
161 }
162 
163 extern int ReadInt( char*& text, bool aSkipSeparator = true );
164 extern double ReadDouble( char*& text, bool aSkipSeparator = true );
165 
166 // See rs274d.cpp:
167 extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
168  APERTURE_T aAperture,
169  int Dcode_index,
170  const wxPoint& aPos,
171  wxSize aSize,
172  bool aLayerNegative );
173 
174 extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
175  int Dcode_index,
176  const wxPoint& aStart,
177  const wxPoint& aEnd,
178  wxSize aPenSize,
179  bool aLayerNegative );
180 
181 extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index,
182  const wxPoint& aStart, const wxPoint& aEnd,
183  const wxPoint& aRelCenter, wxSize aPenSize,
184  bool aClockwise, bool aMultiquadrant,
185  bool aLayerNegative );
186 
187 // Gerber X2 files have a file attribute which specify the type of image
188 // (copper, solder paste ... and sides tpo, bottom or inner copper layers)
189 // Excellon drill files do not have attributes, so, just to identify the image
190 // In gerbview, we add this attribute, similat to a Gerber drill file
191 static const char file_attribute[] = ".FileFunction,Other,Drill*";
192 
194 {
195  { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind
196  { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind
197  { "M15", DRILL_M_TOOL_DOWN, 0 }, // tool down (starting a routed hole)
198  { "M16", DRILL_M_TOOL_UP, 0 }, // tool up (ending a routed hole)
199  { "M17", DRILL_M_TOOL_UP, 0 }, // tool up similar to M16 for a viewer
200  { "M30", DRILL_M_ENDREWIND, -1 }, // End of Program Rewind
201  { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message
202  { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line)
203  { "M48", DRILL_M_HEADER, 0 }, // beginning of a header
204  { "M95", DRILL_M_ENDHEADER, 0 }, // End of the header
205  { "METRIC", DRILL_METRIC_HEADER, 1 },
206  { "INCH", DRILL_IMPERIAL_HEADER, 1 },
207  { "M71", DRILL_M_METRIC, 1 },
208  { "M72", DRILL_M_IMPERIAL, 1 },
209  { "M25", DRILL_M_BEGINPATTERN, 0 }, // Beginning of Pattern
210  { "M01", DRILL_M_ENDPATTERN, 0 }, // End of Pattern
211  { "M97", DRILL_M_CANNEDTEXT, -1 },
212  { "M98", DRILL_M_CANNEDTEXT, -1 },
213  { "DETECT", DRILL_DETECT_BROKEN, -1 },
214  { "ICI", DRILL_INCREMENTALHEADER, 1 },
215  { "FMAT", DRILL_FMT, 1 }, // Use Format command
216  { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 },
217  { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop
218  { "AFS", DRILL_AUTOMATIC_SPEED }, // Automatic Feeds and Speeds
219  { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version
220  { "R", DRILL_RESET_CMD, -1 }, // Reset commands
221  { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header
222  { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header
223  // Keep this item after all commands starting by 'T':
224  { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information
225  { "", DRILL_M_UNKNOWN, 0 } // last item in list
226 };
227 
229 {
230  { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode
231  { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode
232  { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode
233  { "G00", DRILL_G_ROUT, 1 }, // Route Mode
234  { "G05", DRILL_G_DRILL, 0 }, // Drill Mode
235  { "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes)
236  { "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode
237  { "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode
238  { "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode
239  { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordintes origin)
240  { "", DRILL_G_UNKNOWN, 0 }, // last item in list
241 };
242 
243 
244 bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
245 {
246  wxString msg;
247  int layerId = GetActiveLayer(); // current layer used in GerbView
249  auto gerber_layer = images->GetGbrImage( layerId );
250  auto drill_layer = dynamic_cast<EXCELLON_IMAGE*>( gerber_layer );
251 
252  if( gerber_layer && !drill_layer )
253  {
254  // The active layer contains old gerber data we have to clear
255  Erase_Current_DrawLayer( false );
256  }
257 
258  if( drill_layer == nullptr )
259  {
260  drill_layer = new EXCELLON_IMAGE( layerId );
261  layerId = images->AddGbrImage( drill_layer, layerId );
262  }
263 
264  if( layerId < 0 )
265  {
266  DisplayError( this, _( "No room to load file" ) );
267  return false;
268  }
269 
270  // Read the Excellon drill file:
271  bool success = drill_layer->LoadFile( aFullFileName );
272 
273  if( !success )
274  {
275  msg.Printf( _( "File %s not found" ), GetChars( aFullFileName ) );
276  DisplayError( this, msg );
277  return false;
278  }
279 
280  // Display errors list
281  if( drill_layer->GetMessages().size() > 0 )
282  {
283  HTML_MESSAGE_BOX dlg( this, _( "Error reading EXCELLON drill file" ) );
284  dlg.ListSet( drill_layer->GetMessages() );
285  dlg.ShowModal();
286  }
287 
288  if( success )
289  {
290  EDA_DRAW_PANEL_GAL* canvas = GetGalCanvas();
291 
292  if( canvas )
293  {
294  KIGFX::VIEW* view = canvas->GetView();
295 
296  for( GERBER_DRAW_ITEM* item = drill_layer->GetItemsList(); item; item = item->Next() )
297  {
298  view->Add( (KIGFX::VIEW_ITEM*) item );
299  }
300  }
301  }
302 
303  return success;
304 }
305 
306 /*
307  * Read a EXCELLON file.
308  * Gerber classes are used because there is likeness between Gerber files
309  * and Excellon files
310  * DCode can easily store T code (tool size) as round (or oval) shape
311  * Drill commands are similar to flashed gerber items
312  * Routing commands are similar to Gerber polygons
313  * coordinates have the same format as Gerber, can be given in:
314  * decimal format (i.i. floating notation format)
315  * integer 2.4 format in imperial units,
316  * integer 3.2 or 3.3 format (metric units).
317  */
318 
319 bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName )
320 {
321  // Set the default parmeter values:
322  ResetDefaultValues();
323  ClearMessageList();
324 
325  m_Current_File = wxFopen( aFullFileName, "rt" );
326 
327  if( m_Current_File == NULL )
328  return false;
329 
330  wxString msg;
331  m_FileName = aFullFileName;
332 
333  LOCALE_IO toggleIo;
334 
335  // FILE_LINE_READER will close the file.
336  FILE_LINE_READER excellonReader( m_Current_File, m_FileName );
337 
338  while( true )
339  {
340  if( excellonReader.ReadLine() == 0 )
341  break;
342 
343  char* line = excellonReader.Line();
344  char* text = StrPurge( line );
345 
346  if( *text == ';' || *text == 0 ) // comment: skip line or empty malformed line
347  continue;
348 
349  if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE )
350  {
351  Execute_HEADER_And_M_Command( text );
352  }
353  else
354  {
355  switch( *text )
356  {
357  case 'M':
358  Execute_HEADER_And_M_Command( text );
359  break;
360 
361  case 'G': // Line type Gxx : command
362  Execute_EXCELLON_G_Command( text );
363  break;
364 
365  case 'X':
366  case 'Y': // command like X12550Y19250
367  Execute_Drill_Command(text);
368  break;
369 
370  case 'I':
371  case 'J': /* Auxiliary Move command */
372  m_IJPos = ReadIJCoord( text );
373  if( *text == '*' ) // command like X35142Y15945J504
374  {
375  Execute_Drill_Command( text);
376  }
377  break;
378 
379  case 'T': // Tool command
380  Select_Tool( text );
381  break;
382 
383  case '%':
384  break;
385 
386  default:
387  msg.Printf( "Unexpected symbol 0x%2.2X &lt;%c&gt;", *text, *text );
388  AddMessageToList( msg );
389  break;
390  } // End switch
391  }
392  }
393 
394  // Add our file attribute, to identify the drill file
396  char* text = (char*)file_attribute;
397  int dummyline = 0;
398  dummy.ParseAttribCmd( NULL, NULL, 0, text, dummyline );
399  delete m_FileFunction;
400  m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy );
401 
402  m_InUse = true;
403 
404  return true;
405 }
406 
407 
409 {
410  EXCELLON_CMD* cmd = NULL;
411  wxString msg;
412 
413  // Search command in list
414  for( unsigned ii = 0; ; ii++ )
415  {
416  EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
417  int len = candidate->m_Name.size();
418 
419  if( len == 0 ) // End of list reached
420  break;
421 
422  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
423  {
424  cmd = candidate;
425  text += len;
426  break;
427  }
428  }
429 
430  if( !cmd )
431  {
432  msg.Printf( _( "Unknown Excellon command &lt;%s&gt;" ), text );
433  AddMessageToList( msg );
434  while( *text )
435  text++;
436 
437  return false;
438  }
439 
440  // Execute command
441  // some do nothing
442  switch( cmd->m_Code )
443  {
444  case DRILL_SKIP:
445  case DRILL_M_UNKNOWN:
446  break;
447 
448  case DRILL_M_END:
449  break;
450 
451  case DRILL_M_ENDREWIND:
452  break;
453 
454  case DRILL_M_MESSAGE:
455  break;
456 
457  case DRILL_M_LONGMESSAGE:
458  break;
459 
460  case DRILL_M_HEADER:
461  m_State = READ_HEADER_STATE;
462  break;
463 
464  case DRILL_M_ENDHEADER:
465  m_State = READ_PROGRAM_STATE;
466  break;
467 
468  case DRILL_REWIND_STOP: // End of header. No action in a viewer
469  m_State = READ_PROGRAM_STATE;
470  break;
471 
472  case DRILL_M_METRIC:
473  SelectUnits( true );
474  break;
475 
476  case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
477  case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
478  SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false );
479 
480  if( *text != ',' )
481  {
482  // No TZ or LZ specified. Should be a decimal format
483  // but this is not always the case. Use default TZ setting as default
484  m_NoTrailingZeros = false;
485  break;
486  }
487 
488  text++; // skip separator
489  if( *text == 'T' )
490  m_NoTrailingZeros = false;
491  else
492  m_NoTrailingZeros = true;
493  break;
494 
496  break;
497 
498  case DRILL_M_ENDPATTERN:
499  break;
500 
501  case DRILL_M_CANNEDTEXT:
502  break;
503 
504  case DRILL_M_TIPCHECK:
505  break;
506 
507  case DRILL_DETECT_BROKEN:
508  break;
509 
511  if( *text != ',' )
512  {
513  AddMessageToList( "ICI command has no parameter" );
514  break;
515  }
516  text++; // skip separator
517  // Parameter should be ON or OFF
518  if( strncasecmp( text, "OFF", 3 ) == 0 )
519  m_Relative = false;
520  else if( strncasecmp( text, "ON", 2 ) == 0 )
521  m_Relative = true;
522  else
523  AddMessageToList( "ICI command has incorrect parameter" );
524  break;
525 
527  break;
528 
530  break;
531 
532  case DRILL_AXIS_VERSION:
533  break;
534 
535  case DRILL_RESET_CMD:
536  break;
537 
539  break;
540 
541  case DRILL_FMT:
542  break;
543 
545  readToolInformation( text );
546  break;
547 
548  case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
549  // Only the last position is usefull:
550  if( m_RoutePositions.size() > 1 )
551  m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
552 
553  break;
554 
555  case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
556  {
557  D_CODE* tool = GetDCODE( m_Current_Tool );
558 
559  if( !tool )
560  {
561  AddMessageToList( wxString::Format( "Unknown tool code %d", m_Current_Tool ) );
562  break;
563  }
564 
565  for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
566  {
567  GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
568 
569  if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
570  {
571  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
572  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
573  tool->m_Size, false );
574  }
575  else // circular (cw or ccw) routing
576  {
577  bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
578  int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
579  wxPoint center;
580 
581  if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
582  center = wxPoint( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
583  else
584  center = computeCenter( m_RoutePositions[ii-1].GetPos(),
585  m_RoutePositions[ii].GetPos(), radius, rot_ccw );
586 
587  fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
588  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
589  center - m_RoutePositions[ii-1].GetPos(),
590  tool->m_Size, not rot_ccw , true,
591  false );
592  }
593 
594  m_Drawings.Append( gbritem );
595 
596  StepAndRepeatItem( *gbritem );
597  }
598 
599  m_RoutePositions.clear();
600  }
601  break;
602  }
603 
604  while( *text )
605  text++;
606 
607  return true;
608 }
609 
610 
612 {
613  // Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
614  // and enter the TCODE param in list (using the D_CODE param management, which
615  // is similar to TCODE params.
616  if( *aText == 'T' ) // This is the beginning of the definition
617  aText++;
618 
619  // Read tool number:
620  int iprm = ReadInt( aText, false );
621 
622  // Skip Feed rate and Spindle speed, if any here
623  while( *aText && ( *aText == 'F' || *aText == 'S' ) )
624  {
625  aText++;
626  ReadInt( aText, false );
627  }
628 
629  // Read tool shape
630  if( ! *aText )
631  AddMessageToList( wxString:: Format(
632  _( "Tool definition shape not found" ) ) );
633  else if( *aText != 'C' )
634  AddMessageToList( wxString:: Format(
635  _( "Tool definition '%c' not supported" ), *aText ) );
636  if( *aText )
637  aText++;
638 
639  //read tool diameter:
640  double dprm = ReadDouble( aText, false );
641  m_Has_DCode = true;
642 
643  // Initialize Dcode to handle this Tool
644  // Remember: dcodes are >= FIRST_DCODE
645  D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
646 
647  if( dcode == NULL )
648  return false;
649 
650  // conv_scale = scaling factor from inch to Internal Unit
651  double conv_scale = IU_PER_MILS * 1000;
652 
653  if( m_GerbMetric )
654  conv_scale /= 25.4;
655 
656  dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
657  dcode->m_Shape = APT_CIRCLE;
658  dcode->m_Defined = true;
659 
660  return true;
661 }
662 
663 
665 {
666  D_CODE* tool;
667  GERBER_DRAW_ITEM * gbritem;
668 
669  while( true )
670  {
671  switch( *text )
672  {
673  case 'X':
674  case 'Y':
675  ReadXYCoord( text, true );
676 
677  if( *text == 'I' || *text == 'J' )
678  ReadIJCoord( text );
679 
680  break;
681 
682  case 'G': // G85 is found here for oval holes
683  m_PreviousPos = m_CurrentPos;
684  Execute_EXCELLON_G_Command( text );
685  break;
686 
687  case 0: // E.O.L: execute command
688  if( m_RouteModeOn )
689  {
690  // We are in routing mode, and this is an intermediate point.
691  // So just store it
692  int rmode = 0; // linear routing.
693 
694  if( m_Iterpolation == GERB_INTERPOL_ARC_NEG )
695  rmode = ROUTE_CW;
696  else if( m_Iterpolation == GERB_INTERPOL_ARC_POS )
697  rmode = ROUTE_CCW;
698 
699  if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
700  {
701  EXCELLON_ROUTE_COORD point( m_CurrentPos, m_IJPos, rmode );
702  m_RoutePositions.push_back( point );
703  }
704  else
705  {
706  EXCELLON_ROUTE_COORD point( m_CurrentPos, m_ArcRadius, rmode );
707  m_RoutePositions.push_back( point );
708  }
709  return true;
710  }
711 
712  tool = GetDCODE( m_Current_Tool );
713  if( !tool )
714  {
715  wxString msg;
716  msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
717  AddMessageToList( msg );
718  return false;
719  }
720 
721  gbritem = new GERBER_DRAW_ITEM( this );
722  m_Drawings.Append( gbritem );
723 
724  if( m_SlotOn ) // Oblong hole
725  {
726  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
727  m_PreviousPos, m_CurrentPos,
728  tool->m_Size, false );
729  // the hole is made: reset the slot on command (G85)
730  // (it is needed for each oblong hole)
731  m_SlotOn = false;
732  }
733  else
734  {
735  fillFlashedGBRITEM( gbritem, tool->m_Shape, tool->m_Num_Dcode,
736  m_CurrentPos, tool->m_Size, false );
737  }
738 
739  StepAndRepeatItem( *gbritem );
740  m_PreviousPos = m_CurrentPos;
741  return true;
742  break;
743 
744  default:
745  text++;
746  break;
747  }
748  }
749 
750  return true;
751 }
752 
753 
754 bool EXCELLON_IMAGE::Select_Tool( char*& text )
755 {
756  // Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
757  // Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
758  // in tool selection command, if the tool is not defined in list,
759  // and the definition is embedded, it will be entered in list
760  char * startline = text; // the tool id starts here.
761  int tool_id = TCodeNumber( text );
762 
763  // T0 is legal, but is not a selection tool. it is a special command
764  if( tool_id >= 0 )
765  {
766  int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
767 
768  if( dcode_id > (TOOLS_MAX_COUNT - 1) )
769  dcode_id = TOOLS_MAX_COUNT - 1;
770 
771  m_Current_Tool = dcode_id;
772  D_CODE* currDcode = GetDCODEOrCreate( dcode_id, true );
773 
774  if( currDcode == NULL && tool_id > 0 ) // if the definition is embedded, enter it
775  {
776  text = startline; // text starts at the beginning of the command
777  readToolInformation( text );
778  currDcode = GetDCODE( dcode_id );
779  }
780 
781  if( currDcode )
782  currDcode->m_InUse = true;
783  }
784 
785  while( *text )
786  text++;
787 
788  return tool_id >= 0;
789 }
790 
791 
793 {
794  EXCELLON_CMD* cmd = NULL;
795  bool success = false;
796  int id = DRILL_G_UNKNOWN;
797 
798  // Search command in list
799  EXCELLON_CMD* candidate;
800  char * gcmd = text; // gcmd points the G command, for error messages.
801 
802  for( unsigned ii = 0; ; ii++ )
803  {
804  candidate = &excellon_G_CmdList[ii];
805  int len = candidate->m_Name.size();
806  if( len == 0 ) // End of list reached
807  break;
808  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
809  {
810  cmd = candidate;
811  text += len;
812  success = true;
813  id = cmd->m_Code;
814  break;
815  }
816  }
817 
818  switch( id )
819  {
820  case DRILL_G_ZERO_SET:
821  ReadXYCoord( text, true );
822  m_Offset = m_CurrentPos;
823  break;
824 
825  case DRILL_G_ROUT:
826  m_SlotOn = false;
827  m_RouteModeOn = true;
828  m_RoutePositions.clear();
829  m_LastArcDataType = ARC_INFO_TYPE_NONE;
830  ReadXYCoord( text, true );
831  // This is the first point (starting point) of routing
832  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos ) );
833  break;
834 
835  case DRILL_G_DRILL:
836  m_SlotOn = false;
837  m_RouteModeOn = false;
838  m_RoutePositions.clear();
839  m_LastArcDataType = ARC_INFO_TYPE_NONE;
840  break;
841 
842  case DRILL_G_SLOT:
843  m_SlotOn = true;
844  break;
845 
846  case DRILL_G_LINEARMOVE:
847  m_LastArcDataType = ARC_INFO_TYPE_NONE;
848  m_Iterpolation = GERB_INTERPOL_LINEAR_1X;
849  ReadXYCoord( text, true );
850  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos ) );
851  break;
852 
853  case DRILL_G_CWMOVE:
854  m_Iterpolation = GERB_INTERPOL_ARC_NEG;
855  ReadXYCoord( text, true );
856 
857  if( *text == 'I' || *text == 'J' )
858  ReadIJCoord( text );
859 
860  if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
861  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_IJPos, ROUTE_CW ) );
862  else
863  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_ArcRadius, ROUTE_CW ) );
864  break;
865 
866  case DRILL_G_CCWMOVE:
867  m_Iterpolation = GERB_INTERPOL_ARC_POS;
868  ReadXYCoord( text, true );
869 
870  if( *text == 'I' || *text == 'J' )
871  ReadIJCoord( text );
872 
873  if( m_LastArcDataType == ARC_INFO_TYPE_CENTER )
874  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_IJPos, ROUTE_CCW ) );
875  else
876  m_RoutePositions.push_back( EXCELLON_ROUTE_COORD( m_CurrentPos, m_ArcRadius, ROUTE_CCW ) );
877  break;
878 
879  case DRILL_G_ABSOLUTE:
880  m_Relative = false; // false = absolute coord
881  break;
882 
883  case DRILL_G_INCREMENTAL:
884  m_Relative = true; // true = relative coord
885  break;
886 
887  case DRILL_G_UNKNOWN:
888  default:
889  AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: &lt;%s&gt;" ), FROM_UTF8(gcmd) ) );
890  while( *text )
891  text++;
892  return false;
893  }
894 
895  return success;
896 }
897 
898 void EXCELLON_IMAGE::SelectUnits( bool aMetric )
899 {
900  /* Coordinates are measured either in inch or metric (millimeters).
901  * Inch coordinates are in six digits (00.0000) with increments
902  * as small as 0.0001 (1/10,000).
903  * Metric coordinates can be measured in microns (thousandths of a millimeter)
904  * in one of the following three ways:
905  * Five digit 10 micron resolution (000.00)
906  * Six digit 10 micron resolution (0000.00)
907  * Six digit micron resolution (000.000)
908  *
909  * Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
910  * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
911  */
912  if( aMetric )
913  {
914  m_GerbMetric = true;
915  // number of digits in mantissa
916  m_FmtScale.x = m_FmtScale.y = fmtMantissaMM;
917  // number of digits (mantissa+interger)
918  m_FmtLen.x = m_FmtLen.y = fmtIntegerMM+fmtMantissaMM;
919  }
920  else
921  {
922  m_GerbMetric = false;
923  m_FmtScale.x = m_FmtScale.y = fmtMantissaInch;
924  m_FmtLen.x = m_FmtLen.y = fmtIntegerInch+fmtMantissaInch;
925  }
926 }
static EXCELLON_CMD excellon_G_CmdList[]
class X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF...
char * ReadLine() override
Function ReadLine reads a line of text into the buffer and increments the line number counter...
Definition: richio.cpp:194
class X2_ATTRIBUTE The attribute value consists of a number of substrings separated by a comma ...
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes...
Definition: macros.h:53
char * StrPurge(char *text)
Function StrPurge removes leading and training spaces, tabs and end of line chars in text return a po...
Definition: string.cpp:271
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:120
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown...
Definition: common.h:179
This file is part of the common library.
wxSize m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:94
bool m_InUse
false if the aperure (previously defined) is not used to draw something
Definition: dcode.h:102
#define ROUTE_CCW
APERTURE_T m_Shape
shape ( Line, rectangle, circle , oval .. )
Definition: dcode.h:95
static EXCELLON_CMD excellonHeaderCmdList[]
static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int &aRadius, bool aRotCCW)
static const int fmtMantissaInch
static const int fmtIntegerInch
VECTOR2< T > Rotate(double aAngle) const
Function Rotate rotates the vector by a given angle.
Definition: vector2d.h:370
static const int fmtMantissaMM
void fillFlashedGBRITEM(GERBER_DRAW_ITEM *aGbrItem, APERTURE_T aAperture, int Dcode_index, const wxPoint &aPos, wxSize aSize, bool aLayerNegative)
Function fillFlashedGBRITEM initializes a given GBRITEM so that it can draw a circle which is filled ...
Definition: rs274d.cpp:107
bool Select_Tool(char *&text)
KIGFX::VIEW * GetView() const
Function GetView() Returns a pointer to the VIEW instance used in the panel.
int AddGbrImage(GERBER_FILE_IMAGE *aGbrImage, int aIdx)
Add a GERBER_FILE_IMAGE* at index aIdx or at the first free location if aIdx < 0. ...
Class VIEW_ITEM - is an abstract base class for deriving all objects that can be added to a VIEW...
Definition: view_item.h:84
void Erase_Current_DrawLayer(bool query)
GERBER_FILE_IMAGE_LIST is a helper class to handle a list of GERBER_FILE_IMAGE files which are loaded...
int GetActiveLayer()
Function SetActiveLayer returns the active layer.
#define abs(a)
Definition: auxiliary.h:84
bool LoadFile(const wxString &aFullFileName)
Read and load a drill (EXCELLON format) file.
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:104
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:306
Class FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:180
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:292
bool Execute_HEADER_And_M_Command(char *&text)
VECTOR2< double > VECTOR2D
Definition: vector2d.h:586
GERBER_FILE_IMAGE * GetGbrImage(int aIdx)
#define FIRST_DCODE
Definition: dcode.h:71
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:139
APERTURE_T
Enum APERTURE_T is the set of all gerber aperture types allowed, according to page 16 of http://gerbv...
Definition: dcode.h:50
void ListSet(const wxString &aList)
Function ListSet Add a list of items.
int m_Num_Dcode
D code value ( >= 10 )
Definition: dcode.h:96
Subclass of DIALOG_DISPLAY_HTML_TEXT_BASE, which is generated by wxFormBuilder.
GERBER_DRAW_ITEM * Next() const
static const int fmtIntegerMM
bool Execute_Drill_Command(char *&text)
#define ROUTE_CW
GBR_LAYOUT * GetGerberLayout() const
std::string m_Name
Class HTML_MESSAGE_BOX.
static const char file_attribute[]
bool readToolInformation(char *&aText)
Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00 and enter params in TCODE list...
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
Class D_CODE holds a gerber DCODE (also called Aperture) definition.
Definition: dcode.h:82
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
GERBER_FILE_IMAGE_LIST * GetImagesList() const
Definition: gbr_layout.cpp:50
double ReadDouble(char *&text, bool aSkipSeparator=true)
Function ReadDouble reads a double from an ASCII character buffer.
#define TOOLS_MAX_COUNT
Definition: dcode.h:73
#define IU_PER_MILS
Definition: plotter.cpp:134
bool ParseAttribCmd(FILE *aFile, char *aBuffer, int aBuffSize, char *&aText, int &aLineNum)
parse a TF command terminated with a % and fill m_Prms by the parameters found.
The common library.
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:908
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Function Add() Adds a VIEW_ITEM to the view.
Definition: view.cpp:335
bool Execute_EXCELLON_G_Command(char *&text)
Class VIEW.
Definition: view.h:61
void SelectUnits(bool aMetric)
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:245
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)
Function fillArcGBRITEM initializes a given GBRITEM so that it can draw an arc G code.
Definition: rs274d.cpp:211
void fillLineGBRITEM(GERBER_DRAW_ITEM *aGbrItem, int Dcode_index, const wxPoint &aStart, const wxPoint &aEnd, wxSize aPenSize, bool aLayerNegative)
Function fillLineGBRITEM initializes a given GBRITEM so that it can draw a linear D code...
Definition: rs274d.cpp:162
bool Read_EXCELLON_File(const wxString &aFullFileName)
int ReadInt(char *&text, bool aSkipSeparator=true)
Function ReadInt reads an int from an ASCII character buffer.