KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_step.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) 2016 Cirilo Bernardo
5 * Copyright (C) 2016-2024 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#include <wx/log.h>
26#include <wx/stdpaths.h>
27#include <wx/process.h>
28#include <wx/string.h>
29#include <wx/filedlg.h>
30
31#include <pgm_base.h>
32#include <board.h>
33#include <confirm.h>
34#include <bitmaps.h>
38#include <footprint.h>
39#include <kiface_base.h>
40#include <locale_io.h>
41#include <math/vector3.h>
42#include <pcb_edit_frame.h>
43#include <pcbnew_settings.h>
44#include <project/project_file.h> // LAST_PATH_TYPE
45#include <reporter.h>
46#include <trace_helpers.h>
49#include <filename_resolver.h>
50
51
53{
54public:
56 {
57 STEP_ORIGIN_0, // absolute coordinates
58 STEP_ORIGIN_PLOT_AXIS, // origin is plot/drill axis origin
59 STEP_ORIGIN_GRID_AXIS, // origin is grid origin
60 STEP_ORIGIN_BOARD_CENTER, // origin is board center
61 STEP_ORIGIN_USER, // origin is entered by user
62 };
63
64 DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
66
67protected:
68 void onBrowseClicked( wxCommandEvent& aEvent ) override;
69 void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
70 void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
71 void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
72 void onExportButton( wxCommandEvent& aEvent ) override;
73
75 {
76 return m_STEP_OrgUnitChoice->GetSelection();
77 }
78
79 double GetXOrg() const
80 {
82 }
83
84 double GetYOrg()
85 {
87 }
88
90
92 {
93 return m_cbRemoveUnspecified->GetValue();
94 }
95
97 {
98 return m_cbRemoveDNP->GetValue();
99 }
100
102 {
103 return m_cbSubstModels->GetValue();
104 }
105
107 {
108 return m_cbOverwriteFile->GetValue();
109 }
110
111private:
113 STEP_ORIGIN_OPTION m_origin; // The last preference for STEP origin option
114 double m_userOriginX; // remember last User Origin X value
115 double m_userOriginY; // remember last User Origin Y value
116 int m_originUnits; // remember last units for User Origin
117 bool m_noUnspecified; // remember last preference for No Unspecified Component
118 bool m_noDNP; // remember last preference for No DNP Component
119 static bool m_optimizeStep; // remember last preference for Optimize STEP file (stored only for the session)
120 static bool m_exportBoardBody; // remember last preference to export board body (stored only for the session)
121 static bool m_exportComponents; // remember last preference to export components (stored only for the session)
122 static bool m_exportTracks; // remember last preference to export tracks (stored only for the session)
123 static bool m_exportZones; // remember last preference to export zones (stored only for the session)
124 static bool m_fuseShapes; // remember last preference to fuse shapes (stored only for the session)
125 static bool m_exportInnerCopper; // remember last preference to export inner layers (stored only for the session)
126 wxString m_boardPath; // path to the exported board file
127 static int m_toleranceLastChoice; // Store m_tolerance option during a session
128};
129
130
131int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
139
140DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
141 DIALOG_EXPORT_STEP_BASE( aParent )
142{
143 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
144
145 m_parent = aParent;
146 m_boardPath = aBoardPath;
147
148 SetupStandardButtons( { { wxID_OK, _( "Export" ) },
149 { wxID_CANCEL, _( "Close" ) } } );
150
151 // Build default output file name
152 // (last saved filename in project or built from board filename)
154
155 if( path.IsEmpty() )
156 {
157 wxFileName brdFile( m_parent->GetBoard()->GetFileName() );
158 brdFile.SetExt( wxT( "step" ) );
159 path = brdFile.GetFullPath();
160 }
161
162 m_outputFileName->SetValue( path );
163
164 Layout();
165 bSizerSTEPFile->Fit( this );
166
167 SetFocus();
168
170
172
173 switch( m_origin )
174 {
175 default:
176 case STEP_ORIGIN_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
177 case STEP_ORIGIN_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
178 case STEP_ORIGIN_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
179 case STEP_ORIGIN_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
180 }
181
187
188 m_cbOptimizeStep->SetValue( m_optimizeStep );
191 m_cbExportTracks->SetValue( m_exportTracks );
192 m_cbExportZones->SetValue( m_exportZones );
194 m_cbFuseShapes->SetValue( m_fuseShapes );
196 m_cbRemoveDNP->SetValue( m_noDNP );
199
200 m_STEP_OrgUnitChoice->SetSelection( m_originUnits );
201 wxString tmpStr;
202 tmpStr << m_userOriginX;
203 m_STEP_Xorg->SetValue( tmpStr );
204 tmpStr = wxEmptyString;
205 tmpStr << m_userOriginY;
206 m_STEP_Yorg->SetValue( tmpStr );
207
208 wxString bad_scales;
209 size_t bad_count = 0;
210
211 for( FOOTPRINT* fp : aParent->GetBoard()->Footprints() )
212 {
213 for( const FP_3DMODEL& model : fp->Models() )
214 {
215 if( model.m_Scale.x != 1.0 || model.m_Scale.y != 1.0 || model.m_Scale.z != 1.0 )
216 {
217 bad_scales.Append( wxS("\n") );
218 bad_scales.Append( model.m_Filename );
219 bad_count++;
220 }
221 }
222
223 if( bad_count >= 5 )
224 break;
225 }
226
227 if( !bad_scales.empty()
229 {
230 wxString extendedMsg = _( "Non-unity scaled models:" ) + wxT( "\n" ) + bad_scales;
231
232 KIDIALOG msgDlg( m_parent, _( "Scaled models detected. "
233 "Model scaling is not reliable for mechanical export." ),
234 _( "Model Scale Warning" ), wxOK | wxICON_WARNING );
235 msgDlg.SetExtendedMessage( extendedMsg );
236 msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
237
238 msgDlg.ShowModal();
239
240 if( msgDlg.DoNotShowAgain() )
242 }
243
244 if( m_toleranceLastChoice >= 0 )
246
247 // Now all widgets have the size fixed, call FinishDialogSettings
249}
250
251
253{
254 GetOriginOption(); // Update m_origin member.
255
256 PCBNEW_SETTINGS* cfg = nullptr;
257
258 try
259 {
261 }
262 catch( const std::runtime_error& e )
263 {
264 wxFAIL_MSG( e.what() );
265 }
266
267 if( cfg )
268 {
269 cfg->m_ExportStep.origin_mode = static_cast<int>( m_origin );
270 cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
273
274 double val = 0.0;
275
276 m_STEP_Xorg->GetValue().ToDouble( &val );
277 cfg->m_ExportStep.origin_x = val;
278
279 m_STEP_Yorg->GetValue().ToDouble( &val );
280 cfg->m_ExportStep.origin_y = val;
281
283 cfg->m_ExportStep.no_dnp = m_cbRemoveDNP->GetValue();
284 }
285
287 m_optimizeStep = m_cbOptimizeStep->GetValue();
288 m_exportBoardBody = m_cbExportBody->GetValue();
290 m_exportTracks = m_cbExportTracks->GetValue();
291 m_exportZones = m_cbExportZones->GetValue();
293 m_fuseShapes = m_cbFuseShapes->GetValue();
294}
295
296
298{
300
301 if( m_rbDrillAndPlotOrigin->GetValue() )
303 else if( m_rbGridOrigin->GetValue() )
305 else if( m_rbUserDefinedOrigin->GetValue() )
307 else if( m_rbBoardCenterOrigin->GetValue() )
309
310 return m_origin;
311}
312
313
314void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
315{
316 wxFileName brdFile = GetBoard()->GetFileName();
317
318 if( GetScreen()->IsContentModified() || brdFile.GetFullPath().empty() )
319 {
320 if( !doAutoSave() )
321 {
322 DisplayErrorMessage( this, _( "STEP export failed! "
323 "Please save the PCB and try again" ) );
324 return;
325 }
326
327 // Use auto-saved board for export
328 brdFile.SetName( FILEEXT::AutoSaveFilePrefix + brdFile.GetName() );
329 }
330
331 DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
332 dlg.ShowModal();
333}
334
335
336void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
337{
338 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
339}
340
341
342void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
343{
344 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
345}
346
347
348void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
349{
350 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
351}
352
353
354void DIALOG_EXPORT_STEP::onBrowseClicked( wxCommandEvent& aEvent )
355{
356 wxString filter = _( "STEP files" )
358 + _( "Binary glTF files" )
360
361 // Build the absolute path of current output directory to preselect it in the file browser.
362 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
363 wxFileName fn( Prj().AbsolutePath( path ) );
364
365 wxFileDialog dlg( this, _( "STEP Output File" ), fn.GetPath(), fn.GetFullName(), filter,
366 wxFD_SAVE );
367
368 if( dlg.ShowModal() == wxID_CANCEL )
369 return;
370
371 m_outputFileName->SetValue( dlg.GetPath() );
372}
373
374
375void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
376{
377 wxString path = m_outputFileName->GetValue();
379
380 // Build the absolute path of current output directory to preselect it in the file browser.
381 std::function<bool( wxString* )> textResolver =
382 [&]( wxString* token ) -> bool
383 {
384 return m_parent->GetBoard()->ResolveTextVar( token, 0 );
385 };
386
387 path = ExpandTextVars( path, &textResolver );
389 path = Prj().AbsolutePath( path );
390
391 if( path.IsEmpty() )
392 {
393 DisplayErrorMessage( this, _( "No filename for output file" ) );
394 return;
395 }
396
397 double tolerance; // default value in mm
399 m_optimizeStep = m_cbOptimizeStep->GetValue();
400 m_exportBoardBody = m_cbExportBody->GetValue();
402 m_exportTracks = m_cbExportTracks->GetValue();
403 m_exportZones = m_cbExportZones->GetValue();
405 m_fuseShapes = m_cbFuseShapes->GetValue();
406
407 switch( m_choiceTolerance->GetSelection() )
408 {
409 case 0: tolerance = 0.001; break;
410 default:
411 case 1: tolerance = 0.01; break;
412 case 2: tolerance = 0.1; break;
413 }
414
415 SHAPE_POLY_SET outline;
416 wxString msg;
417
418 // Check if the board outline is continuous
419 // max dist from one endPt to next startPt to build a closed shape:
420 int chainingEpsilon = pcbIUScale.mmToIU( tolerance );
421
422 // Arc to segment approx error (not critical here: we do not use the outline shape):
423 int maxError = pcbIUScale.mmToIU( 0.005 );
424 bool success = BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, maxError,
425 chainingEpsilon, nullptr );
426 if( !success )
427 {
428 DisplayErrorMessage( this, wxString::Format(
429 _( "Board outline is missing or not closed using %.3f mm tolerance.\n"
430 "Run DRC for a full analysis." ), tolerance ) );
431 return;
432 }
433
434 wxFileName fn( Prj().AbsolutePath( path ) );
435
436 if( fn.FileExists() && !GetOverwriteFile() )
437 {
438 msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
439 fn.GetFullPath() );
440
441 if( wxMessageBox( msg, _( "STEP/GLTF Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
442 return;
443 }
444
445 wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
446#ifdef __WXMAC__
447 // On macOS, we have standalone applications inside the main bundle, so we handle that here:
448 if( appK2S.GetPath().Find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
449 {
450 appK2S.AppendDir( wxT( ".." ) );
451 appK2S.AppendDir( wxT( ".." ) );
452 appK2S.AppendDir( wxT( ".." ) );
453 appK2S.AppendDir( wxT( ".." ) );
454 appK2S.AppendDir( wxT( "MacOS" ) );
455 }
456#else
457 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
458 {
459 appK2S.RemoveLastDir();
460 appK2S.AppendDir( "kicad" );
461 }
462#endif
463
464 appK2S.SetName( wxT( "kicad-cli" ) );
465 appK2S.Normalize( FN_NORMALIZE_FLAGS );
466
467 wxString cmdK2S = wxT( "\"" );
468 cmdK2S.Append( appK2S.GetFullPath() );
469 cmdK2S.Append( wxT( "\"" ) );
470
471 cmdK2S.Append( wxT( " pcb" ) );
472 cmdK2S.Append( wxT( " export" ) );
473
474 if( fn.GetExt() == FILEEXT::GltfBinaryFileExtension )
475 cmdK2S.Append( wxT( " glb" ) );
476 else if( fn.GetExt() == FILEEXT::BrepFileExtension )
477 cmdK2S.Append( wxT( " brep" ) );
478 else
479 cmdK2S.Append( wxT( " step" ) );
480
482 cmdK2S.Append( wxT( " --no-unspecified" ) );
483
484 if( GetNoDNPOption() )
485 cmdK2S.Append( wxT( " --no-dnp" ) );
486
487 if( GetSubstOption() )
488 cmdK2S.Append( wxT( " --subst-models" ) );
489
490 if( !m_optimizeStep )
491 cmdK2S.Append( wxT( " --no-optimize-step" ) );
492
493 if( !m_exportBoardBody )
494 cmdK2S.Append( wxT( " --no-board-body" ) );
495
496 if( !m_exportComponents )
497 cmdK2S.Append( wxT( " --no-components" ) );
498
499 if( m_exportTracks )
500 cmdK2S.Append( wxT( " --include-tracks" ) );
501
502 if( m_exportZones )
503 cmdK2S.Append( wxT( " --include-zones" ) );
504
506 cmdK2S.Append( wxT( " --include-inner-copper" ) );
507
508 if( m_fuseShapes )
509 cmdK2S.Append( wxT( " --fuse-shapes" ) );
510
511 // TODO: --net-filter
512
513 // Note: for some reason, using \" to insert a quote in a format string, under MacOS
514 // wxString::Format does not work. So use a %c format in string
515 int quote = '\'';
516 int dblquote = '"';
517
518 switch( GetOriginOption() )
519 {
520 case STEP_ORIGIN_0:
521 wxFAIL_MSG( wxT( "Unsupported origin option: how did we get here?" ) );
522 break;
523
525 cmdK2S.Append( wxT( " --drill-origin" ) );
526 break;
527
529 cmdK2S.Append( wxT( " --grid-origin" ) );
530 break;
531
532 case STEP_ORIGIN_USER:
533 {
534 double xOrg = GetXOrg();
535 double yOrg = GetYOrg();
536
537 if( GetOrgUnitsChoice() == 1 )
538 {
539 // selected reference unit is in inches, and STEP units are mm
540 xOrg *= 25.4;
541 yOrg *= 25.4;
542 }
543
545 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
546 quote, xOrg, yOrg, quote ) );
547 break;
548 }
549
551 {
552 BOX2I bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
553 double xOrg = pcbIUScale.IUTomm( bbox.GetCenter().x );
554 double yOrg = pcbIUScale.IUTomm( bbox.GetCenter().y );
556
557 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
558 quote, xOrg, yOrg, quote ) );
559 break;
560 }
561 }
562
563 {
565 cmdK2S.Append( wxString::Format( wxT( " --min-distance=%c%.3fmm%c" ),
566 quote, tolerance, quote ) );
567 }
568
569 // Output file path.
570 cmdK2S.Append( wxString::Format( wxT( " -f -o %c%s%c" ),
571 dblquote, fn.GetFullPath(), dblquote ) );
572
573
574 // Input file path.
575 cmdK2S.Append( wxString::Format( wxT( " %c%s%c" ), dblquote, m_boardPath, dblquote ) );
576
577 wxLogTrace( traceKiCad2Step, wxT( "export step command: %s" ), cmdK2S );
578
579 DIALOG_EXPORT_STEP_LOG* log = new DIALOG_EXPORT_STEP_LOG( this, cmdK2S );
580 log->ShowModal();
581 Close();
582}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: board.cpp:424
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1555
const FOOTPRINTS & Footprints() const
Definition: board.h:322
const wxString & GetFileName() const
Definition: board.h:318
const Vec GetCenter() const
Definition: box2.h:220
DO_NOT_SHOW_AGAIN m_DoNotShowAgain
Class DIALOG_EXPORT_STEP_BASE.
wxRadioButton * m_rbDrillAndPlotOrigin
STD_BITMAP_BUTTON * m_browseButton
STEP_ORIGIN_OPTION GetOriginOption()
PCB_EDIT_FRAME * m_parent
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
STEP_ORIGIN_OPTION m_origin
void onExportButton(wxCommandEvent &aEvent) override
DIALOG_EXPORT_STEP(PCB_EDIT_FRAME *aParent, const wxString &aBoardPath)
void onUpdateUnits(wxUpdateUIEvent &aEvent) override
void onUpdateYPos(wxUpdateUIEvent &aEvent) override
void onBrowseClicked(wxCommandEvent &aEvent) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:47
bool DoNotShowAgain() const
Definition: confirm.cpp:64
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:56
int ShowModal() override
Definition: confirm.cpp:100
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
DIALOG_EXPORT_STEP m_ExportStep
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
The main frame for Pcbnew.
void SetLastPath(LAST_PATH_TYPE aType, const wxString &aLastPath)
Set the path of the last file successfully read.
bool doAutoSave() override
Perform auto save when the board has been modified and not saved within the auto save interval.
void OnExportSTEP(wxCommandEvent &event)
Export the current BOARD to a STEP assembly.
wxString GetLastPath(LAST_PATH_TYPE aType)
Get the last path for a particular type.
bool IsContentModified() const override
Get if the current board has been modified but not saved.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:678
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:320
Represent a set of closed polygons.
void SetBitmap(const wxBitmapBundle &aBmp)
void SetValue(const wxString &aValue) override
Set a new value in evaluator buffer, and display it in the wxTextCtrl.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:334
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
This file is part of the common library.
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
#define _(s)
static const std::string BrepFileExtension
static const std::string StepFileAbrvExtension
static const std::string GltfBinaryFileExtension
static const std::string AutoSaveFilePrefix
static const std::string StepFileExtension
const wxChar *const traceKiCad2Step
Flag to enable KiCad2Step debug tracing.
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:572
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1059
see class PGM_BASE
@ LAST_PATH_STEP
Definition: project_file.h:50
std::vector< FAB_LAYER_COLOR > dummy
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
wxLogTrace helper definitions.
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Definition of file extensions used in Kicad.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition: wx_filename.h:39