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-2023 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#include <trigo.h> // for RotatePoint
67
68#include <gerbview.h>
69#include <gerbview_frame.h>
70#include <gerber_file_image.h>
72#include <excellon_image.h>
73#include <excellon_defaults.h>
74#include <macros.h>
75#include <string_utils.h>
76#include <locale_io.h>
78#include <view/view.h>
79#include <gerbview_settings.h>
80
81#include <cmath>
82
84
85// A helper function to calculate the arc center of an arc
86// known by 2 end points, the radius, and the angle direction (CW or CCW)
87// Arc angles are <= 180 degrees in circular interpol.
88static VECTOR2I computeCenter( VECTOR2I aStart, VECTOR2I aEnd, int& aRadius, bool aRotCCW )
89{
90 VECTOR2I center;
91 VECTOR2D end;
92 end.x = double(aEnd.x - aStart.x);
93 end.y = double(aEnd.y - aStart.y);
94
95 // Be sure aRadius/2 > dist between aStart and aEnd
96 double min_radius = end.EuclideanNorm() * 2;
97
98 if( min_radius <= aRadius )
99 {
100 // Adjust the radius and the arc center for a 180 deg arc between end points
101 aRadius = KiROUND( min_radius );
102 center.x = ( aStart.x + aEnd.x + 1 ) / 2;
103 center.y = ( aStart.y + aEnd.y + 1 ) / 2;
104 return center;
105 }
106
107 /* to compute the centers position easily:
108 * rotate the segment (0,0 to end.x,end.y) to make it horizontal (end.y = 0).
109 * the X center position is end.x/2
110 * the Y center positions are on the vertical line starting at end.x/2, 0
111 * and solve aRadius^2 = X^2 + Y^2 (2 values)
112 */
113 EDA_ANGLE seg_angle( end );
114 VECTOR2D h_segm = end;
115 RotatePoint( h_segm, seg_angle );
116 double cX = h_segm.x/2;
117 double cY1 = sqrt( (double)aRadius*aRadius - cX*cX );
118 double cY2 = -cY1;
119 VECTOR2D center1( cX, cY1 );
120 RotatePoint( center1, -seg_angle );
121 EDA_ANGLE arc_angle1 = EDA_ANGLE( end - center1 ) - EDA_ANGLE( VECTOR2D( 0.0, 0.0 ) - center1 );
122 VECTOR2D center2( cX, cY2 );
123 RotatePoint( center2, -seg_angle );
124 EDA_ANGLE arc_angle2 = EDA_ANGLE( end - center2 ) - EDA_ANGLE( VECTOR2D( 0.0, 0.0 ) - center2 );
125
126 if( !aRotCCW )
127 {
128 if( arc_angle1 < ANGLE_0 )
129 arc_angle1 += ANGLE_360;
130
131 if( arc_angle2 < ANGLE_0 )
132 arc_angle2 += ANGLE_360;
133 }
134 else
135 {
136 if( arc_angle1 > ANGLE_0 )
137 arc_angle1 -= ANGLE_360;
138
139 if( arc_angle2 > ANGLE_0 )
140 arc_angle2 -= ANGLE_360;
141 }
142
143 // Arc angle must be <= 180.0 degrees.
144 // So choose the center that create a arc angle <= 180.0
145 if( std::abs( arc_angle1 ) <= ANGLE_180 )
146 {
147 center.x = KiROUND( center1.x );
148 center.y = KiROUND( center1.y );
149 }
150 else
151 {
152 center.x = KiROUND( center2.x );
153 center.y = KiROUND( center2.y );
154 }
155
156 return center+aStart;
157}
158
159extern int ReadInt( char*& text, bool aSkipSeparator = true );
160extern double ReadDouble( char*& text, bool aSkipSeparator = true );
161
162// See rs274d.cpp:
163extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
164 APERTURE_T aAperture,
165 int Dcode_index,
166 const VECTOR2I& aPos,
167 VECTOR2I aSize,
168 bool aLayerNegative );
169
170extern void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
171 int Dcode_index,
172 const VECTOR2I& aStart,
173 const VECTOR2I& aEnd,
174 VECTOR2I aPenSize,
175 bool aLayerNegative );
176
177extern void fillArcGBRITEM( GERBER_DRAW_ITEM* aGbrItem,
178 int Dcode_index,
179 const VECTOR2I& aStart,
180 const VECTOR2I& aEnd,
181 const VECTOR2I& aRelCenter,
182 VECTOR2I aPenSize,
183 bool aClockwise,
184 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, similar to a Gerber drill file
191static 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_ENDFILE, -1 }, // End of File (last line of NC drill)
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 { ";FILE_FORMAT",
217 DRILL_FORMAT_ALTIUM, 1 }, // Use Format command
218 { ";", DRILL_HEADER_SKIP, 0 }, // Other ; hints that we don't implement
219 { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 },
220 { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop
221 { "AFS", DRILL_AUTOMATIC_SPEED, 0 }, // Automatic Feeds and Speeds
222 { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version
223 { "R", DRILL_RESET_CMD, -1 }, // Reset commands
224 { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header
225 { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header
226 // Keep this item after all commands starting by 'T':
227 { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information
228 { "", DRILL_M_UNKNOWN, 0 } // last item in list
229};
230
232{
233 { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode
234 { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode
235 { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode
236 { "G00", DRILL_G_ROUT, 1 }, // Route Mode
237 { "G05", DRILL_G_DRILL, 0 }, // Drill Mode
238 { "G85", DRILL_G_SLOT, 0 }, // Canned Mode slot (oval holes)
239 { "G01", DRILL_G_LINEARMOVE, 1 }, // Linear (Straight Line) routing Mode
240 { "G02", DRILL_G_CWMOVE, 1 }, // Circular CW Mode
241 { "G03", DRILL_G_CCWMOVE, 1 }, // Circular CCW Mode
242 { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordinates origin)
243 { "", DRILL_G_UNKNOWN, 0 }, // last item in list
244};
245
246
247bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName )
248{
249 wxString msg;
250 int layerId = GetActiveLayer(); // current layer used in GerbView
252 GERBER_FILE_IMAGE* gerber_layer = images->GetGbrImage( layerId );
253
254 // If the active layer contains old gerber or nc drill data, remove it
255 if( gerber_layer )
257
258 std::unique_ptr<EXCELLON_IMAGE> drill_layer_uptr = std::make_unique<EXCELLON_IMAGE>( layerId );
259
260 EXCELLON_DEFAULTS nc_defaults;
261 GERBVIEW_SETTINGS* cfg = static_cast<GERBVIEW_SETTINGS*>( config() );
262 cfg->GetExcellonDefaults( nc_defaults );
263
264 // Read the Excellon drill file:
265 bool success = drill_layer_uptr->LoadFile( aFullFileName, &nc_defaults );
266
267 if( !success )
268 {
269 drill_layer_uptr.reset();
270 msg.Printf( _( "File %s not found." ), aFullFileName );
271 ShowInfoBarError( msg );
272 return false;
273 }
274
275 EXCELLON_IMAGE* drill_layer = drill_layer_uptr.release();
276
277 layerId = images->AddGbrImage( drill_layer, layerId );
278
279 if( layerId < 0 )
280 {
281 delete drill_layer;
282 ShowInfoBarError( _( "No empty layers to load file into." ) );
283 return false;
284 }
285
286 // Display errors list
287 if( drill_layer->GetMessages().size() > 0 )
288 {
289 HTML_MESSAGE_BOX dlg( this, _( "Error reading EXCELLON drill file" ) );
290 dlg.ListSet( drill_layer->GetMessages() );
291 dlg.ShowModal();
292 }
293
294 if( GetCanvas() )
295 {
296 for( GERBER_DRAW_ITEM* item : drill_layer->GetItems() )
297 GetCanvas()->GetView()->Add( (KIGFX::VIEW_ITEM*) item );
298 }
299
300 return success;
301}
302
303
305{
307 SelectUnits( false, nullptr ); // Default unit = inch
308 m_hasFormat = false; // will be true if a Altium file containing
309 // the nn:mm file format is read
310
311 // Files using non decimal can use No Trailing zeros or No leading Zeros
312 // Unfortunately, the identifier (INCH,TZ or INCH,LZ for instance) is not
313 // always set in drill files.
314 // The option leading zeros looks like more frequent, so use this default
315 m_NoTrailingZeros = true;
316}
317
318
319/*
320 * Original function derived from drill_file_p() of gerbv 2.7.0.
321 * Copyright of the source file drill.cpp included below:
322 */
323/*
324 * gEDA - GNU Electronic Design Automation
325 * drill.c
326 * Copyright (C) 2000-2006 Andreas Andersson
327 *
328 * $Id$
329 *
330 * This program is free software; you can redistribute it and/or modify
331 * it under the terms of the GNU General Public License as published by
332 * the Free Software Foundation; either version 2 of the License, or
333 * (at your option) any later version.
334 *
335 * This program is distributed in the hope that it will be useful,
336 * but WITHOUT ANY WARRANTY; without even the implied warranty of
337 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
338 * GNU General Public License for more details.
339 *
340 * You should have received a copy of the GNU General Public License
341 * along with this program; if not, write to the Free Software
342 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
343 */
344bool EXCELLON_IMAGE::TestFileIsExcellon( const wxString& aFullFileName )
345{
346 char* letter;
347 bool foundM48 = false;
348 bool foundM30 = false;
349 bool foundPercent = false;
350 bool foundT = false;
351 bool foundX = false;
352 bool foundY = false;
353
354 FILE* file = wxFopen( aFullFileName, "rb" );
355
356 if( file == nullptr )
357 return false;
358
359 FILE_LINE_READER excellonReader( file, aFullFileName );
360
361 try
362 {
363 while( true )
364 {
365 if( excellonReader.ReadLine() == nullptr )
366 break;
367
368 // Remove all whitespace from the beginning and end
369 char* line = StrPurge( excellonReader.Line() );
370
371 // Skip empty lines
372 if( *line == 0 )
373 continue;
374
375 // Check that file is not binary (non-printing chars)
376 for( size_t i = 0; i < strlen( line ); i++ )
377 {
378 if( !isascii( line[i] ) )
379 return false;
380 }
381
382 // We don't want to look for any commands after a comment so
383 // just end the line early if we find a comment
384 char* buf = strstr( line, ";" );
385 if( buf != nullptr )
386 *buf = 0;
387
388 // Check for M48 = start of drill header
389 if( strstr( line, "M48" ) )
390 foundM48 = true;
391
392 // Check for M30 = end of drill program
393 if( strstr( line, "M30" ) )
394 if( foundPercent )
395 foundM30 = true; // Found M30 after % = good
396
397 // Check for % on its own line at end of header
398 if( ( letter = strstr( line, "%" ) ) != nullptr )
399 if( ( letter[1] == '\r' ) || ( letter[1] == '\n' ) )
400 foundPercent = true;
401
402 // Check for T<number>
403 if( ( letter = strstr( line, "T" ) ) != nullptr )
404 {
405 if( !foundT && ( foundX || foundY ) )
406 foundT = false; /* Found first T after X or Y */
407 else
408 {
409 // verify next char is digit
410 if( isdigit( letter[1] ) )
411 foundT = true;
412 }
413 }
414
415 // look for X<number> or Y<number>
416 if( ( letter = strstr( line, "X" ) ) != nullptr )
417 if( isdigit( letter[1] ) )
418 foundX = true;
419
420 if( ( letter = strstr( line, "Y" ) ) != nullptr )
421 if( isdigit( letter[1] ) )
422 foundY = true;
423 }
424 }
425 catch( IO_ERROR& )
426 {
427 return false;
428 }
429
430
431 /* Now form logical expression determining if this is a drill file */
432 if( ( ( foundX || foundY ) && foundT ) && ( foundM48 || ( foundPercent && foundM30 ) ) )
433 return true;
434 else if( foundM48 && foundT && foundPercent && foundM30 )
435 /* Pathological case of drill file with valid header
436 and EOF but no drill XY locations. */
437 return true;
438
439 return false;
440}
441
442
443/*
444 * Read a EXCELLON file.
445 * Gerber classes are used because there is likeness between Gerber files
446 * and Excellon files
447 * DCode can easily store T code (tool size) as round (or oval) shape
448 * Drill commands are similar to flashed gerber items
449 * Routing commands are similar to Gerber polygons
450 * coordinates have the same format as Gerber, can be given in:
451 * decimal format (i.i. floating notation format)
452 * integer 2.4 format in imperial units,
453 * integer 3.2 or 3.3 format (metric units).
454 */
455bool EXCELLON_IMAGE::LoadFile( const wxString & aFullFileName, EXCELLON_DEFAULTS* aDefaults )
456{
457 // Set the default parameter values:
460
461 m_Current_File = wxFopen( aFullFileName, wxT( "rt" ) );
462
463 if( m_Current_File == nullptr )
464 return false;
465
466 // Initial format setting, usualy defined in file, but not always...
467 m_NoTrailingZeros = aDefaults->m_LeadingZero;
468 m_GerbMetric = aDefaults->m_UnitsMM;
469
470 wxString msg;
471 m_FileName = aFullFileName;
472
473 LOCALE_IO toggleIo;
474
475 // FILE_LINE_READER will close the file.
476 FILE_LINE_READER excellonReader( m_Current_File, m_FileName );
477
478 while( true )
479 {
480 if( excellonReader.ReadLine() == nullptr )
481 break;
482
483 char* line = excellonReader.Line();
484 char* text = StrPurge( line );
485
486 if( *text == 0 ) // Skip empty lines
487 continue;
488
490 {
492
493 // Now units (inch/mm) are known, set the coordinate format
494 SelectUnits( m_GerbMetric, aDefaults );
495 }
496 else
497 {
498 switch( *text )
499 {
500 case ';':
501 case 'M':
503 break;
504
505 case 'G': // Line type Gxx : command
507 break;
508
509 case 'X':
510 case 'Y': // command like X12550Y19250
512 break;
513
514 case 'I':
515 case 'J': /* Auxiliary Move command */
517 if( *text == '*' ) // command like X35142Y15945J504
518 {
520 }
521 break;
522
523 case 'T': // Select Tool command (can also create
524 // the tool with an embedded definition)
525 Select_Tool( text );
526 break;
527
528 case '%':
529 break;
530
531 default:
532 msg.Printf( wxT( "Unexpected symbol 0x%2.2X &lt;%c&gt;" ), *text, *text );
533 AddMessageToList( msg );
534 break;
535 } // End switch
536 }
537 }
538
539 // Add our file attribute, to identify the drill file
541 char* text = (char*)file_attribute;
542 int dummyline = 0;
543 dummy.ParseAttribCmd( nullptr, nullptr, 0, text, dummyline );
544 delete m_FileFunction;
546
547 m_InUse = true;
548
549 return true;
550}
551
552
554{
555 EXCELLON_CMD* cmd = nullptr;
556 wxString msg;
557
558 // Search command in list
559 for( unsigned ii = 0; ; ii++ )
560 {
561 EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
562 int len = candidate->m_Name.size();
563
564 if( len == 0 ) // End of list reached
565 break;
566
567 if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
568 {
569 cmd = candidate;
570 text += len;
571 break;
572 }
573 }
574
575 if( !cmd )
576 {
577 msg.Printf( _( "Unknown Excellon command &lt;%s&gt;" ), text );
578 AddMessageToList( msg );
579 while( *text )
580 text++;
581
582 return false;
583 }
584
585 // Execute command
586 // some do nothing
587 switch( cmd->m_Code )
588 {
589 case DRILL_SKIP:
590 case DRILL_M_UNKNOWN:
591 break;
592
593 case DRILL_M_END:
594 case DRILL_M_ENDFILE:
595 // if a route command is in progress, finish it
596 if( m_RouteModeOn )
598
599 break;
600
601 case DRILL_M_MESSAGE:
602 break;
603
605 break;
606
607 case DRILL_M_HEADER:
609 break;
610
613 break;
614
615 case DRILL_REWIND_STOP: // End of header. No action in a viewer
617 break;
618
621 break;
622
624 break;
625
626 case DRILL_M_METRIC:
627 SelectUnits( true, nullptr );
628 break;
629
630 case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
631 case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
632 SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false, nullptr );
633
634 if( *text != ',' )
635 {
636 // No TZ or LZ specified. Should be a decimal format
637 // but this is not always the case. Use our default setting
638 break;
639 }
640
641 text++; // skip separator
642 if( *text == 'T' )
643 m_NoTrailingZeros = false;
644 else
645 m_NoTrailingZeros = true;
646 break;
647
649 break;
650
652 break;
653
655 break;
656
657 case DRILL_M_TIPCHECK:
658 break;
659
661 break;
662
664 if( *text != ',' )
665 {
666 AddMessageToList( wxT( "ICI command has no parameter" ) );
667 break;
668 }
669 text++; // skip separator
670 // Parameter should be ON or OFF
671 if( strncasecmp( text, "OFF", 3 ) == 0 )
672 m_Relative = false;
673 else if( strncasecmp( text, "ON", 2 ) == 0 )
674 m_Relative = true;
675 else
676 AddMessageToList( wxT( "ICI command has incorrect parameter" ) );
677 break;
678
680 break;
681
683 break;
684
686 break;
687
688 case DRILL_RESET_CMD:
689 break;
690
692 break;
693
694 case DRILL_FMT:
695 break;
696
699 break;
700
701 case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
702 // Only the last position is useful:
703 if( m_RoutePositions.size() > 1 )
704 m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
705
706 break;
707
708 case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
710 break;
711 }
712
713 while( *text )
714 text++;
715
716 return true;
717}
718
719
721{
722 int mantissaDigits = 0;
723 int characteristicDigits = 0;
724
725 // Example String: ;FILE_FORMAT=4:4
726 // The ;FILE_FORMAT potion will already be stripped off.
727 // Parse the rest strictly as single_digit:single_digit like 4:4 or 2:4
728 // Don't allow anything clever like spaces or multiple digits
729 if( *aText != '=' )
730 return;
731
732 aText++;
733
734 if( !isdigit( *aText ) )
735 return;
736
737 characteristicDigits = *aText - '0';
738 aText++;
739
740 if( *aText != ':' )
741 return;
742
743 aText++;
744
745 if( !isdigit( *aText ) )
746 return;
747
748 mantissaDigits = *aText - '0';
749
750 m_hasFormat = true;
751 m_FmtLen.x = m_FmtLen.y = characteristicDigits + mantissaDigits;
752 m_FmtScale.x = m_FmtScale.y = mantissaDigits;
753}
754
755
757{
758 // Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
759 // and enter the TCODE param in list (using the D_CODE param management, which
760 // is similar to TCODE params.
761 if( *aText == 'T' ) // This is the beginning of the definition
762 aText++;
763
764 // Read tool number:
765 int iprm = ReadInt( aText, false );
766
767 // Skip Feed rate and Spindle speed, if any here
768 while( *aText && ( *aText == 'F' || *aText == 'S' ) )
769 {
770 aText++;
771 ReadInt( aText, false );
772 }
773
774 // Read tool shape
775 if( ! *aText )
777 _( "Tool definition shape not found" ) ) );
778 else if( *aText != 'C' )
780 _( "Tool definition '%c' not supported" ), *aText ) );
781 if( *aText )
782 aText++;
783
784 //read tool diameter:
785 double dprm = ReadDouble( aText, false );
786 m_Has_DCode = true;
787
788 // Initialize Dcode to handle this Tool
789 // Remember: dcodes are >= FIRST_DCODE
790 D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
791
792 if( dcode == nullptr )
793 return false;
794
795 // conv_scale = scaling factor from inch to Internal Unit
796 double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
797
798 if( m_GerbMetric )
799 conv_scale /= 25.4;
800
801 dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
802 dcode->m_ApertType = APT_CIRCLE;
803 dcode->m_Defined = true;
804
805 return true;
806}
807
808
810{
811 D_CODE* tool;
812 GERBER_DRAW_ITEM * gbritem;
813
814 while( true )
815 {
816 switch( *text )
817 {
818 case 'X':
819 case 'Y':
820 ReadXYCoord( text, true );
821
822 if( *text == 'I' || *text == 'J' )
823 ReadIJCoord( text );
824
825 break;
826
827 case 'G': // G85 is found here for oval holes
830 break;
831
832 case 0: // E.O.L: execute command
833 if( m_RouteModeOn )
834 {
835 // We are in routing mode, and this is an intermediate point.
836 // So just store it
837 int rmode = 0; // linear routing.
838
840 rmode = ROUTE_CW;
842 rmode = ROUTE_CCW;
843
845 {
847 m_RoutePositions.push_back( point );
848 }
849 else
850 {
852 m_RoutePositions.push_back( point );
853 }
854 return true;
855 }
856
857 tool = GetDCODE( m_Current_Tool );
858 if( !tool )
859 {
860 wxString msg;
861 msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
862 AddMessageToList( msg );
863 return false;
864 }
865
866 gbritem = new GERBER_DRAW_ITEM( this );
867 AddItemToList( gbritem );
868
869 if( m_SlotOn ) // Oblong hole
870 {
871 fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
873 tool->m_Size, false );
874 // the hole is made: reset the slot on command (G85)
875 // (it is needed for each oblong hole)
876 m_SlotOn = false;
877 }
878 else
879 {
880 fillFlashedGBRITEM( gbritem, tool->m_ApertType, tool->m_Num_Dcode,
881 m_CurrentPos, tool->m_Size, false );
882 }
883
884 StepAndRepeatItem( *gbritem );
886 return true;
887 break;
888
889 default:
890 text++;
891 break;
892 }
893 }
894
895 return true;
896}
897
898
900{
901 // Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
902 // Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
903 // in tool selection command, if the tool is not defined in list,
904 // and the definition is embedded, it will be entered in list
905 char * startline = text; // the tool id starts here.
906 int tool_id = CodeNumber( text );
907
908 // T0 is legal, but is not a selection tool. it is a special command
909 if( tool_id >= 0 )
910 {
911 int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
912
913 if( dcode_id > (TOOLS_MAX_COUNT - 1) )
914 dcode_id = TOOLS_MAX_COUNT - 1;
915
916 m_Current_Tool = dcode_id;
917 D_CODE* currDcode = GetDCODE( dcode_id );
918
919 // if the tool does not exist, and the definition is embedded, create this tool
920 if( currDcode == nullptr && tool_id > 0 )
921 {
922 text = startline; // text starts at the beginning of the command
924 currDcode = GetDCODE( dcode_id );
925 }
926
927 // If the Tool is still not existing, create a dummy tool
928 if( !currDcode )
929 currDcode = GetDCODEOrCreate( dcode_id, true );
930
931 if( currDcode )
932 currDcode->m_InUse = true;
933 }
934
935 while( *text )
936 text++;
937
938 return tool_id >= 0;
939}
940
941
943{
944 EXCELLON_CMD* cmd = nullptr;
945 bool success = false;
946 int id = DRILL_G_UNKNOWN;
947
948 // Search command in list
949 EXCELLON_CMD* candidate;
950 char * gcmd = text; // gcmd points the G command, for error messages.
951
952 for( unsigned ii = 0; ; ii++ )
953 {
954 candidate = &excellon_G_CmdList[ii];
955 int len = candidate->m_Name.size();
956 if( len == 0 ) // End of list reached
957 break;
958 if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
959 {
960 cmd = candidate;
961 text += len;
962 success = true;
963 id = cmd->m_Code;
964 break;
965 }
966 }
967
968 switch( id )
969 {
970 case DRILL_G_ZERO_SET:
971 ReadXYCoord( text, true );
973 break;
974
975 case DRILL_G_ROUT:
976 m_SlotOn = false;
977
978 if( m_RouteModeOn )
980
981 m_RouteModeOn = true;
982 m_RoutePositions.clear();
984 ReadXYCoord( text, true );
985 // This is the first point (starting point) of routing
986 m_RoutePositions.emplace_back( m_CurrentPos );
987 break;
988
989 case DRILL_G_DRILL:
990 m_SlotOn = false;
991
992 if( m_RouteModeOn )
994
995 m_RouteModeOn = false;
996 m_RoutePositions.clear();
998 break;
999
1000 case DRILL_G_SLOT:
1001 m_SlotOn = true;
1002 break;
1003
1004 case DRILL_G_LINEARMOVE:
1007 ReadXYCoord( text, true );
1008 m_RoutePositions.emplace_back( m_CurrentPos );
1009 break;
1010
1011 case DRILL_G_CWMOVE:
1013 ReadXYCoord( text, true );
1014
1015 if( *text == 'I' || *text == 'J' )
1016 ReadIJCoord( text );
1017
1020 else
1022 break;
1023
1024 case DRILL_G_CCWMOVE:
1026 ReadXYCoord( text, true );
1027
1028 if( *text == 'I' || *text == 'J' )
1029 ReadIJCoord( text );
1030
1033 else
1035 break;
1036
1037 case DRILL_G_ABSOLUTE:
1038 m_Relative = false; // false = absolute coord
1039 break;
1040
1042 m_Relative = true; // true = relative coord
1043 break;
1044
1045 case DRILL_G_UNKNOWN:
1046 default:
1047 AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: &lt;%s&gt;" ), FROM_UTF8(gcmd) ) );
1048 while( *text )
1049 text++;
1050 return false;
1051 }
1052
1053 return success;
1054}
1055
1056void EXCELLON_IMAGE::SelectUnits( bool aMetric, EXCELLON_DEFAULTS* aDefaults )
1057{
1058 /* Coordinates are measured either in inch or metric (millimeters).
1059 * Inch coordinates are in six digits (00.0000) with increments
1060 * as small as 0.0001 (1/10,000).
1061 * Metric coordinates can be measured in microns (thousandths of a millimeter)
1062 * in one of the following three ways:
1063 * Five digit 10 micron resolution (000.00)
1064 * Six digit 10 micron resolution (0000.00)
1065 * Six digit micron resolution (000.000)
1066 *
1067 * Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
1068 * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
1069 *
1070 * However some drill files do not use standard values.
1071 */
1072 if( aMetric )
1073 {
1074 m_GerbMetric = true;
1075
1076 if( !m_hasFormat )
1077 {
1078 if( aDefaults )
1079 {
1080 // number of digits in mantissa
1081 m_FmtScale.x = m_FmtScale.y = aDefaults->m_MmMantissaLen;
1082 // number of digits (mantissa+integer)
1083 m_FmtLen.x = m_FmtLen.y = aDefaults->m_MmIntegerLen
1084 + aDefaults->m_MmMantissaLen;
1085 }
1086 else
1087 {
1090 }
1091 }
1092 }
1093 else
1094 {
1095 m_GerbMetric = false;
1096
1097 if( !m_hasFormat )
1098 {
1099 if( aDefaults )
1100 {
1101 m_FmtScale.x = m_FmtScale.y = aDefaults->m_InchMantissaLen;
1102 m_FmtLen.x = m_FmtLen.y = aDefaults->m_InchIntegerLen
1103 + aDefaults->m_InchMantissaLen;
1104 }
1105 else
1106 {
1109 }
1110 }
1111 }
1112}
1113
1114
1116{
1117 // Ends a route command started by M15 ot G01, G02 or G03 command
1118 // if a route command is not in progress, do nothing
1119
1120 if( !m_RouteModeOn )
1121 return;
1122
1123 D_CODE* tool = GetDCODE( m_Current_Tool );
1124
1125 if( !tool )
1126 {
1127 AddMessageToList( wxString::Format( wxT( "Unknown tool code %d" ), m_Current_Tool ) );
1128 return;
1129 }
1130
1131 for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
1132 {
1133 GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
1134
1135 if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
1136 {
1137 fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
1138 m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1139 tool->m_Size, false );
1140 }
1141 else // circular (cw or ccw) routing
1142 {
1143 bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
1144 int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
1145 VECTOR2I center;
1146
1147 if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
1148 center = VECTOR2I( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
1149 else
1150 center = computeCenter( m_RoutePositions[ii-1].GetPos(),
1151 m_RoutePositions[ii].GetPos(), radius, rot_ccw );
1152
1153 fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
1154 m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1155 center - m_RoutePositions[ii-1].GetPos(),
1156 tool->m_Size, not rot_ccw , true,
1157 false );
1158 }
1159
1160 AddItemToList( gbritem );
1161
1162 StepAndRepeatItem( *gbritem );
1163 }
1164
1165 m_RoutePositions.clear();
1166 m_RouteModeOn = false;
1167}
constexpr EDA_IU_SCALE gerbIUScale
Definition: base_units.h:108
A gerber DCODE (also called Aperture) definition.
Definition: dcode.h:80
int m_Num_Dcode
D code value ( >= 10 )
Definition: dcode.h:193
VECTOR2I m_Size
Horizontal and vertical dimensions.
Definition: dcode.h:190
APERTURE_T m_ApertType
Aperture type ( Line, rectangle, circle, oval poly, macro )
Definition: dcode.h:191
bool m_Defined
false if the aperture is not defined in the header
Definition: dcode.h:202
bool m_InUse
false if the aperture (previously defined) is not used to draw something
Definition: dcode.h:200
virtual APP_SETTINGS_BASE * config() const
Returns the settings object used in SaveSettings(), and is overloaded in KICAD_MANAGER_FRAME.
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...
virtual EDA_DRAW_PANEL_GAL * GetCanvas() const
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual KIGFX::VIEW * GetView() const
Return a pointer to the #VIEW instance used in the panel.
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...
static bool TestFileIsExcellon(const wxString &aFullFileName)
Performs a heuristics-based check of whether the file is an Excellon drill file.
bool LoadFile(const wxString &aFullFileName, EXCELLON_DEFAULTS *aDefaults)
Read and load a drill (EXCELLON format) file.
bool Execute_Drill_Command(char *&text)
void FinishRouteCommand()
End a route command started by M15 ot G01, G02 or G03 command.
EXCELLON_STATE m_State
bool Execute_HEADER_And_M_Command(char *&text)
bool Execute_EXCELLON_G_Command(char *&text)
bool m_hasFormat
Excellon file do not have a format statement to specify the coordinate format like nn:mm.
bool readToolInformation(char *&aText)
Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00 and enter params in TCODE list.
virtual void ResetDefaultValues() override
Set all parameters to a default value, before reading a file.
std::vector< EXCELLON_ROUTE_COORD > m_RoutePositions
bool Select_Tool(char *&text)
void SelectUnits(bool aMetric, EXCELLON_DEFAULTS *aDefaults)
Switch unit selection, and the coordinate format (nn:mm) if not yet set.
A LINE_READER that reads from an open file.
Definition: richio.h:173
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
Definition: richio.cpp:219
GERBER_FILE_IMAGE_LIST * GetImagesList() const
Definition: gbr_layout.cpp:41
GERBER_FILE_IMAGE_LIST is a helper class to handle a list of GERBER_FILE_IMAGE files which are loaded...
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.
GERBER_FILE_IMAGE * GetGbrImage(int aIdx)
Hold the image data and parameters for one gerber file and layer parameters.
VECTOR2I ReadIJCoord(char *&Text)
Return the current coordinate type pointed to by InnJnn Text (InnnnJmmmm)
X2_ATTRIBUTE_FILEFUNCTION * m_FileFunction
file function parameters, found in a TF command or a G04
virtual void ResetDefaultValues()
Set all parameters to a default value, before reading a file.
const wxArrayString & GetMessages() const
void ClearMessageList()
Clear the message list.
VECTOR2I m_Offset
Coord Offset, from OF command.
wxSize m_FmtScale
Fmt 2.3: m_FmtScale = 3, fmt 3.4: m_FmtScale = 4.
int m_ArcRadius
Identifier for arc data type (IJ (center) or A## (radius)).
wxString m_FileName
Full File Name for this layer.
void StepAndRepeatItem(const GERBER_DRAW_ITEM &aItem)
Gerber format has a command Step an Repeat.
VECTOR2I m_PreviousPos
old current specified coord for plot
bool m_InUse
true if this image is currently in use (a file is loaded in it) false if it must be not drawn
void AddMessageToList(const wxString &aMessage)
Add a message to the message list.
LAST_EXTRA_ARC_DATA_TYPE m_LastArcDataType
VECTOR2I m_IJPos
IJ coord (for arcs & circles )
bool m_Relative
false = absolute Coord, true = relative Coord.
D_CODE * GetDCODE(int aDCODE) const
Return a pointer to the D_CODE within this GERBER for the given aDCODE.
bool m_Has_DCode
< True if has DCodes in file or false if no DCodes found. Perhaps deprecated RS274D file.
int m_Current_Tool
Current Tool (Dcode) number selected.
D_CODE * GetDCODEOrCreate(int aDCODE, bool aCreateIfNoExist=true)
Return a pointer to the D_CODE within this GERBER for the given aDCODE.
void AddItemToList(GERBER_DRAW_ITEM *aItem)
Add a new GERBER_DRAW_ITEM item to the drawings list.
VECTOR2I ReadXYCoord(char *&aText, bool aExcellonMode=false)
Return the current coordinate type pointed to by XnnYnn Text (XnnnnYmmmm).
wxSize m_FmtLen
Nb chars per coord. ex fmt 2.3, m_FmtLen = 5.
bool m_GerbMetric
false = Inches, true = metric
bool m_NoTrailingZeros
true: remove tailing zeros.
int m_Iterpolation
Linear, 90 arc, Circ.
VECTOR2I m_CurrentPos
current specified coord for plot
GERBER_DRAW_ITEMS & GetItems()
int CodeNumber(char *&aText)
Reads the next number and returns the value.
Definition: rs274d.cpp:401
bool Read_EXCELLON_File(const wxString &aFullFileName)
GBR_LAYOUT * GetGerberLayout() const
int GetActiveLayer() const
Return the active layer.
void Erase_Current_DrawLayer(bool query)
void GetExcellonDefaults(EXCELLON_DEFAULTS &aNCDefaults)
return the Excellon default values to read a drill file
void ListSet(const wxString &aList)
Add a list of items.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:77
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:316
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
X2_ATTRIBUTE_FILEFUNCTION ( from TF.FileFunction in Gerber file) Example file function: TF....
The attribute value consists of a number of substrings separated by a comma.
APERTURE_T
The set of all gerber aperture types allowed from ADD dcode command, like ADD11C,0....
Definition: dcode.h:48
@ APT_CIRCLE
Definition: dcode.h:49
#define FIRST_DCODE
Definition: dcode.h:69
#define TOOLS_MAX_COUNT
Definition: dcode.h:71
#define _(s)
static constexpr EDA_ANGLE & ANGLE_180
Definition: eda_angle.h:433
static constexpr EDA_ANGLE & ANGLE_360
Definition: eda_angle.h:435
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
#define FMT_MANTISSA_INCH
#define FMT_INTEGER_MM
#define FMT_MANTISSA_MM
#define FMT_INTEGER_INCH
#define ROUTE_CW
#define ROUTE_CCW
@ DRILL_G_CWMOVE
@ DRILL_G_ZERO_SET
@ DRILL_G_INCREMENTAL
@ DRILL_G_ZEROSET
@ DRILL_G_DRILL
@ DRILL_G_LINEARMOVE
@ DRILL_G_ABSOLUTE
@ DRILL_G_SLOT
@ DRILL_G_ROUT
@ DRILL_G_UNKNOWN
@ DRILL_G_CCWMOVE
@ DRILL_M_UNKNOWN
@ DRILL_M_TOOL_UP
@ DRILL_SKIP
@ DRILL_M_ENDPATTERN
@ DRILL_M_TOOL_DOWN
@ DRILL_AUTOMATIC_SPEED
@ DRILL_M_END
@ DRILL_TOOL_CHANGE_STOP
@ DRILL_TOOL_INFORMATION
@ DRILL_AUTOMATIC_TOOL_CHANGE
@ DRILL_M_ENDHEADER
@ DRILL_M_HEADER
@ DRILL_FORMAT_ALTIUM
@ DRILL_M_METRIC
@ DRILL_DETECT_BROKEN
@ DRILL_HEADER_SKIP
@ DRILL_REWIND_STOP
@ DRILL_M_TIPCHECK
@ DRILL_M_LONGMESSAGE
@ DRILL_INCREMENTALHEADER
@ DRILL_AXIS_VERSION
@ DRILL_RESET_CMD
@ DRILL_M_MESSAGE
@ DRILL_METRIC_HEADER
@ DRILL_M_BEGINPATTERN
@ DRILL_IMPERIAL_HEADER
@ DRILL_M_ENDFILE
@ DRILL_FMT
@ DRILL_M_CANNEDTEXT
@ DRILL_M_IMPERIAL
static EXCELLON_CMD excellonHeaderCmdList[]
void fillFlashedGBRITEM(GERBER_DRAW_ITEM *aGbrItem, APERTURE_T aAperture, int Dcode_index, const VECTOR2I &aPos, VECTOR2I 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
static EXCELLON_CMD excellon_G_CmdList[]
static const char file_attribute[]
void fillArcGBRITEM(GERBER_DRAW_ITEM *aGbrItem, int Dcode_index, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aRelCenter, VECTOR2I aPenSize, bool aClockwise, bool aMultiquadrant, bool aLayerNegative)
Initialize a given GBRITEM so that it can draw an arc G code.
Definition: rs274d.cpp:202
int ReadInt(char *&text, bool aSkipSeparator=true)
Read an integer from an ASCII character buffer.
static VECTOR2I computeCenter(VECTOR2I aStart, VECTOR2I aEnd, int &aRadius, bool aRotCCW)
double ReadDouble(char *&text, bool aSkipSeparator=true)
Read a double precision floating point number from an ASCII character buffer.
void fillLineGBRITEM(GERBER_DRAW_ITEM *aGbrItem, int Dcode_index, const VECTOR2I &aStart, const VECTOR2I &aEnd, VECTOR2I aPenSize, bool aLayerNegative)
Initialize a given GBRITEM so that it can draw a linear D code.
Definition: rs274d.cpp:153
@ ARC_INFO_TYPE_NONE
@ ARC_INFO_TYPE_CENTER
@ GERB_INTERPOL_ARC_NEG
Definition: gerbview.h:36
@ GERB_INTERPOL_LINEAR_1X
Definition: gerbview.h:35
@ GERB_INTERPOL_ARC_POS
Definition: gerbview.h:37
This file contains miscellaneous commonly used macros and functions.
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
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
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
std::vector< FAB_LAYER_COLOR > dummy
char * StrPurge(char *text)
Remove leading and training spaces, tabs and end of line chars in text.
const double IU_PER_MILS
Definition: base_units.h:78
std::string m_Name
management of default values used to read a Excellon (.nc) drill file Some important parameters are n...
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
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:85
VECTOR2< double > VECTOR2D
Definition: vector2d.h:589
VECTOR2< int > VECTOR2I
Definition: vector2d.h:590