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