KiCad PCB EDA Suite
Loading...
Searching...
No Matches
gendrill_gerber_writer.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) 2017 Jean_Pierre Charras <jp.charras at wanadoo.fr>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
31#include <string_utils.h>
32#include <locale_io.h>
33#include <board.h>
34#include <footprint.h>
35#include <pcb_track.h>
36#include <pad.h>
37#include <pcbplot.h>
39#include <reporter.h>
40#include <gbr_metadata.h>
41
42// set to 1 to use flashed oblong holes, 0 to draw them by a line (route holes).
43// WARNING: currently ( gerber-layer-format-specification-revision-2023-08 ),
44// oblong holes **must be routed* in a drill file and not flashed,
45// so set FLASH_OVAL_HOLE to 0
46#define FLASH_OVAL_HOLE 0
47
48
50 : GENDRILL_WRITER_BASE( aPcb )
51{
54 m_unitsMetric = true;
55 m_drillFileExtension = wxT( "gbr" );
56 m_merge_PTH_NPTH = false;
57}
58
59
60bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, bool aGenDrill,
61 bool aGenMap, bool aGenTenting, REPORTER* aReporter )
62{
63 bool success = true;
64 // Note: In Gerber drill files, NPTH and PTH are always separate files
65 m_merge_PTH_NPTH = false;
66
67 wxFileName fn;
68 wxString msg;
69
70 std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
71
72 // append a pair representing the NPTH set of holes, for separate drill files.
73 // (Gerber drill files are separate files for PTH and NPTH)
74 hole_sets.emplace_back( F_Cu, B_Cu );
75
76 for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
77 it != hole_sets.end(); ++it )
78 {
79 DRILL_LAYER_PAIR pair = *it;
80 // For separate drill files, the last layer pair is the NPTH drill file.
81 bool doing_npth = ( it == hole_sets.end() - 1 );
82
83 buildHolesList( pair, doing_npth );
84
85 // The file is created if it has holes, or if it is the non plated drill file
86 // to be sure the NPTH file is up to date in separate files mode.
87 // Also a PTH drill/map file is always created, to be sure at least one plated hole drill
88 // file is created (do not create any PTH drill file can be seen as not working drill
89 // generator).
90 if( getHolesCount() > 0 || doing_npth || pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
91 {
92 fn = getDrillFileName( pair, doing_npth, false );
93 fn.SetPath( aPlotDirectory );
94
95 if( aGenDrill )
96 {
97 wxString fullFilename = fn.GetFullPath();
98
99 int result = createDrillFile( fullFilename, doing_npth, pair );
100
101 if( result < 0 )
102 {
103 if( aReporter )
104 {
105 msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
106 aReporter->Report( msg, RPT_SEVERITY_ERROR );
107 success = false;
108 }
109
110 break;
111 }
112 else
113 {
114 if( aReporter )
115 {
116 msg.Printf( _( "Created file '%s'." ), fullFilename );
117 aReporter->Report( msg, RPT_SEVERITY_ACTION );
118 }
119 }
120 }
121 }
122
123 if( getHolesCount() > 0 && !doing_npth )
124 {
125 for( IPC4761_FEATURES feature :
126 { IPC4761_FEATURES::FILLED, IPC4761_FEATURES::CAPPED,
127 IPC4761_FEATURES::COVERED_BACK, IPC4761_FEATURES::COVERED_FRONT,
128 IPC4761_FEATURES::PLUGGED_BACK, IPC4761_FEATURES::PLUGGED_FRONT,
129 IPC4761_FEATURES::TENTED_BACK, IPC4761_FEATURES::TENTED_FRONT } )
130 {
131 if( !aGenTenting )
132 {
133 if( feature == IPC4761_FEATURES::TENTED_BACK
134 || feature == IPC4761_FEATURES::TENTED_FRONT )
135 {
136 continue;
137 }
138 }
139
140 fn = getProtectionFileName( pair, feature );
141 fn.SetPath( aPlotDirectory );
142
143 wxString fullFilename = fn.GetFullPath();
144
145 if( createProtectionFile( fullFilename, feature, pair ) < 0 )
146 {
147 if( aReporter )
148 {
149 msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
150 aReporter->Report( msg, RPT_SEVERITY_ERROR );
151 success = false;
152 }
153 }
154 else
155 {
156 if( aReporter )
157 {
158 msg.Printf( _( "Created file '%s'." ), fullFilename );
159 aReporter->Report( msg, RPT_SEVERITY_ACTION );
160 }
161 }
162 }
163 }
164 }
165
166 if( aGenMap )
167 success &= CreateMapFilesSet( aPlotDirectory, aReporter );
168
169 if( aReporter )
170 aReporter->ReportTail( _( "Done." ), RPT_SEVERITY_INFO );
171
172 return success;
173}
174
175
176#if !FLASH_OVAL_HOLE
177// A helper class to transform an oblong hole to a segment
178static void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart, VECTOR2I& aEnd );
179#endif
180
181int GERBER_WRITER::createProtectionFile( const wxString& aFullFilename, IPC4761_FEATURES aFeature,
182 DRILL_LAYER_PAIR aLayerPair )
183{
184 GERBER_PLOTTER plotter;
185 // Gerber drill file imply X2 format:
186 plotter.UseX2format( true );
187 plotter.UseX2NetAttributes( true );
188 plotter.DisableApertMacros( false );
189
190 // Add the standard X2 header, without FileFunction
191 AddGerberX2Header( &plotter, m_pcb );
192 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS / 10, /* scale */ 1.0,
193 /* mirror */ false );
194
195 // has meaning only for gerber plotter. Must be called only after SetViewport
196 plotter.SetGerberCoordinatesFormat( 6 );
197 plotter.SetCreator( wxT( "PCBNEW" ) );
198
199 // Add the standard X2 FileFunction for drill files
200 // %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
201 wxString text = "%TF,FileFunction,Other,";
202
203 std::string attrib;
204 switch( aFeature )
205 {
206 case IPC4761_FEATURES::CAPPED:
207 text << wxT( "Capping" );
208 attrib = "Capping";
209 break;
210 case IPC4761_FEATURES::FILLED:
211 text << wxT( "Filling" );
212 attrib = "Filling";
213 break;
214 case IPC4761_FEATURES::COVERED_BACK:
215 text << wxT( "Covering-Back" );
216 attrib = "Covering";
217 break;
218 case IPC4761_FEATURES::COVERED_FRONT:
219 text << wxT( "Covering-Front" );
220 attrib = "Covering";
221 break;
222 case IPC4761_FEATURES::PLUGGED_BACK:
223 text << wxT( "Plugging-Back" );
224 attrib = "Plugging";
225 break;
226 case IPC4761_FEATURES::PLUGGED_FRONT:
227 text << wxT( "Plugging-Front" );
228 attrib = "Plugging";
229 break;
230 case IPC4761_FEATURES::TENTED_BACK:
231 text << wxT( "Tenting-Back" );
232 attrib = "Tenting";
233 break;
234 case IPC4761_FEATURES::TENTED_FRONT:
235 text << wxT( "Tenting-Front" );
236 attrib = "Tenting";
237 break;
238 default: return -1;
239 }
240 text << wxT( "*%" );
241 plotter.AddLineToHeader( text );
242
243 // Add file polarity (positive)
244 text = wxT( "%TF.FilePolarity,Positive*%" );
245 plotter.AddLineToHeader( text );
246
247
248 if( !plotter.OpenFile( aFullFilename ) )
249 return -1;
250
251 plotter.StartPlot( wxT( "1" ) );
252
253 int holes_count = 0;
254
255 for( auto& hole_descr : m_holeListBuffer )
256 {
257 if( !dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
258 {
259 continue;
260 }
261
262 const PCB_VIA* via = dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent );
263
264 bool cont = false;
265 int diameter = hole_descr.m_Hole_Diameter;
266 // clang-format off: suggestion is inconsitent
267 switch( aFeature )
268 {
269 case IPC4761_FEATURES::FILLED:
270 cont = ! hole_descr.m_Hole_Filled;
271 break;
272 case IPC4761_FEATURES::CAPPED:
273 cont = ! hole_descr.m_Hole_Capped;
274 break;
275 case IPC4761_FEATURES::COVERED_BACK:
276 cont = !hole_descr.m_Hole_Bot_Covered;
277 diameter = via->GetWidth( via->BottomLayer() );
278 break;
279 case IPC4761_FEATURES::COVERED_FRONT:
280 cont = ! hole_descr.m_Hole_Top_Covered;
281 diameter = via->GetWidth( via->TopLayer() );
282 break;
283 case IPC4761_FEATURES::PLUGGED_BACK:
284 cont = !hole_descr.m_Hole_Bot_Plugged;
285 break;
286 case IPC4761_FEATURES::PLUGGED_FRONT:
287 cont = ! hole_descr.m_Hole_Top_Plugged;
288 break;
289 case IPC4761_FEATURES::TENTED_BACK:
290 cont = ! hole_descr.m_Hole_Bot_Tented;
291 diameter = via->GetWidth( via->BottomLayer() );
292 break;
293 case IPC4761_FEATURES::TENTED_FRONT:
294 cont = ! hole_descr.m_Hole_Top_Tented;
295 diameter = via->GetWidth( via->TopLayer() );
296 break;
297 }
298 // clang-format on: suggestion is inconsitent
299
300 if( cont )
301 continue;
302
303 GBR_METADATA gbr_metadata;
304
305 gbr_metadata.SetApertureAttrib( attrib );
306
307 plotter.FlashPadCircle( hole_descr.m_Hole_Pos, diameter, FILLED, &gbr_metadata );
308
309 holes_count++;
310 }
311
312 plotter.EndPlot();
313
314 return holes_count;
315}
316
317int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
318 DRILL_LAYER_PAIR aLayerPair )
319{
320 int holes_count;
321
322 LOCALE_IO dummy; // Use the standard notation for double numbers
323
324 GERBER_PLOTTER plotter;
325
326 // Gerber drill file imply X2 format:
327 plotter.UseX2format( true );
328 plotter.UseX2NetAttributes( true );
329 plotter.DisableApertMacros( false );
330
331 // Add the standard X2 header, without FileFunction
332 AddGerberX2Header( &plotter, m_pcb );
333 plotter.SetViewport( m_offset, pcbIUScale.IU_PER_MILS/10, /* scale */ 1.0, /* mirror */false );
334
335 // has meaning only for gerber plotter. Must be called only after SetViewport
336 plotter.SetGerberCoordinatesFormat( 6 );
337 plotter.SetCreator( wxT( "PCBNEW" ) );
338
339 // Add the standard X2 FileFunction for drill files
340 // %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
341 wxString text = BuildFileFunctionAttributeString( aLayerPair,
342 aIsNpth ? TYPE_FILE::NPTH_FILE
344 plotter.AddLineToHeader( text );
345
346 // Add file polarity (positive)
347 text = wxT( "%TF.FilePolarity,Positive*%" );
348 plotter.AddLineToHeader( text );
349
350 if( !plotter.OpenFile( aFullFilename ) )
351 return -1;
352
353 plotter.StartPlot( wxT( "1" ) );
354
355 holes_count = 0;
356
357 VECTOR2I hole_pos;
358 bool last_item_is_via = true; // a flag to clear object attributes when a via hole is created.
359
360 for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
361 {
362 HOLE_INFO& hole_descr = m_holeListBuffer[ii];
363 hole_pos = hole_descr.m_Hole_Pos;
364
365 // Manage the aperture attributes: in drill files 3 attributes can be used:
366 // "ViaDrill", only for vias, not pads
367 // "ComponentDrill", only for Through Holes pads
368 // "Slot" for oblong holes;
369 GBR_METADATA gbr_metadata;
370
371 if( dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
372 {
374
375 if( !last_item_is_via )
376 {
377 // be sure the current object attribute is cleared for vias
378 plotter.EndBlock( nullptr );
379 }
380
381 last_item_is_via = true;
382 }
383 else if( dyn_cast<const PAD*>( hole_descr.m_ItemParent ) )
384 {
385 last_item_is_via = false;
386 const PAD* pad = dyn_cast<const PAD*>( hole_descr.m_ItemParent );
387
388 if( pad->GetProperty() == PAD_PROP::CASTELLATED )
389 {
390 gbr_metadata.SetApertureAttrib(
392 }
393 else
394 {
395 // Good practice of oblong pad holes (slots) is to use a specific aperture for
396 // routing, not used in drill commands.
397 if( hole_descr.m_Hole_Shape )
398 {
399 gbr_metadata.SetApertureAttrib(
401 }
402 else
403 {
404 gbr_metadata.SetApertureAttrib(
406 }
407 }
408
409 // Add object attribute: component reference to pads (mainly useful for users)
410 wxString ref = pad->GetParentFootprint()->GetReference();
411
412 gbr_metadata.SetCmpReference( ref );
414 }
415
416 if( hole_descr.m_Hole_Shape )
417 {
418#if FLASH_OVAL_HOLE // set to 1 to use flashed oblong holes,
419 // 0 to draw them as a line.
420 plotter.FlashPadOval( hole_pos, hole_descr.m_Hole_Size, hole_descr.m_Hole_Orient,
421 FILLED, &gbr_metadata );
422#else
423 // Use routing for oblong hole (Slots)
424 VECTOR2I start, end;
425 convertOblong2Segment( hole_descr.m_Hole_Size, hole_descr.m_Hole_Orient, start, end );
426 int width = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
427
428 if ( width == 0 )
429 continue;
430
431 plotter.ThickSegment( start+hole_pos, end+hole_pos, width, FILLED, &gbr_metadata );
432#endif
433 }
434 else
435 {
436 int diam = std::min( hole_descr.m_Hole_Size.x, hole_descr.m_Hole_Size.y );
437 plotter.FlashPadCircle( hole_pos, diam, FILLED, &gbr_metadata );
438 }
439
440 holes_count++;
441 }
442
443 plotter.EndPlot();
444
445 return holes_count;
446}
447
448
449#if !FLASH_OVAL_HOLE
450void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart,
451 VECTOR2I& aEnd )
452{
453 VECTOR2I size( aSize );
454 EDA_ANGLE orient( aOrient );
455
456 /* The pad will be drawn as an oblong shape with size.y > size.x
457 * (Oval vertical orientation 0)
458 */
459 if( size.x > size.y )
460 {
461 std::swap( size.x, size.y );
462 orient += ANGLE_90;
463 }
464
465 int deltaxy = size.y - size.x; // distance between centers of the oval
466 aStart = VECTOR2I( 0, deltaxy / 2 );
467 RotatePoint( aStart, orient );
468
469 aEnd = VECTOR2I( 0, -deltaxy / 2 );
470 RotatePoint( aEnd, orient );
471}
472#endif
473
474
475void GERBER_WRITER::SetFormat( int aRightDigits )
476{
477 /* Set conversion scale depending on drill file units */
478 m_conversionUnits = 1.0 / pcbIUScale.IU_PER_MM; // Gerber units = mm
479
480 // Set precision (unit is mm).
481 m_precision.m_Lhs = 4;
482 m_precision.m_Rhs = aRightDigits == 6 ? 6 : 5;
483}
484
485
486const wxString GERBER_WRITER::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
487 bool aMerge_PTH_NPTH ) const
488{
489 // Gerber files extension is always .gbr.
490 // Therefore, to mark drill files, add "-drl" to the filename.
491 wxFileName fname( GENDRILL_WRITER_BASE::getDrillFileName( aPair, aNPTH, aMerge_PTH_NPTH ) );
492 fname.SetName( fname.GetName() + wxT( "-drl" ) );
493
494 return fname.GetFullPath();
495}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:296
@ GBR_APERTURE_ATTRIB_CMP_DRILL
Aperture used for pad holes in drill files.
Definition: gbr_metadata.h:141
@ GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL
Aperture used for pads oblong holes in drill files.
Definition: gbr_metadata.h:144
@ GBR_APERTURE_ATTRIB_CASTELLATEDDRILL
Aperture used for castellated pads in drill files.
Definition: gbr_metadata.h:138
@ GBR_APERTURE_ATTRIB_VIADRILL
Aperture used for via holes in drill files.
Definition: gbr_metadata.h:140
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:211
void SetCmpReference(const wxString &aComponentRef)
Definition: gbr_metadata.h:255
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
Definition: gbr_metadata.h:215
void SetNetAttribType(int aNetAttribType)
Definition: gbr_metadata.h:233
@ GBR_NETINFO_CMP
print info associated to a component (TO.C attribute)
Create drill maps and drill reports and drill files.
std::vector< DRILL_LAYER_PAIR > getUniqueLayerPairs() const
Get unique layer pairs by examining the micro and blind_buried vias.
virtual const wxString getDrillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const
void buildHolesList(DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list)
Create the list of holes and tools for a given board.
const wxString BuildFileFunctionAttributeString(DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHoleType, bool aCompatNCdrill=false) const
virtual const wxString getProtectionFileName(DRILL_LAYER_PAIR aPair, IPC4761_FEATURES aFeature) const
std::vector< HOLE_INFO > m_holeListBuffer
bool CreateMapFilesSet(const wxString &aPlotDirectory, REPORTER *aReporter=nullptr)
Create the full set of map files for the board, in PS, PDF ... format (use SetMapFileFormat() to sele...
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false) override
Selection of Gerber units and resolution (number of digits in mantissa).
virtual void FlashPadCircle(const VECTOR2I &pos, int diametre, OUTLINE_MODE trace_mode, void *aData) override
Filled circular flashes are stored as apertures.
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
virtual bool EndPlot() override
void UseX2format(bool aEnable)
virtual void FlashPadOval(const VECTOR2I &aPadPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, OUTLINE_MODE aTraceMode, void *aData) override
void UseX2NetAttributes(bool aEnable)
virtual void EndBlock(void *aData) override
Define the end of a group of drawing items the group is started by StartBlock().
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, OUTLINE_MODE tracemode, void *aData) override
virtual bool StartPlot(const wxString &pageNumber) override
Write GERBER header to file initialize global variable g_Plot_PlotOutputFile.
void DisableApertMacros(bool aDisable)
Disable Aperture Macro (AM) command, only for broken Gerber Readers.
bool CreateDrillandMapFilesSet(const wxString &aPlotDirectory, bool aGenDrill, bool aGenMap, bool aGenTenting, REPORTER *aReporter=nullptr)
Create the full set of Excellon drill file for the board filenames are computed from the board name,...
virtual const wxString getDrillFileName(DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH) const override
int createDrillFile(wxString &aFullFilename, bool aIsNpth, DRILL_LAYER_PAIR aLayerPair)
Create an Excellon drill file.
void SetFormat(int aRightDigits=6)
Initialize internal parameters to match the given format.
GERBER_WRITER(BOARD *aPcb)
int createProtectionFile(const wxString &aFullFilename, IPC4761_FEATURES aFeature, DRILL_LAYER_PAIR aLayerPair)
Create a Gerber X2 file for via protection features.
Handle hole which must be drilled (diameter, position and layers).
BOARD_ITEM * m_ItemParent
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
Definition: pad.h:54
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition: plotter.cpp:75
virtual void SetCreator(const wxString &aCreator)
Definition: plotter.h:154
void AddLineToHeader(const wxString &aExtraString)
Add a line to the list of free lines to print at the beginning of the file.
Definition: plotter.h:164
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:73
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the end of the list, for objects that support report ordering.
Definition: reporter.h:101
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
Handle special data (items attributes) during plot.
std::pair< PCB_LAYER_ID, PCB_LAYER_ID > DRILL_LAYER_PAIR
static void convertOblong2Segment(const VECTOR2I &aSize, const EDA_ANGLE &aOrient, VECTOR2I &aStart, VECTOR2I &aEnd)
Classes used in drill files, map files and report files generation.
static const bool FILLED
Definition: gr_basic.cpp:30
@ B_Cu
Definition: layer_ids.h:65
@ F_Cu
Definition: layer_ids.h:64
void AddGerberX2Header(PLOTTER *aPlotter, const BOARD *aBoard, bool aUseX1CompatibilityMode)
Calculate some X2 attributes as defined in the Gerber file format specification J4 (chapter 5) and ad...
Definition: pcbplot.cpp:294
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
std::vector< FAB_LAYER_COLOR > dummy
const double IU_PER_MM
Definition: base_units.h:76
const double IU_PER_MILS
Definition: base_units.h:77
VECTOR2I end
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695