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