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 #include <math/util.h> // for KiROUND
69 
70 #include <gerbview.h>
71 #include <gerbview_frame.h>
72 #include <gerber_file_image.h>
73 #include <gerber_file_image_list.h>
74 #include <excellon_image.h>
75 #include <kicad_string.h>
76 #include <X2_gerber_attributes.h>
77 #include <view/view.h>
78 
79 #include <cmath>
80 
81 #include <html_messagebox.h>
82 
83 // Default format for dimensions: they are the default values, not the actual values
84 // number of digits in mantissa:
85 static const int fmtMantissaMM = 3;
86 static const int fmtMantissaInch = 4;
87 // number of digits, integer part:
88 static const int fmtIntegerMM = 3;
89 static const int fmtIntegerInch = 2;
90 
91 // A helper function to calculate the arc center of an arc
92 // known by 2 end points, the radius, and the angle direction (CW or CCW)
93 // Arc angles are <= 180 degrees in circular interpol.
94 static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int& aRadius, bool aRotCCW )
95 {
96  wxPoint center;
97  VECTOR2D end;
98  end.x = double(aEnd.x - aStart.x);
99  end.y = double(aEnd.y - aStart.y);
100 
101  // Be sure aRadius/2 > dist between aStart and aEnd
102  double min_radius = end.EuclideanNorm() * 2;
103 
104  if( min_radius <= aRadius )
105  {
106  // Adjust the radius and the arc center for a 180 deg arc between end points
107  aRadius = KiROUND( min_radius );
108  center.x = ( aStart.x + aEnd.x + 1 ) / 2;
109  center.y = ( aStart.y + aEnd.y + 1 ) / 2;
110  return center;
111  }
112 
113  /* to compute the centers position easily:
114  * rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0).
115  * the X center position is end.x/2
116  * the Y center positions are on the vertical line starting at end.x/2, 0
117  * and solve aRadius^2 = X^2 + Y^2 (2 values)
118  */
119  double seg_angle = end.Angle(); //in radian
120  VECTOR2D h_segm = end.Rotate( - seg_angle );
121  double cX = h_segm.x/2;
122  double cY1 = sqrt( (double)aRadius*aRadius - cX*cX );
123  double cY2 = -cY1;
124  VECTOR2D center1( cX, cY1 );
125  center1 = center1.Rotate( seg_angle );
126  double arc_angle1 = (end - center1).Angle() - (VECTOR2D(0.0,0.0) - center1).Angle();
127  VECTOR2D center2( cX, cY2 );
128  center2 = center2.Rotate( seg_angle );
129  double arc_angle2 = (end - center2).Angle() - (VECTOR2D(0.0,0.0) - center2).Angle();
130 
131  if( !aRotCCW )
132  {
133  if( arc_angle1 < 0.0 )
134  arc_angle1 += 2*M_PI;
135 
136  if( arc_angle2 < 0.0 )
137  arc_angle2 += 2*M_PI;
138  }
139  else
140  {
141  if( arc_angle1 > 0.0 )
142  arc_angle1 -= 2*M_PI;
143 
144  if( arc_angle2 > 0.0 )
145  arc_angle2 -= 2*M_PI;
146  }
147 
148  // Arc angle must be <= 180.0 degrees.
149  // So choose the center that create a arc angle <= 180.0
150  if( std::abs( arc_angle1 ) <= M_PI )
151  {
152  center.x = KiROUND( center1.x );
153  center.y = KiROUND( center1.y );
154  }
155  else
156  {
157  center.x = KiROUND( center2.x );
158  center.y = KiROUND( center2.y );
159  }
160 
161  return center+aStart;
162 }
163 
164 extern int ReadInt( char*& text, bool aSkipSeparator = true );
165 extern double ReadDouble( char*& text, bool aSkipSeparator = true );
166 
167 // See rs274d.cpp:
168 extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
169  APERTURE_T aAperture,
170  int Dcode_index,
171  const wxPoint& aPos,
172  wxSize aSize,
173  bool aLayerNegative );
174 
175 extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
176  int Dcode_index,
177  const wxPoint& aStart,
178  const wxPoint& aEnd,
179  wxSize aPenSize,
180  bool aLayerNegative );
181 
182 extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index,
183  const wxPoint& aStart, const wxPoint& aEnd,
184  const wxPoint& aRelCenter, wxSize aPenSize,
185  bool aClockwise, bool aMultiquadrant,
186  bool aLayerNegative );
187 
188 // Gerber X2 files have a file attribute which specify the type of image
189 // (copper, solder paste ... and sides tpo, bottom or inner copper layers)
190 // Excellon drill files do not have attributes, so, just to identify the image
191 // In gerbview, we add this attribute, similat to a Gerber drill file
192 static const char file_attribute[] = ".FileFunction,Other,Drill*";
193 
195 {
196  { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind
197  { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind
198  { "M15", DRILL_M_TOOL_DOWN, 0 }, // tool down (starting a routed hole)
199  { "M16", DRILL_M_TOOL_UP, 0 }, // tool up (ending a routed hole)
200  { "M17", DRILL_M_TOOL_UP, 0 }, // tool up similar to M16 for a viewer
201  { "M30", DRILL_M_ENDFILE, -1 }, // End of File (last line of NC drill)
202  { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message
203  { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line)
204  { "M48", DRILL_M_HEADER, 0 }, // beginning of a header
205  { "M95", DRILL_M_ENDHEADER, 0 }, // End of the header
206  { "METRIC", DRILL_METRIC_HEADER, 1 },
207  { "INCH", DRILL_IMPERIAL_HEADER, 1 },
208  { "M71", DRILL_M_METRIC, 1 },
209  { "M72", DRILL_M_IMPERIAL, 1 },
210  { "M25", DRILL_M_BEGINPATTERN, 0 }, // Beginning of Pattern
211  { "M01", DRILL_M_ENDPATTERN, 0 }, // End of Pattern
212  { "M97", DRILL_M_CANNEDTEXT, -1 },
213  { "M98", DRILL_M_CANNEDTEXT, -1 },
214  { "DETECT", DRILL_DETECT_BROKEN, -1 },
215  { "ICI", DRILL_INCREMENTALHEADER, 1 },
216  { "FMAT", DRILL_FMT, 1 }, // Use Format command
217  { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 },
218  { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop
219  { "AFS", DRILL_AUTOMATIC_SPEED, 0 }, // Automatic Feeds and Speeds
220  { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version
221  { "R", DRILL_RESET_CMD, -1 }, // Reset commands
222  { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header
223  { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header
224  // Keep this item after all commands starting by 'T':
225  { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information
226  { "", DRILL_M_UNKNOWN, 0 } // last item in list
227 };
228 
230 {
231  { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode
232  { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode
233  { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode
234  { "G00", DRILL_G_ROUT, 1 }, // Route Mode
235  { "G05", DRILL_G_DRILL, 0 }, // Drill Mode
236  { "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes)
237  { "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode
238  { "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode
239  { "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode
240  { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordinates origin)
241  { "", DRILL_G_UNKNOWN, 0 }, // last item in list
242 };
243 
244 
245 bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
246 {
247  wxString msg;
248  int layerId = GetActiveLayer(); // current layer used in GerbView
250  auto gerber_layer = images->GetGbrImage( layerId );
251 
252  // OIf the active layer contains old gerber or nc drill data, remove it
253  if( gerber_layer )
254  Erase_Current_DrawLayer( false );
255 
256  EXCELLON_IMAGE* drill_layer = new EXCELLON_IMAGE( layerId );
257 
258  // Read the Excellon drill file:
259  bool success = drill_layer->LoadFile( aFullFileName );
260 
261  if( !success )
262  {
263  delete drill_layer;
264  msg.Printf( _( "File %s not found" ), aFullFileName );
265  DisplayError( this, msg );
266  return false;
267  }
268 
269  layerId = images->AddGbrImage( drill_layer, layerId );
270 
271  if( layerId < 0 )
272  {
273  delete drill_layer;
274  DisplayError( this, _( "No room to load file" ) );
275  return false;
276  }
277 
278  // Display errors list
279  if( drill_layer->GetMessages().size() > 0 )
280  {
281  HTML_MESSAGE_BOX dlg( this, _( "Error reading EXCELLON drill file" ) );
282  dlg.ListSet( drill_layer->GetMessages() );
283  dlg.ShowModal();
284  }
285 
286  if( GetCanvas() )
287  {
288  for( GERBER_DRAW_ITEM* item : drill_layer->GetItems() )
289  GetCanvas()->GetView()->Add( (KIGFX::VIEW_ITEM*) item );
290  }
291 
292  return success;
293 }
294 
295 /*
296  * Read a EXCELLON file.
297  * Gerber classes are used because there is likeness between Gerber files
298  * and Excellon files
299  * DCode can easily store T code (tool size) as round (or oval) shape
300  * Drill commands are similar to flashed gerber items
301  * Routing commands are similar to Gerber polygons
302  * coordinates have the same format as Gerber, can be given in:
303  * decimal format (i.i. floating notation format)
304  * integer 2.4 format in imperial units,
305  * integer 3.2 or 3.3 format (metric units).
306  */
307 
308 bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName )
309 {
310  // Set the default parmeter values:
313 
314  m_Current_File = wxFopen( aFullFileName, "rt" );
315 
316  if( m_Current_File == NULL )
317  return false;
318 
319  wxString msg;
320  m_FileName = aFullFileName;
321 
322  LOCALE_IO toggleIo;
323 
324  // FILE_LINE_READER will close the file.
325  FILE_LINE_READER excellonReader( m_Current_File, m_FileName );
326 
327  while( true )
328  {
329  if( excellonReader.ReadLine() == 0 )
330  break;
331 
332  char* line = excellonReader.Line();
333  char* text = StrPurge( line );
334 
335  if( *text == ';' || *text == 0 ) // comment: skip line or empty malformed line
336  continue;
337 
339  {
341  }
342  else
343  {
344  switch( *text )
345  {
346  case 'M':
348  break;
349 
350  case 'G': // Line type Gxx : command
352  break;
353 
354  case 'X':
355  case 'Y': // command like X12550Y19250
356  Execute_Drill_Command(text);
357  break;
358 
359  case 'I':
360  case 'J': /* Auxiliary Move command */
361  m_IJPos = ReadIJCoord( text );
362  if( *text == '*' ) // command like X35142Y15945J504
363  {
364  Execute_Drill_Command( text);
365  }
366  break;
367 
368  case 'T': // Tool command
369  Select_Tool( text );
370  break;
371 
372  case '%':
373  break;
374 
375  default:
376  msg.Printf( "Unexpected symbol 0x%2.2X &lt;%c&gt;", *text, *text );
377  AddMessageToList( msg );
378  break;
379  } // End switch
380  }
381  }
382 
383  // Add our file attribute, to identify the drill file
385  char* text = (char*)file_attribute;
386  int dummyline = 0;
387  dummy.ParseAttribCmd( NULL, NULL, 0, text, dummyline );
388  delete m_FileFunction;
390 
391  m_InUse = true;
392 
393  return true;
394 }
395 
396 
398 {
399  EXCELLON_CMD* cmd = NULL;
400  wxString msg;
401 
402  // Search command in list
403  for( unsigned ii = 0; ; ii++ )
404  {
405  EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
406  int len = candidate->m_Name.size();
407 
408  if( len == 0 ) // End of list reached
409  break;
410 
411  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
412  {
413  cmd = candidate;
414  text += len;
415  break;
416  }
417  }
418 
419  if( !cmd )
420  {
421  msg.Printf( _( "Unknown Excellon command &lt;%s&gt;" ), text );
422  AddMessageToList( msg );
423  while( *text )
424  text++;
425 
426  return false;
427  }
428 
429  // Execute command
430  // some do nothing
431  switch( cmd->m_Code )
432  {
433  case DRILL_SKIP:
434  case DRILL_M_UNKNOWN:
435  break;
436 
437  case DRILL_M_END:
438  case DRILL_M_ENDFILE:
439  // if a route command is in progress, finish it
440  if( m_RouteModeOn )
442 
443  break;
444 
445  case DRILL_M_MESSAGE:
446  break;
447 
448  case DRILL_M_LONGMESSAGE:
449  break;
450 
451  case DRILL_M_HEADER:
453  break;
454 
455  case DRILL_M_ENDHEADER:
457  break;
458 
459  case DRILL_REWIND_STOP: // End of header. No action in a viewer
461  break;
462 
463  case DRILL_M_METRIC:
464  SelectUnits( true );
465  break;
466 
467  case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
468  case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
469  SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false );
470 
471  if( *text != ',' )
472  {
473  // No TZ or LZ specified. Should be a decimal format
474  // but this is not always the case. Use default TZ setting as default
475  m_NoTrailingZeros = false;
476  break;
477  }
478 
479  text++; // skip separator
480  if( *text == 'T' )
481  m_NoTrailingZeros = false;
482  else
483  m_NoTrailingZeros = true;
484  break;
485 
487  break;
488 
489  case DRILL_M_ENDPATTERN:
490  break;
491 
492  case DRILL_M_CANNEDTEXT:
493  break;
494 
495  case DRILL_M_TIPCHECK:
496  break;
497 
498  case DRILL_DETECT_BROKEN:
499  break;
500 
502  if( *text != ',' )
503  {
504  AddMessageToList( "ICI command has no parameter" );
505  break;
506  }
507  text++; // skip separator
508  // Parameter should be ON or OFF
509  if( strncasecmp( text, "OFF", 3 ) == 0 )
510  m_Relative = false;
511  else if( strncasecmp( text, "ON", 2 ) == 0 )
512  m_Relative = true;
513  else
514  AddMessageToList( "ICI command has incorrect parameter" );
515  break;
516 
518  break;
519 
521  break;
522 
523  case DRILL_AXIS_VERSION:
524  break;
525 
526  case DRILL_RESET_CMD:
527  break;
528 
530  break;
531 
532  case DRILL_FMT:
533  break;
534 
536  readToolInformation( text );
537  break;
538 
539  case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
540  // Only the last position is usefull:
541  if( m_RoutePositions.size() > 1 )
542  m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
543 
544  break;
545 
546  case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
548  break;
549  }
550 
551  while( *text )
552  text++;
553 
554  return true;
555 }
556 
557 
559 {
560  // Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
561  // and enter the TCODE param in list (using the D_CODE param management, which
562  // is similar to TCODE params.
563  if( *aText == 'T' ) // This is the beginning of the definition
564  aText++;
565 
566  // Read tool number:
567  int iprm = ReadInt( aText, false );
568 
569  // Skip Feed rate and Spindle speed, if any here
570  while( *aText && ( *aText == 'F' || *aText == 'S' ) )
571  {
572  aText++;
573  ReadInt( aText, false );
574  }
575 
576  // Read tool shape
577  if( ! *aText )
579  _( "Tool definition shape not found" ) ) );
580  else if( *aText != 'C' )
582  _( "Tool definition '%c' not supported" ), *aText ) );
583  if( *aText )
584  aText++;
585 
586  //read tool diameter:
587  double dprm = ReadDouble( aText, false );
588  m_Has_DCode = true;
589 
590  // Initialize Dcode to handle this Tool
591  // Remember: dcodes are >= FIRST_DCODE
592  D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
593 
594  if( dcode == NULL )
595  return false;
596 
597  // conv_scale = scaling factor from inch to Internal Unit
598  double conv_scale = IU_PER_MILS * 1000;
599 
600  if( m_GerbMetric )
601  conv_scale /= 25.4;
602 
603  dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
604  dcode->m_Shape = APT_CIRCLE;
605  dcode->m_Defined = true;
606 
607  return true;
608 }
609 
610 
612 {
613  D_CODE* tool;
614  GERBER_DRAW_ITEM * gbritem;
615 
616  while( true )
617  {
618  switch( *text )
619  {
620  case 'X':
621  case 'Y':
622  ReadXYCoord( text, true );
623 
624  if( *text == 'I' || *text == 'J' )
625  ReadIJCoord( text );
626 
627  break;
628 
629  case 'G': // G85 is found here for oval holes
632  break;
633 
634  case 0: // E.O.L: execute command
635  if( m_RouteModeOn )
636  {
637  // We are in routing mode, and this is an intermediate point.
638  // So just store it
639  int rmode = 0; // linear routing.
640 
642  rmode = ROUTE_CW;
644  rmode = ROUTE_CCW;
645 
647  {
648  EXCELLON_ROUTE_COORD point( m_CurrentPos, m_IJPos, rmode );
649  m_RoutePositions.push_back( point );
650  }
651  else
652  {
654  m_RoutePositions.push_back( point );
655  }
656  return true;
657  }
658 
659  tool = GetDCODE( m_Current_Tool );
660  if( !tool )
661  {
662  wxString msg;
663  msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
664  AddMessageToList( msg );
665  return false;
666  }
667 
668  gbritem = new GERBER_DRAW_ITEM( this );
669  AddItemToList( gbritem );
670 
671  if( m_SlotOn ) // Oblong hole
672  {
673  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
675  tool->m_Size, false );
676  // the hole is made: reset the slot on command (G85)
677  // (it is needed for each oblong hole)
678  m_SlotOn = false;
679  }
680  else
681  {
682  fillFlashedGBRITEM( gbritem, tool->m_Shape, tool->m_Num_Dcode,
683  m_CurrentPos, tool->m_Size, false );
684  }
685 
686  StepAndRepeatItem( *gbritem );
688  return true;
689  break;
690 
691  default:
692  text++;
693  break;
694  }
695  }
696 
697  return true;
698 }
699 
700 
701 bool EXCELLON_IMAGE::Select_Tool( char*& text )
702 {
703  // Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
704  // Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
705  // in tool selection command, if the tool is not defined in list,
706  // and the definition is embedded, it will be entered in list
707  char * startline = text; // the tool id starts here.
708  int tool_id = TCodeNumber( text );
709 
710  // T0 is legal, but is not a selection tool. it is a special command
711  if( tool_id >= 0 )
712  {
713  int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
714 
715  if( dcode_id > (TOOLS_MAX_COUNT - 1) )
716  dcode_id = TOOLS_MAX_COUNT - 1;
717 
718  m_Current_Tool = dcode_id;
719  D_CODE* currDcode = GetDCODEOrCreate( dcode_id, true );
720 
721  if( currDcode == NULL && tool_id > 0 ) // if the definition is embedded, enter it
722  {
723  text = startline; // text starts at the beginning of the command
724  readToolInformation( text );
725  currDcode = GetDCODE( dcode_id );
726  }
727 
728  if( currDcode )
729  currDcode->m_InUse = true;
730  }
731 
732  while( *text )
733  text++;
734 
735  return tool_id >= 0;
736 }
737 
738 
740 {
741  EXCELLON_CMD* cmd = NULL;
742  bool success = false;
743  int id = DRILL_G_UNKNOWN;
744 
745  // Search command in list
746  EXCELLON_CMD* candidate;
747  char * gcmd = text; // gcmd points the G command, for error messages.
748 
749  for( unsigned ii = 0; ; ii++ )
750  {
751  candidate = &excellon_G_CmdList[ii];
752  int len = candidate->m_Name.size();
753  if( len == 0 ) // End of list reached
754  break;
755  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
756  {
757  cmd = candidate;
758  text += len;
759  success = true;
760  id = cmd->m_Code;
761  break;
762  }
763  }
764 
765  switch( id )
766  {
767  case DRILL_G_ZERO_SET:
768  ReadXYCoord( text, true );
770  break;
771 
772  case DRILL_G_ROUT:
773  m_SlotOn = false;
774 
775  if( m_RouteModeOn )
777 
778  m_RouteModeOn = true;
779  m_RoutePositions.clear();
781  ReadXYCoord( text, true );
782  // This is the first point (starting point) of routing
783  m_RoutePositions.emplace_back( m_CurrentPos );
784  break;
785 
786  case DRILL_G_DRILL:
787  m_SlotOn = false;
788 
789  if( m_RouteModeOn )
791 
792  m_RouteModeOn = false;
793  m_RoutePositions.clear();
795  break;
796 
797  case DRILL_G_SLOT:
798  m_SlotOn = true;
799  break;
800 
801  case DRILL_G_LINEARMOVE:
804  ReadXYCoord( text, true );
805  m_RoutePositions.emplace_back( m_CurrentPos );
806  break;
807 
808  case DRILL_G_CWMOVE:
810  ReadXYCoord( text, true );
811 
812  if( *text == 'I' || *text == 'J' )
813  ReadIJCoord( text );
814 
816  m_RoutePositions.emplace_back( m_CurrentPos, m_IJPos, ROUTE_CW );
817  else
819  break;
820 
821  case DRILL_G_CCWMOVE:
823  ReadXYCoord( text, true );
824 
825  if( *text == 'I' || *text == 'J' )
826  ReadIJCoord( text );
827 
830  else
832  break;
833 
834  case DRILL_G_ABSOLUTE:
835  m_Relative = false; // false = absolute coord
836  break;
837 
838  case DRILL_G_INCREMENTAL:
839  m_Relative = true; // true = relative coord
840  break;
841 
842  case DRILL_G_UNKNOWN:
843  default:
844  AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: &lt;%s&gt;" ), FROM_UTF8(gcmd) ) );
845  while( *text )
846  text++;
847  return false;
848  }
849 
850  return success;
851 }
852 
853 void EXCELLON_IMAGE::SelectUnits( bool aMetric )
854 {
855  /* Coordinates are measured either in inch or metric (millimeters).
856  * Inch coordinates are in six digits (00.0000) with increments
857  * as small as 0.0001 (1/10,000).
858  * Metric coordinates can be measured in microns (thousandths of a millimeter)
859  * in one of the following three ways:
860  * Five digit 10 micron resolution (000.00)
861  * Six digit 10 micron resolution (0000.00)
862  * Six digit micron resolution (000.000)
863  *
864  * Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
865  * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
866  */
867  if( aMetric )
868  {
869  m_GerbMetric = true;
870  // number of digits in mantissa
872  // number of digits (mantissa+interger)
874  }
875  else
876  {
877  m_GerbMetric = false;
880  }
881 }
882 
883 
885 {
886  // Ends a route command started by M15 ot G01, G02 or G03 command
887  // if a route command is not in progress, do nothing
888 
889  if( !m_RouteModeOn )
890  return;
891 
892  D_CODE* tool = GetDCODE( m_Current_Tool );
893 
894  if( !tool )
895  {
896  AddMessageToList( wxString::Format( "Unknown tool code %d", m_Current_Tool ) );
897  return;
898  }
899 
900  for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
901  {
902  GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
903 
904  if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
905  {
906  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
907  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
908  tool->m_Size, false );
909  }
910  else // circular (cw or ccw) routing
911  {
912  bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
913  int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
914  wxPoint center;
915 
916  if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
917  center = wxPoint( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
918  else
919  center = computeCenter( m_RoutePositions[ii-1].GetPos(),
920  m_RoutePositions[ii].GetPos(), radius, rot_ccw );
921 
922  fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
923  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
924  center - m_RoutePositions[ii-1].GetPos(),
925  tool->m_Size, not rot_ccw , true,
926  false );
927  }
928 
929  AddItemToList( gbritem );
930 
931  StepAndRepeatItem( *gbritem );
932  }
933 
934  m_RoutePositions.clear();
935  m_RouteModeOn = false;
936 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:236
static EXCELLON_CMD excellon_G_CmdList[]
X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF....
void AddMessageToList(const wxString &aMessage)
Function AddMessageToList Add a message to the message list.
X2_ATTRIBUTE_FILEFUNCTION * m_FileFunction
char * ReadLine() override
Function ReadLine reads a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:194
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:139
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:62
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
Definition: string.cpp:310
virtual void ResetDefaultValues() override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:90
GERBER_DRAW_ITEMS & GetItems()
This file is part of the common library.
wxSize m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:94
wxPoint ReadIJCoord(char *&Text)
Returns the current coordinate type pointed to by InnJnn Text (InnnnJmmmm) These coordinates are rela...
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
virtual EDA_DRAW_PANEL_GAL * GetCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
static const int fmtIntegerInch
D_CODE * GetDCODE(int aDCODE) const
Function GetDCODE returns a pointer to the D_CODE within this GERBER for the given aDCODE.
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:103
void ClearMessageList()
Function ClearMessageList Clear the message list Call it before reading a Gerber file.
bool Select_Tool(char *&text)
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.
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.
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
FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:180
void AddItemToList(GERBER_DRAW_ITEM *aItem)
Add a new GERBER_DRAW_ITEM item to the drawings list.
bool Execute_HEADER_And_M_Command(char *&text)
#define NULL
VECTOR2< double > VECTOR2D
Definition: vector2d.h:593
GERBER_FILE_IMAGE * GetGbrImage(int aIdx)
#define FIRST_DCODE
Definition: dcode.h:71
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
const wxArrayString & GetMessages() const
void ListSet(const wxString &aList)
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.
static const int fmtIntegerMM
bool Execute_Drill_Command(char *&text)
virtual KIGFX::VIEW * GetView() const
Function GetView() Returns a pointer to the VIEW instance used in the panel.
#define ROUTE_CW
std::string m_Name
HTML_MESSAGE_BOX.
double Angle() const
Function Angle computes the angle of the vector.
Definition: vector2d.h:313
wxPoint ReadXYCoord(char *&aText, bool aExcellonMode=false)
Function ReadXYCoord Returns the current coordinate type pointed to by XnnYnn Text (XnnnnYmmmm)
excellon_state m_State
GBR_LAYOUT * GetGerberLayout() const
static const char file_attribute[]
int TCodeNumber(char *&aText)
bool readToolInformation(char *&aText)
Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00 and enter params in TCODE list.
VECTOR2< T > Rotate(double aAngle) const
Function Rotate rotates the vector by a given angle.
Definition: vector2d.h:377
LAST_EXTRA_ARC_DATA_TYPE m_LastArcDataType
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 to draw a dummy shape when a LIB_PART is not found in library.
#define _(s)
Definition: 3d_actions.cpp:31
void FinishRouteCommand()
Ends a route command started by M15 ot G01, G02 or G03 command.
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
GERBER_FILE_IMAGE_LIST * GetImagesList() const
Definition: gbr_layout.cpp:44
#define IU_PER_MILS
Definition: plotter.cpp:137
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:61
The common library.
std::vector< EXCELLON_ROUTE_COORD > m_RoutePositions
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:299
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Function Add() Adds a VIEW_ITEM to the view.
Definition: view.cpp:346
bool Execute_EXCELLON_G_Command(char *&text)
void SelectUnits(bool aMetric)
void StepAndRepeatItem(const GERBER_DRAW_ITEM &aItem)
Function StepAndRepeatItem Gerber format has a command Step an Repeat This function must be called wh...
D_CODE * GetDCODEOrCreate(int aDCODE, bool aCreateIfNoExist=true)
Function GetDCODEOrCreate returns a pointer to the D_CODE within this GERBER for the given aDCODE.
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:207
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:158
bool Read_EXCELLON_File(const wxString &aFullFileName)
int ReadInt(char *&text, bool aSkipSeparator=true)
Function ReadInt reads an int from an ASCII character buffer.