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