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 'R': // Repeat hole command R#(X#Y#)
516 break;
517
518 case 'X':
519 case 'Y': // command like X12550Y19250
521 break;
522
523 case 'I':
524 case 'J': /* Auxiliary Move command */
526 if( *text == '*' ) // command like X35142Y15945J504
527 {
529 }
530 break;
531
532 case 'T': // Select Tool command (can also create
533 // the tool with an embedded definition)
534 Select_Tool( text );
535 break;
536
537 case '%':
538 break;
539
540 default:
541 msg.Printf( wxT( "Unexpected symbol 0x%2.2X &lt;%c&gt;" ), *text, *text );
542 AddMessageToList( msg );
543 break;
544 } // End switch
545 }
546 }
547
548 // Add our file attribute, to identify the drill file
550 char* text = (char*)file_attribute;
551 int dummyline = 0;
552 dummy.ParseAttribCmd( nullptr, nullptr, 0, text, dummyline );
553 delete m_FileFunction;
555
556 m_InUse = true;
557
558 return true;
559}
560
561
563{
564 EXCELLON_CMD* cmd = nullptr;
565 wxString msg;
566
567 // Search command in list
568 for( unsigned ii = 0; ; ii++ )
569 {
570 EXCELLON_CMD* candidate = &excellonHeaderCmdList[ii];
571 int len = candidate->m_Name.size();
572
573 if( len == 0 ) // End of list reached
574 break;
575
576 if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
577 {
578 cmd = candidate;
579 text += len;
580 break;
581 }
582 }
583
584 if( !cmd )
585 {
586 msg.Printf( _( "Unknown Excellon command &lt;%s&gt;" ), text );
587 AddMessageToList( msg );
588 while( *text )
589 text++;
590
591 return false;
592 }
593
594 // Execute command
595 // some do nothing
596 switch( cmd->m_Code )
597 {
598 case DRILL_SKIP:
599 case DRILL_M_UNKNOWN:
600 break;
601
602 case DRILL_M_END:
603 case DRILL_M_ENDFILE:
604 // if a route command is in progress, finish it
605 if( m_RouteModeOn )
607
608 break;
609
610 case DRILL_M_MESSAGE:
611 break;
612
614 break;
615
616 case DRILL_M_HEADER:
618 break;
619
622 break;
623
624 case DRILL_REWIND_STOP: // End of header. No action in a viewer
626 break;
627
630 break;
631
633 break;
634
635 case DRILL_M_METRIC:
636 SelectUnits( true, nullptr );
637 break;
638
639 case DRILL_IMPERIAL_HEADER: // command like INCH,TZ or INCH,LZ
640 case DRILL_METRIC_HEADER: // command like METRIC,TZ or METRIC,LZ
641 SelectUnits( cmd->m_Code == DRILL_METRIC_HEADER ? true : false, nullptr );
642
643 if( *text != ',' )
644 {
645 // No TZ or LZ specified. Should be a decimal format
646 // but this is not always the case. Use our default setting
647 break;
648 }
649
650 text++; // skip separator
651 if( *text == 'T' )
652 m_NoTrailingZeros = false;
653 else
654 m_NoTrailingZeros = true;
655 break;
656
658 break;
659
661 break;
662
664 break;
665
666 case DRILL_M_TIPCHECK:
667 break;
668
670 break;
671
673 if( *text != ',' )
674 {
675 AddMessageToList( wxT( "ICI command has no parameter" ) );
676 break;
677 }
678 text++; // skip separator
679 // Parameter should be ON or OFF
680 if( strncasecmp( text, "OFF", 3 ) == 0 )
681 m_Relative = false;
682 else if( strncasecmp( text, "ON", 2 ) == 0 )
683 m_Relative = true;
684 else
685 AddMessageToList( wxT( "ICI command has incorrect parameter" ) );
686 break;
687
689 break;
690
692 break;
693
695 break;
696
697 case DRILL_RESET_CMD:
698 break;
699
701 break;
702
703 case DRILL_FMT:
704 break;
705
708 break;
709
710 case DRILL_M_TOOL_DOWN: // tool down (starting a routed hole or polyline)
711 // Only the last position is useful:
712 if( m_RoutePositions.size() > 1 )
713 m_RoutePositions.erase( m_RoutePositions.begin(), m_RoutePositions.begin() + m_RoutePositions.size() - 1 );
714
715 break;
716
717 case DRILL_M_TOOL_UP: // tool up (ending a routed polyline)
719 break;
720 }
721
722 while( *text )
723 text++;
724
725 return true;
726}
727
728
730{
731 int mantissaDigits = 0;
732 int characteristicDigits = 0;
733
734 // Example String: ;FILE_FORMAT=4:4
735 // The ;FILE_FORMAT potion will already be stripped off.
736 // Parse the rest strictly as single_digit:single_digit like 4:4 or 2:4
737 // Don't allow anything clever like spaces or multiple digits
738 if( *aText != '=' )
739 return;
740
741 aText++;
742
743 if( !isdigit( *aText ) )
744 return;
745
746 characteristicDigits = *aText - '0';
747 aText++;
748
749 if( *aText != ':' )
750 return;
751
752 aText++;
753
754 if( !isdigit( *aText ) )
755 return;
756
757 mantissaDigits = *aText - '0';
758
759 m_hasFormat = true;
760 m_FmtLen.x = m_FmtLen.y = characteristicDigits + mantissaDigits;
761 m_FmtScale.x = m_FmtScale.y = mantissaDigits;
762}
763
764
766{
767 // Read a tool definition like T1C0.02 or T1F00S00C0.02 or T1C0.02F00S00
768 // and enter the TCODE param in list (using the D_CODE param management, which
769 // is similar to TCODE params.
770 if( *aText == 'T' ) // This is the beginning of the definition
771 aText++;
772
773 // Read tool number:
774 int iprm = ReadInt( aText, false );
775
776 // Skip Feed rate and Spindle speed, if any here
777 while( *aText && ( *aText == 'F' || *aText == 'S' ) )
778 {
779 aText++;
780 ReadInt( aText, false );
781 }
782
783 // Read tool shape
784 if( ! *aText )
785 AddMessageToList( wxString:: Format(
786 _( "Tool definition shape not found" ) ) );
787 else if( *aText != 'C' )
788 AddMessageToList( wxString:: Format(
789 _( "Tool definition '%c' not supported" ), *aText ) );
790 if( *aText )
791 aText++;
792
793 //read tool diameter:
794 double dprm = ReadDouble( aText, false );
795 m_Has_DCode = true;
796
797 // Initialize Dcode to handle this Tool
798 // Remember: dcodes are >= FIRST_DCODE
799 D_CODE* dcode = GetDCODEOrCreate( iprm + FIRST_DCODE );
800
801 if( dcode == nullptr )
802 return false;
803
804 // conv_scale = scaling factor from inch to Internal Unit
805 double conv_scale = gerbIUScale.IU_PER_MILS * 1000;
806
807 if( m_GerbMetric )
808 conv_scale /= 25.4;
809
810 dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale );
811 dcode->m_ApertType = APT_CIRCLE;
812 dcode->m_Defined = true;
813
814 return true;
815}
816
817
819{
820 D_CODE* tool;
821 GERBER_DRAW_ITEM * gbritem;
822
823 while( true )
824 {
825 switch( *text )
826 {
827 case 'X':
828 case 'Y':
829 ReadXYCoord( text, true );
830
831 if( *text == 'I' || *text == 'J' )
832 ReadIJCoord( text );
833
834 break;
835
836 case 'G': // G85 is found here for oval holes
839 break;
840
841 case 0: // E.O.L: execute command
842 if( m_RouteModeOn )
843 {
844 // We are in routing mode, and this is an intermediate point.
845 // So just store it
846 int rmode = 0; // linear routing.
847
849 rmode = ROUTE_CW;
851 rmode = ROUTE_CCW;
852
854 {
856 m_RoutePositions.push_back( point );
857 }
858 else
859 {
861 m_RoutePositions.push_back( point );
862 }
863 return true;
864 }
865
866 tool = GetDCODE( m_Current_Tool );
867 if( !tool )
868 {
869 wxString msg;
870 msg.Printf( _( "Tool %d not defined" ), m_Current_Tool );
871 AddMessageToList( msg );
872 return false;
873 }
874
875 gbritem = new GERBER_DRAW_ITEM( this );
876 AddItemToList( gbritem );
877
878 if( m_SlotOn ) // Oblong hole
879 {
880 fillLineGBRITEM( gbritem, tool->m_Num_Dcode, m_PreviousPos, m_CurrentPos, tool->m_Size, false );
881 // the hole is made: reset the slot on command (G85)
882 // (it is needed for each oblong hole)
883 m_SlotOn = false;
884 }
885 else
886 {
887 fillFlashedGBRITEM( gbritem, tool->m_ApertType, tool->m_Num_Dcode, m_CurrentPos, tool->m_Size,
888 false );
889 }
890
891 StepAndRepeatItem( *gbritem );
893 return true;
894 break;
895
896 default:
897 text++;
898 break;
899 }
900 }
901
902 return true;
903}
904
905
907{
908 // Excellon repeat hole command R#(X#Y#)
909 // Repeat the last hole # times, adding the given incremental X and Y step
910 // each time, using the currently selected tool.
911 int count = CodeNumber( text ); // reads the count, advances past R and the digits
912
913 if( count <= 0 )
914 return false;
915
916 VECTOR2I startPos = m_CurrentPos;
917 bool saveRelative = m_Relative;
918
919 m_Relative = true;
920 VECTOR2I step = ReadXYCoord( text, true ) - startPos;
921 m_Relative = saveRelative;
922 m_CurrentPos = startPos;
923
924 D_CODE* tool = GetDCODE( m_Current_Tool );
925
926 if( !tool )
927 {
928 AddMessageToList( wxString::Format( _( "Tool %d not defined" ), m_Current_Tool ) );
929 return false;
930 }
931
932 for( int ii = 0; ii < count; ++ii )
933 {
934 m_CurrentPos += step;
935
936 GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
937 AddItemToList( gbritem );
938 fillFlashedGBRITEM( gbritem, tool->m_ApertType, tool->m_Num_Dcode, m_CurrentPos, tool->m_Size, false );
939 StepAndRepeatItem( *gbritem );
940 }
941
943 return true;
944}
945
946
948{
949 // Select the tool from the command line Tn, with n = 1 ... TOOLS_MAX_COUNT - 1
950 // Because some drill file have an embedded TCODE definition (like T1C.008F0S0)
951 // in tool selection command, if the tool is not defined in list,
952 // and the definition is embedded, it will be entered in list
953 char * startline = text; // the tool id starts here.
954 int tool_id = CodeNumber( text );
955
956 // T0 is legal, but is not a selection tool. it is a special command
957 if( tool_id >= 0 )
958 {
959 int dcode_id = tool_id + FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE
960
961 if( !D_CODE::IsValidDcodeValue(dcode_id ) )
962 dcode_id = LAST_DCODE;
963
964 m_Current_Tool = dcode_id;
965 D_CODE* currDcode = GetDCODE( dcode_id );
966
967 // if the tool does not exist, and the definition is embedded, create this tool
968 if( currDcode == nullptr && tool_id > 0 )
969 {
970 text = startline; // text starts at the beginning of the command
972 currDcode = GetDCODE( dcode_id );
973 }
974
975 // If the Tool is still not existing, create a dummy tool
976 if( !currDcode )
977 currDcode = GetDCODEOrCreate( dcode_id, true );
978
979 if( currDcode )
980 currDcode->m_InUse = true;
981 }
982
983 while( *text )
984 text++;
985
986 return tool_id >= 0;
987}
988
989
991{
992 EXCELLON_CMD* cmd = nullptr;
993 bool success = false;
994 int id = DRILL_G_UNKNOWN;
995
996 // Search command in list
997 EXCELLON_CMD* candidate;
998 char * gcmd = text; // gcmd points the G command, for error messages.
999
1000 for( unsigned ii = 0; ; ii++ )
1001 {
1002 candidate = &excellon_G_CmdList[ii];
1003 int len = candidate->m_Name.size();
1004 if( len == 0 ) // End of list reached
1005 break;
1006 if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found.
1007 {
1008 cmd = candidate;
1009 text += len;
1010 success = true;
1011 id = cmd->m_Code;
1012 break;
1013 }
1014 }
1015
1016 switch( id )
1017 {
1018 case DRILL_G_ZERO_SET:
1019 ReadXYCoord( text, true );
1021 break;
1022
1023 case DRILL_G_ROUT:
1024 m_SlotOn = false;
1025
1026 if( m_RouteModeOn )
1028
1029 m_RouteModeOn = true;
1030 m_RoutePositions.clear();
1032 ReadXYCoord( text, true );
1033 // This is the first point (starting point) of routing
1034 m_RoutePositions.emplace_back( m_CurrentPos );
1035 break;
1036
1037 case DRILL_G_DRILL:
1038 m_SlotOn = false;
1039
1040 if( m_RouteModeOn )
1042
1043 m_RouteModeOn = false;
1044 m_RoutePositions.clear();
1046 break;
1047
1048 case DRILL_G_SLOT:
1049 m_SlotOn = true;
1050 break;
1051
1052 case DRILL_G_LINEARMOVE:
1055 ReadXYCoord( text, true );
1056 m_RoutePositions.emplace_back( m_CurrentPos );
1057 break;
1058
1059 case DRILL_G_CWMOVE:
1061 ReadXYCoord( text, true );
1062
1063 if( *text == 'I' || *text == 'J' )
1064 ReadIJCoord( text );
1065
1068 else
1070
1071 break;
1072
1073 case DRILL_G_CCWMOVE:
1075 ReadXYCoord( text, true );
1076
1077 if( *text == 'I' || *text == 'J' )
1078 ReadIJCoord( text );
1079
1082 else
1084
1085 break;
1086
1087 case DRILL_G_ABSOLUTE:
1088 m_Relative = false; // false = absolute coord
1089 break;
1090
1092 m_Relative = true; // true = relative coord
1093 break;
1094
1095 case DRILL_G_UNKNOWN:
1096 default:
1097 AddMessageToList( wxString::Format( _( "Unknown Excellon G Code: &lt;%s&gt;" ), From_UTF8(gcmd) ) );
1098
1099 while( *text )
1100 text++;
1101
1102 return false;
1103 }
1104
1105 return success;
1106}
1107
1108void EXCELLON_IMAGE::SelectUnits( bool aMetric, EXCELLON_DEFAULTS* aDefaults )
1109{
1110 /* Coordinates are measured either in inch or metric (millimeters).
1111 * Inch coordinates are in six digits (00.0000) with increments
1112 * as small as 0.0001 (1/10,000).
1113 * Metric coordinates can be measured in microns (thousandths of a millimeter)
1114 * in one of the following three ways:
1115 * Five digit 10 micron resolution (000.00)
1116 * Six digit 10 micron resolution (0000.00)
1117 * Six digit micron resolution (000.000)
1118 *
1119 * Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution
1120 * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution
1121 *
1122 * However some drill files do not use standard values.
1123 */
1124 if( aMetric )
1125 {
1126 m_GerbMetric = true;
1127
1128 if( !m_hasFormat )
1129 {
1130 if( aDefaults )
1131 {
1132 // number of digits in mantissa
1133 m_FmtScale.x = m_FmtScale.y = aDefaults->m_MmMantissaLen;
1134 // number of digits (mantissa+integer)
1135 m_FmtLen.x = m_FmtLen.y = aDefaults->m_MmIntegerLen
1136 + aDefaults->m_MmMantissaLen;
1137 }
1138 else
1139 {
1142 }
1143 }
1144 }
1145 else
1146 {
1147 m_GerbMetric = false;
1148
1149 if( !m_hasFormat )
1150 {
1151 if( aDefaults )
1152 {
1153 m_FmtScale.x = m_FmtScale.y = aDefaults->m_InchMantissaLen;
1154 m_FmtLen.x = m_FmtLen.y = aDefaults->m_InchIntegerLen
1155 + aDefaults->m_InchMantissaLen;
1156 }
1157 else
1158 {
1161 }
1162 }
1163 }
1164}
1165
1166
1168{
1169 // Ends a route command started by M15 ot G01, G02 or G03 command
1170 // if a route command is not in progress, do nothing
1171
1172 if( !m_RouteModeOn )
1173 return;
1174
1175 D_CODE* tool = GetDCODE( m_Current_Tool );
1176
1177 if( !tool )
1178 {
1179 AddMessageToList( wxString::Format( wxT( "Unknown tool code %d" ), m_Current_Tool ) );
1180 return;
1181 }
1182
1183 for( size_t ii = 1; ii < m_RoutePositions.size(); ii++ )
1184 {
1185 GERBER_DRAW_ITEM* gbritem = new GERBER_DRAW_ITEM( this );
1186
1187 if( m_RoutePositions[ii].m_rmode == 0 ) // linear routing
1188 {
1189 fillLineGBRITEM( gbritem, tool->m_Num_Dcode,
1190 m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1191 tool->m_Size, false );
1192 }
1193 else // circular (cw or ccw) routing
1194 {
1195 bool rot_ccw = m_RoutePositions[ii].m_rmode == ROUTE_CW;
1196 int radius = m_RoutePositions[ii].m_radius; // Can be adjusted by computeCenter.
1198
1199 if( m_RoutePositions[ii].m_arc_type_info == ARC_INFO_TYPE_CENTER )
1200 center = VECTOR2I( m_RoutePositions[ii].m_cx, m_RoutePositions[ii].m_cy );
1201 else
1202 center = computeCenter( m_RoutePositions[ii-1].GetPos(),
1203 m_RoutePositions[ii].GetPos(), radius, rot_ccw );
1204
1205 fillArcGBRITEM( gbritem, tool->m_Num_Dcode,
1206 m_RoutePositions[ii-1].GetPos(), m_RoutePositions[ii].GetPos(),
1207 center - m_RoutePositions[ii-1].GetPos(),
1208 tool->m_Size, not rot_ccw , true,
1209 false );
1210 }
1211
1212 AddItemToList( gbritem );
1213
1214 StepAndRepeatItem( *gbritem );
1215 }
1216
1217 m_RoutePositions.clear();
1218 m_RouteModeOn = false;
1219}
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 Execute_Repeat_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