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-2021 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 <math/util.h> // for KiROUND
66 
67 #include <gerbview.h>
68 #include <gerbview_frame.h>
69 #include <gerber_file_image.h>
70 #include <gerber_file_image_list.h>
71 #include <excellon_image.h>
72 #include <excellon_defaults.h>
73 #include <macros.h>
74 #include <string_utils.h>
75 #include <locale_io.h>
76 #include <X2_gerber_attributes.h>
77 #include <view/view.h>
78 #include <gerbview_settings.h>
79 
80 #include <cmath>
81 
83 
84 // A helper function to calculate the arc center of an arc
85 // known by 2 end points, the radius, and the angle direction (CW or CCW)
86 // Arc angles are <= 180 degrees in circular interpol.
87 static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int& aRadius, bool aRotCCW )
88 {
89  wxPoint center;
90  VECTOR2D end;
91  end.x = double(aEnd.x - aStart.x);
92  end.y = double(aEnd.y - aStart.y);
93 
94  // Be sure aRadius/2 > dist between aStart and aEnd
95  double min_radius = end.EuclideanNorm() * 2;
96 
97  if( min_radius <= aRadius )
98  {
99  // Adjust the radius and the arc center for a 180 deg arc between end points
100  aRadius = KiROUND( min_radius );
101  center.x = ( aStart.x + aEnd.x + 1 ) / 2;
102  center.y = ( aStart.y + aEnd.y + 1 ) / 2;
103  return center;
104  }
105 
106  /* to compute the centers position easily:
107  * rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0).
108  * the X center position is end.x/2
109  * the Y center positions are on the vertical line starting at end.x/2, 0
110  * and solve aRadius^2 = X^2 + Y^2 (2 values)
111  */
112  double seg_angle = end.Angle(); //in radian
113  VECTOR2D h_segm = end.Rotate( - seg_angle );
114  double cX = h_segm.x/2;
115  double cY1 = sqrt( (double)aRadius*aRadius - cX*cX );
116  double cY2 = -cY1;
117  VECTOR2D center1( cX, cY1 );
118  center1 = center1.Rotate( seg_angle );
119  double arc_angle1 = (end - center1).Angle() - (VECTOR2D(0.0,0.0) - center1).Angle();
120  VECTOR2D center2( cX, cY2 );
121  center2 = center2.Rotate( seg_angle );
122  double arc_angle2 = (end - center2).Angle() - (VECTOR2D(0.0,0.0) - center2).Angle();
123 
124  if( !aRotCCW )
125  {
126  if( arc_angle1 < 0.0 )
127  arc_angle1 += 2*M_PI;
128 
129  if( arc_angle2 < 0.0 )
130  arc_angle2 += 2*M_PI;
131  }
132  else
133  {
134  if( arc_angle1 > 0.0 )
135  arc_angle1 -= 2*M_PI;
136 
137  if( arc_angle2 > 0.0 )
138  arc_angle2 -= 2*M_PI;
139  }
140 
141  // Arc angle must be <= 180.0 degrees.
142  // So choose the center that create a arc angle <= 180.0
143  if( std::abs( arc_angle1 ) <= M_PI )
144  {
145  center.x = KiROUND( center1.x );
146  center.y = KiROUND( center1.y );
147  }
148  else
149  {
150  center.x = KiROUND( center2.x );
151  center.y = KiROUND( center2.y );
152  }
153 
154  return center+aStart;
155 }
156 
157 extern int ReadInt( char*& text, bool aSkipSeparator = true );
158 extern double ReadDouble( char*& text, bool aSkipSeparator = true );
159 
160 // See rs274d.cpp:
161 extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
162  APERTURE_T aAperture,
163  int Dcode_index,
164  const wxPoint& aPos,
165  wxSize aSize,
166  bool aLayerNegative );
167 
168 extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
169  int Dcode_index,
170  const wxPoint& aStart,
171  const wxPoint& aEnd,
172  wxSize aPenSize,
173  bool aLayerNegative );
174 
175 extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem, int Dcode_index,
176  const wxPoint& aStart, const wxPoint& aEnd,
177  const wxPoint& aRelCenter, wxSize aPenSize,
178  bool aClockwise, bool aMultiquadrant,
179  bool aLayerNegative );
180 
181 // Gerber X2 files have a file attribute which specify the type of image
182 // (copper, solder paste ... and sides tpo, bottom or inner copper layers)
183 // Excellon drill files do not have attributes, so, just to identify the image
184 // In gerbview, we add this attribute, similar to a Gerber drill file
185 static const char file_attribute[] = ".FileFunction,Other,Drill*";
186 
188 {
189  { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind
190  { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind
191  { "M15", DRILL_M_TOOL_DOWN, 0 }, // tool down (starting a routed hole)
192  { "M16", DRILL_M_TOOL_UP, 0 }, // tool up (ending a routed hole)
193  { "M17", DRILL_M_TOOL_UP, 0 }, // tool up similar to M16 for a viewer
194  { "M30", DRILL_M_ENDFILE, -1 }, // End of File (last line of NC drill)
195  { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message
196  { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line)
197  { "M48", DRILL_M_HEADER, 0 }, // beginning of a header
198  { "M95", DRILL_M_ENDHEADER, 0 }, // End of the header
199  { "METRIC", DRILL_METRIC_HEADER, 1 },
200  { "INCH", DRILL_IMPERIAL_HEADER, 1 },
201  { "M71", DRILL_M_METRIC, 1 },
202  { "M72", DRILL_M_IMPERIAL, 1 },
203  { "M25", DRILL_M_BEGINPATTERN, 0 }, // Beginning of Pattern
204  { "M01", DRILL_M_ENDPATTERN, 0 }, // End of Pattern
205  { "M97", DRILL_M_CANNEDTEXT, -1 },
206  { "M98", DRILL_M_CANNEDTEXT, -1 },
207  { "DETECT", DRILL_DETECT_BROKEN, -1 },
208  { "ICI", DRILL_INCREMENTALHEADER, 1 },
209  { "FMAT", DRILL_FMT, 1 }, // Use Format command
210  { ";FILE_FORMAT",
211  DRILL_FORMAT_ALTIUM, 1 }, // Use Format command
212  { ";", DRILL_HEADER_SKIP, 0 }, // Other ; hints that we don't implement
213  { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 },
214  { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop
215  { "AFS", DRILL_AUTOMATIC_SPEED, 0 }, // Automatic Feeds and Speeds
216  { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version
217  { "R", DRILL_RESET_CMD, -1 }, // Reset commands
218  { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header
219  { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header
220  // Keep this item after all commands starting by 'T':
221  { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information
222  { "", DRILL_M_UNKNOWN, 0 } // last item in list
223 };
224 
226 {
227  { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode
228  { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode
229  { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode
230  { "G00", DRILL_G_ROUT, 1 }, // Route Mode
231  { "G05", DRILL_G_DRILL, 0 }, // Drill Mode
232  { "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes)
233  { "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode
234  { "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode
235  { "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode
236  { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordinates origin)
237  { "", DRILL_G_UNKNOWN, 0 }, // last item in list
238 };
239 
240 
241 bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
242 {
243  wxString msg;
244  int layerId = GetActiveLayer(); // current layer used in GerbView
246  GERBER_FILE_IMAGE* gerber_layer = images->GetGbrImage( layerId );
247 
248  // If the active layer contains old gerber or nc drill data, remove it
249  if( gerber_layer )
250  Erase_Current_DrawLayer( false );
251 
252  std::unique_ptr<EXCELLON_IMAGE> drill_layer_uptr = std::make_unique<EXCELLON_IMAGE>( layerId );
253 
254  EXCELLON_DEFAULTS nc_defaults;
255  GERBVIEW_SETTINGS* cfg = static_cast<GERBVIEW_SETTINGS*>( config() );
256  cfg->GetExcellonDefaults( nc_defaults );
257 
258  // Read the Excellon drill file:
259  bool success = drill_layer_uptr->LoadFile( aFullFileName, &nc_defaults );
260 
261  if( !success )
262  {
263  drill_layer_uptr.reset();
264  msg.Printf( _( "File %s not found." ), aFullFileName );
265  ShowInfoBarError( msg );
266  return false;
267  }
268 
269  EXCELLON_IMAGE* drill_layer = drill_layer_uptr.release();
270 
271  layerId = images->AddGbrImage( drill_layer, layerId );
272 
273  if( layerId < 0 )
274  {
275  delete drill_layer;
276  ShowInfoBarError( _( "No empty layers to load file into." ) );
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( GetCanvas() )
289  {
290  for( GERBER_DRAW_ITEM* item : drill_layer->GetItems() )
291  GetCanvas()->GetView()->Add( (KIGFX::VIEW_ITEM*) item );
292  }
293 
294  return success;
295 }
296 
297 
299 {
301  SelectUnits( false, nullptr ); // Default unit = inch
302  m_hasFormat = false; // will be true if a Altium file containing
303  // the nn:mm file format is read
304 
305  // Files using non decimal can use No Trailing zeros or No leading Zeros
306  // Unfortunately, the identifier (INCH,TZ or INCH,LZ for instance) is not
307  // always set in drill files.
308  // The option leading zeros looks like more frequent, so use this default
309  m_NoTrailingZeros = true;
310 }
311 
312 /*
313  * Read a EXCELLON file.
314  * Gerber classes are used because there is likeness between Gerber files
315  * and Excellon files
316  * DCode can easily store T code (tool size) as round (or oval) shape
317  * Drill commands are similar to flashed gerber items
318  * Routing commands are similar to Gerber polygons
319  * coordinates have the same format as Gerber, can be given in:
320  * decimal format (i.i. floating notation format)
321  * integer 2.4 format in imperial units,
322  * integer 3.2 or 3.3 format (metric units).
323  */
324 bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName, EXCELLON_DEFAULTS* aDefaults )
325 {
326  // Set the default parameter values:
329 
330  m_Current_File = wxFopen( aFullFileName, "rt" );
331 
332  if( m_Current_File == nullptr )
333  return false;
334 
335  // Initial format setting, usualy defined in file, but not always...
336  m_NoTrailingZeros = aDefaults->m_LeadingZero;
337  m_GerbMetric = aDefaults->m_UnitsMM;
338 
339  wxString msg;
340  m_FileName = aFullFileName;
341 
342  LOCALE_IO toggleIo;
343 
344  // FILE_LINE_READER will close the file.
345  FILE_LINE_READER excellonReader( m_Current_File, m_FileName );
346 
347  while( true )
348  {
349  if( excellonReader.ReadLine() == nullptr )
350  break;
351 
352  char* line = excellonReader.Line();
353  char* text = StrPurge( line );
354 
355  if( *text == 0 ) // Skip empty lines
356  continue;
357 
359  {
361 
362  // Now units (inch/mm) are known, set the coordinate format
363  SelectUnits( m_GerbMetric, aDefaults );
364  }
365  else
366  {
367  switch( *text )
368  {
369  case ';':
370  case 'M':
372  break;
373 
374  case 'G': // Line type Gxx : command
376  break;
377 
378  case 'X':
379  case 'Y': // command like X12550Y19250
381  break;
382 
383  case 'I':
384  case 'J': /* Auxiliary Move command */
385  m_IJPos = ReadIJCoord( text );
386  if( *text == '*' ) // command like X35142Y15945J504
387  {
389  }
390  break;
391 
392  case 'T': // Select Tool command (can also create
393  // the tool with an embedded definition)
394  Select_Tool( text );
395  break;
396 
397  case '%':
398  break;
399 
400  default:
401  msg.Printf( "Unexpected symbol 0x%2.2X &lt;%c&gt;", *text, *text );
402  AddMessageToList( msg );
403  break;
404  } // End switch
405  }
406  }
407 
408  // Add our file attribute, to identify the drill file
410  char* text = (char*)file_attribute;
411  int dummyline = 0;
412  dummy.ParseAttribCmd( nullptr, nullptr, 0, text, dummyline );
413  delete m_FileFunction;
415 
416  m_InUse = true;
417 
418  return true;
419 }
420 
421 
423 {
424  EXCELLON_CMD* cmd = nullptr;
425  wxString msg;
426 
427  // Search command in list
428  for( unsigned ii = 0; ; ii++ )
429  {
430  EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
431  int len = candidate->m_Name.size();
432 
433  if( len == 0 ) // End of list reached
434  break;
435 
436  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
437  {
438  cmd = candidate;
439  text += len;
440  break;
441  }
442  }
443 
444  if( !cmd )
445  {
446  msg.Printf( _( "Unknown Excellon command &lt;%s&gt;" ), text );
447  AddMessageToList( msg );
448  while( *text )
449  text++;
450 
451  return false;
452  }
453 
454  // Execute command
455  // some do nothing
456  switch( cmd->m_Code )
457  {
458  case DRILL_SKIP:
459  case DRILL_M_UNKNOWN:
460  break;
461 
462  case DRILL_M_END:
463  case DRILL_M_ENDFILE:
464  // if a route command is in progress, finish it
465  if( m_RouteModeOn )
467 
468  break;
469 
470  case DRILL_M_MESSAGE:
471  break;
472 
473  case DRILL_M_LONGMESSAGE:
474  break;
475 
476  case DRILL_M_HEADER:
478  break;
479 
480  case DRILL_M_ENDHEADER:
482  break;
483 
484  case DRILL_REWIND_STOP: // End of header. No action in a viewer
486  break;
487 
488  case DRILL_FORMAT_ALTIUM:
489  readFileFormat( text );
490  break;
491 
492  case DRILL_HEADER_SKIP:
493  break;
494 
495  case DRILL_M_METRIC:
496  SelectUnits( true, nullptr );
497  break;
498 
499  case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
500  case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
501  SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false, nullptr );
502 
503  if( *text != ',' )
504  {
505  // No TZ or LZ specified. Should be a decimal format
506  // but this is not always the case. Use our default setting
507  break;
508  }
509 
510  text++; // skip separator
511  if( *text == 'T' )
512  m_NoTrailingZeros = false;
513  else
514  m_NoTrailingZeros = true;
515  break;
516 
518  break;
519 
520  case DRILL_M_ENDPATTERN:
521  break;
522 
523  case DRILL_M_CANNEDTEXT:
524  break;
525 
526  case DRILL_M_TIPCHECK:
527  break;
528 
529  case DRILL_DETECT_BROKEN:
530  break;
531 
533  if( *text != ',' )
534  {
535  AddMessageToList( "ICI command has no parameter" );
536  break;
537  }
538  text++; // skip separator
539  // Parameter should be ON or OFF
540  if( strncasecmp( text, "OFF", 3 ) == 0 )
541  m_Relative = false;
542  else if( strncasecmp( text, "ON", 2 ) == 0 )
543  m_Relative = true;
544  else
545  AddMessageToList( "ICI command has incorrect parameter" );
546  break;
547 
549  break;
550 
552  break;
553 
554  case DRILL_AXIS_VERSION:
555  break;
556 
557  case DRILL_RESET_CMD:
558  break;
559 
561  break;
562 
563  case DRILL_FMT:
564  break;
565 
568  break;
569 
570  case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
571  // Only the last position is useful:
572  if( m_RoutePositions.size() > 1 )
573  m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
574 
575  break;
576 
577  case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
579  break;
580  }
581 
582  while( *text )
583  text++;
584 
585  return true;
586 }
587 
588 
589 void EXCELLON_IMAGE::readFileFormat( char*& aText )
590 {
591  int mantissaDigits = 0;
592  int characteristicDigits = 0;
593 
594  // Example String: ;FILE_FORMAT=4:4
595  // The ;FILE_FORMAT potion will already be stripped off.
596  // Parse the rest strictly as single_digit:single_digit like 4:4 or 2:4
597  // Don't allow anything clever like spaces or multiple digits
598  if( *aText != '=' )
599  return;
600 
601  aText++;
602 
603  if( !isdigit( *aText ) )
604  return;
605 
606  characteristicDigits = *aText - '0';
607  aText++;
608 
609  if( *aText != ':' )
610  return;
611 
612  aText++;
613 
614  if( !isdigit( *aText ) )
615  return;
616 
617  mantissaDigits = *aText - '0';
618 
619  m_hasFormat = true;
620  m_FmtLen.x = m_FmtLen.y = characteristicDigits + mantissaDigits;
621  m_FmtScale.x = m_FmtScale.y = mantissaDigits;
622 }
623 
624 
626 {
627  // Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
628  // and enter the TCODE param in list (using the D_CODE param management, which
629  // is similar to TCODE params.
630  if( *aText == 'T' ) // This is the beginning of the definition
631  aText++;
632 
633  // Read tool number:
634  int iprm = ReadInt( aText, false );
635 
636  // Skip Feed rate and Spindle speed, if any here
637  while( *aText && ( *aText == 'F' || *aText == 'S' ) )
638  {
639  aText++;
640  ReadInt( aText, false );
641  }
642 
643  // Read tool shape
644  if( ! *aText )
646  _( "Tool definition shape not found" ) ) );
647  else if( *aText != 'C' )
649  _( "Tool definition '%c' not supported" ), *aText ) );
650  if( *aText )
651  aText++;
652 
653  //read tool diameter:
654  double dprm = ReadDouble( aText, false );
655  m_Has_DCode = true;
656 
657  // Initialize Dcode to handle this Tool
658  // Remember: dcodes are >= FIRST_DCODE
659  D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
660 
661  if( dcode == nullptr )
662  return false;
663 
664  // conv_scale = scaling factor from inch to Internal Unit
665  double conv_scale = IU_PER_MILS * 1000;
666 
667  if( m_GerbMetric )
668  conv_scale /= 25.4;
669 
670  dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
671  dcode->m_Shape = APT_CIRCLE;
672  dcode->m_Defined = true;
673 
674  return true;
675 }
676 
677 
679 {
680  D_CODE* tool;
681  GERBER_DRAW_ITEM * gbritem;
682 
683  while( true )
684  {
685  switch( *text )
686  {
687  case 'X':
688  case 'Y':
689  ReadXYCoord( text, true );
690 
691  if( *text == 'I' || *text == 'J' )
692  ReadIJCoord( text );
693 
694  break;
695 
696  case 'G': // G85 is found here for oval holes
699  break;
700 
701  case 0: // E.O.L: execute command
702  if( m_RouteModeOn )
703  {
704  // We are in routing mode, and this is an intermediate point.
705  // So just store it
706  int rmode = 0; // linear routing.
707 
709  rmode = ROUTE_CW;
711  rmode = ROUTE_CCW;
712 
714  {
715  EXCELLON_ROUTE_COORD point( m_CurrentPos, m_IJPos, rmode );
716  m_RoutePositions.push_back( point );
717  }
718  else
719  {
721  m_RoutePositions.push_back( point );
722  }
723  return true;
724  }
725 
726  tool = GetDCODE( m_Current_Tool );
727  if( !tool )
728  {
729  wxString msg;
730  msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
731  AddMessageToList( msg );
732  return false;
733  }
734 
735  gbritem = new GERBER_DRAW_ITEM( this );
736  AddItemToList( gbritem );
737 
738  if( m_SlotOn ) // Oblong hole
739  {
740  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
742  tool->m_Size, false );
743  // the hole is made: reset the slot on command (G85)
744  // (it is needed for each oblong hole)
745  m_SlotOn = false;
746  }
747  else
748  {
749  fillFlashedGBRITEM( gbritem, tool->m_Shape, tool->m_Num_Dcode,
750  m_CurrentPos, tool->m_Size, false );
751  }
752 
753  StepAndRepeatItem( *gbritem );
755  return true;
756  break;
757 
758  default:
759  text++;
760  break;
761  }
762  }
763 
764  return true;
765 }
766 
767 
768 bool EXCELLON_IMAGE::Select_Tool( char*& text )
769 {
770  // Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
771  // Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
772  // in tool selection command, if the tool is not defined in list,
773  // and the definition is embedded, it will be entered in list
774  char * startline = text; // the tool id starts here.
775  int tool_id = TCodeNumber( text );
776 
777  // T0 is legal, but is not a selection tool. it is a special command
778  if( tool_id >= 0 )
779  {
780  int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
781 
782  if( dcode_id > (TOOLS_MAX_COUNT - 1) )
783  dcode_id = TOOLS_MAX_COUNT - 1;
784 
785  m_Current_Tool = dcode_id;
786  D_CODE* currDcode = GetDCODE( dcode_id );
787 
788  // if the tool does not exist, and the definition is embedded, create this tool
789  if( currDcode == nullptr && tool_id > 0 )
790  {
791  text = startline; // text starts at the beginning of the command
793  currDcode = GetDCODE( dcode_id );
794  }
795 
796  // If the Tool is still not existing, create a dummy tool
797  if( !currDcode )
798  currDcode = GetDCODEOrCreate( dcode_id, true );
799 
800  if( currDcode )
801  currDcode->m_InUse = true;
802  }
803 
804  while( *text )
805  text++;
806 
807  return tool_id >= 0;
808 }
809 
810 
812 {
813  EXCELLON_CMD* cmd = nullptr;
814  bool success = false;
815  int id = DRILL_G_UNKNOWN;
816 
817  // Search command in list
818  EXCELLON_CMD* candidate;
819  char * gcmd = text; // gcmd points the G command, for error messages.
820 
821  for( unsigned ii = 0; ; ii++ )
822  {
823  candidate = &excellon_G_CmdList[ii];
824  int len = candidate->m_Name.size();
825  if( len == 0 ) // End of list reached
826  break;
827  if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
828  {
829  cmd = candidate;
830  text += len;
831  success = true;
832  id = cmd->m_Code;
833  break;
834  }
835  }
836 
837  switch( id )
838  {
839  case DRILL_G_ZERO_SET:
840  ReadXYCoord( text, true );
842  break;
843 
844  case DRILL_G_ROUT:
845  m_SlotOn = false;
846 
847  if( m_RouteModeOn )
849 
850  m_RouteModeOn = true;
851  m_RoutePositions.clear();
853  ReadXYCoord( text, true );
854  // This is the first point (starting point) of routing
855  m_RoutePositions.emplace_back( m_CurrentPos );
856  break;
857 
858  case DRILL_G_DRILL:
859  m_SlotOn = false;
860 
861  if( m_RouteModeOn )
863 
864  m_RouteModeOn = false;
865  m_RoutePositions.clear();
867  break;
868 
869  case DRILL_G_SLOT:
870  m_SlotOn = true;
871  break;
872 
873  case DRILL_G_LINEARMOVE:
876  ReadXYCoord( text, true );
877  m_RoutePositions.emplace_back( m_CurrentPos );
878  break;
879 
880  case DRILL_G_CWMOVE:
882  ReadXYCoord( text, true );
883 
884  if( *text == 'I' || *text == 'J' )
885  ReadIJCoord( text );
886 
888  m_RoutePositions.emplace_back( m_CurrentPos, m_IJPos, ROUTE_CW );
889  else
891  break;
892 
893  case DRILL_G_CCWMOVE:
895  ReadXYCoord( text, true );
896 
897  if( *text == 'I' || *text == 'J' )
898  ReadIJCoord( text );
899 
902  else
904  break;
905 
906  case DRILL_G_ABSOLUTE:
907  m_Relative = false; // false = absolute coord
908  break;
909 
910  case DRILL_G_INCREMENTAL:
911  m_Relative = true; // true = relative coord
912  break;
913 
914  case DRILL_G_UNKNOWN:
915  default:
916  AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: &lt;%s&gt;" ), FROM_UTF8(gcmd) ) );
917  while( *text )
918  text++;
919  return false;
920  }
921 
922  return success;
923 }
924 
925 void EXCELLON_IMAGE::SelectUnits( bool aMetric, EXCELLON_DEFAULTS* aDefaults )
926 {
927  /* Coordinates are measured either in inch or metric (millimeters).
928  * Inch coordinates are in six digits (00.0000) with increments
929  * as small as 0.0001 (1/10,000).
930  * Metric coordinates can be measured in microns (thousandths of a millimeter)
931  * in one of the following three ways:
932  * Five digit 10 micron resolution (000.00)
933  * Six digit 10 micron resolution (0000.00)
934  * Six digit micron resolution (000.000)
935  *
936  * Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
937  * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
938  *
939  * However some drill files do not use standard values.
940  */
941  if( aMetric )
942  {
943  m_GerbMetric = true;
944 
945  if( !m_hasFormat )
946  {
947  if( aDefaults )
948  {
949  // number of digits in mantissa
950  m_FmtScale.x = m_FmtScale.y = aDefaults->m_MmMantissaLen;
951  // number of digits (mantissa+integer)
952  m_FmtLen.x = m_FmtLen.y = aDefaults->m_MmIntegerLen
953  + aDefaults->m_MmMantissaLen;
954  }
955  else
956  {
959  }
960  }
961  }
962  else
963  {
964  m_GerbMetric = false;
965 
966  if( !m_hasFormat )
967  {
968  if( aDefaults )
969  {
970  m_FmtScale.x = m_FmtScale.y = aDefaults->m_InchMantissaLen;
971  m_FmtLen.x = m_FmtLen.y = aDefaults->m_InchIntegerLen
972  + aDefaults->m_InchMantissaLen;
973  }
974  else
975  {
978  }
979  }
980  }
981 }
982 
983 
985 {
986  // Ends a route command started by M15 ot G01, G02 or G03 command
987  // if a route command is not in progress, do nothing
988 
989  if( !m_RouteModeOn )
990  return;
991 
992  D_CODE* tool = GetDCODE( m_Current_Tool );
993 
994  if( !tool )
995  {
996  AddMessageToList( wxString::Format( "Unknown tool code %d", m_Current_Tool ) );
997  return;
998  }
999 
1000  for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
1001  {
1002  GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
1003 
1004  if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
1005  {
1006  fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
1007  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1008  tool->m_Size, false );
1009  }
1010  else // circular (cw or ccw) routing
1011  {
1012  bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
1013  int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
1014  wxPoint center;
1015 
1016  if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
1017  center = wxPoint( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
1018  else
1019  center = computeCenter( m_RoutePositions[ii-1].GetPos(),
1020  m_RoutePositions[ii].GetPos(), radius, rot_ccw );
1021 
1022  fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
1023  m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1024  center - m_RoutePositions[ii-1].GetPos(),
1025  tool->m_Size, not rot_ccw , true,
1026  false );
1027  }
1028 
1029  AddItemToList( gbritem );
1030 
1031  StepAndRepeatItem( *gbritem );
1032  }
1033 
1034  m_RoutePositions.clear();
1035  m_RouteModeOn = false;
1036 }
static EXCELLON_CMD excellon_G_CmdList[]
X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF....
void AddMessageToList(const wxString &aMessage)
Add a message to the message list.
X2_ATTRIBUTE_FILEFUNCTION * m_FileFunction
bool LoadFile(const wxString &aFullFileName, EXCELLON_DEFAULTS *aDefaults)
Read and load a drill (EXCELLON format) file.
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:214
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
The attribute value consists of a number of substrings separated by a comma.
#define FMT_INTEGER_INCH
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
GERBER_DRAW_ITEMS & GetItems()
wxSize m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:188
wxPoint ReadIJCoord(char *&Text)
Return the current coordinate type pointed to by InnJnn Text (InnnnJmmmm)
bool m_InUse
false if the aperture (previously defined) is not used to draw something
Definition: dcode.h:197
#define ROUTE_CCW
APERTURE_T m_Shape
shape ( Line, rectangle, circle , oval .. )
Definition: dcode.h:189
static EXCELLON_CMD excellonHeaderCmdList[]
static wxPoint computeCenter(wxPoint aStart, wxPoint aEnd, int &aRadius, bool aRotCCW)
void SelectUnits(bool aMetric, EXCELLON_DEFAULTS *aDefaults)
Switch unit selection, and the coordinate format (nn:mm) if not yet set.
virtual EDA_DRAW_PANEL_GAL * GetCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
D_CODE * GetDCODE(int aDCODE) const
Return a pointer to the D_CODE within this GERBER for the given aDCODE.
Hold the image data and parameters for one gerber file and layer parameters.
wxSize m_FmtLen
Image rotation (0, 90, 180, 270 only) in degrees.
void fillFlashedGBRITEM(GERBER_DRAW_ITEM *aGbrItem, APERTURE_T aAperture, int Dcode_index, const wxPoint &aPos, wxSize aSize, bool aLayerNegative)
Initializes a given GBRITEM so that it can draw a circle which is filled and has no pen border.
Definition: rs274d.cpp:99
void ClearMessageList()
Clear the message list.
EXCELLON_STATE m_State
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.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:81
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...
wxSize m_FmtScale
< Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4.
This file contains miscellaneous commonly used macros and functions.
int m_ArcRadius
Identifier for arc data type (IJ (center) or A## (radius)).
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:199
A LINE_READER that reads from an open file.
Definition: richio.h:172
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)
VECTOR2< double > VECTOR2D
Definition: vector2d.h:622
GERBER_FILE_IMAGE * GetGbrImage(int aIdx)
#define FIRST_DCODE
Definition: dcode.h:70
APERTURE_T
The set of all gerber aperture types allowed, according to page 16 of http://gerbv....
Definition: dcode.h:49
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:190
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
bool Execute_Drill_Command(char *&text)
#define _(s)
virtual KIGFX::VIEW * GetView() const
Return a pointer to the #VIEW instance used in the panel.
#define ROUTE_CW
bool m_hasFormat
Excellon file do not have a format statement to specify the coordinate format like nn:mm.
bool m_Relative
< false = absolute Coord, true = relative Coord.
std::string m_Name
double Angle() const
Compute the angle of the vector.
Definition: vector2d.h:307
wxPoint ReadXYCoord(char *&aText, bool aExcellonMode=false)
Return the current coordinate type pointed to by XnnYnn Text (XnnnnYmmmm).
#define FMT_INTEGER_MM
#define FMT_MANTISSA_INCH
GBR_LAYOUT * GetGerberLayout() const
bool m_Has_DCode
< True if has DCodes in file or false if no DCodes found. Perhaps deprecated RS274D file.
static const char file_attribute[]
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
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.
int GetActiveLayer() const
Return the active layer.
VECTOR2< T > Rotate(double aAngle) const
Rotate the vector by a given angle.
Definition: vector2d.h:371
LAST_EXTRA_ARC_DATA_TYPE m_LastArcDataType
A gerber DCODE (also called Aperture) definition.
Definition: dcode.h:80
Handle a drill image.
void readFileFormat(char *&aText)
Read an Altium-specific FILE_FORMAT=X:X attribute that specifies the length and mantissa of the numbe...
virtual void ResetDefaultValues() override
Set all parameters to a default value, before reading a file.
void FinishRouteCommand()
End a route command started by M15 ot G01, G02 or G03 command.
double ReadDouble(char *&text, bool aSkipSeparator=true)
Read a double precision floating point number from an ASCII character buffer.
#define TOOLS_MAX_COUNT
Definition: dcode.h:72
GERBER_FILE_IMAGE_LIST * GetImagesList() const
Definition: gbr_layout.cpp:41
#define IU_PER_MILS
Definition: plotter.cpp:136
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:73
std::vector< EXCELLON_ROUTE_COORD > m_RoutePositions
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
void GetExcellonDefaults(EXCELLON_DEFAULTS &aNCDefaults)
return the Excellon default values to read a drill file
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:323
bool Execute_EXCELLON_G_Command(char *&text)
#define FMT_MANTISSA_MM
management of default values used to read a Excellon (.nc) drill file Some important parameters are n...
void StepAndRepeatItem(const GERBER_DRAW_ITEM &aItem)
Gerber format has a command Step an Repeat.
D_CODE * GetDCODEOrCreate(int aDCODE, bool aCreateIfNoExist=true)
Return 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)
Initialize a given GBRITEM so that it can draw an arc G code.
Definition: rs274d.cpp:202
virtual void ResetDefaultValues()
Set all parameters to a default value, before reading a file.
void fillLineGBRITEM(GERBER_DRAW_ITEM *aGbrItem, int Dcode_index, const wxPoint &aStart, const wxPoint &aEnd, wxSize aPenSize, bool aLayerNegative)
Initialize a given GBRITEM so that it can draw a linear D code.
Definition: rs274d.cpp:153
bool Read_EXCELLON_File(const wxString &aFullFileName)
int ReadInt(char *&text, bool aSkipSeparator=true)
Read an integer from an ASCII character buffer.