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-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#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_exportTracks; // remember last preference to export tracks (stored only for the session)
121 static bool m_exportZones; // remember last preference to export tracks (stored only for the session)
122 wxString m_boardPath; // path to the exported board file
123 static int m_toleranceLastChoice; // Store m_tolerance option during a session
124};
125
126
127int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
131
132DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
133 DIALOG_EXPORT_STEP_BASE( aParent )
134{
135 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
136
137 m_parent = aParent;
138 m_boardPath = aBoardPath;
139
140 SetupStandardButtons( { { wxID_OK, _( "Export" ) },
141 { wxID_CANCEL, _( "Close" ) } } );
142
143 // Build default output file name
144 // (last saved filename in project or built from board filename)
146
147 if( path.IsEmpty() )
148 {
149 wxFileName brdFile( m_parent->GetBoard()->GetFileName() );
150 brdFile.SetExt( wxT( "step" ) );
151 path = brdFile.GetFullPath();
152 }
153
154 m_outputFileName->SetValue( path );
155
156 Layout();
157 bSizerSTEPFile->Fit( this );
158
159 SetFocus();
160
162
164
165 switch( m_origin )
166 {
167 default:
168 case STEP_ORIGIN_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
169 case STEP_ORIGIN_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
170 case STEP_ORIGIN_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
171 case STEP_ORIGIN_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
172 }
173
179
180 m_cbOptimizeStep->SetValue( m_optimizeStep );
181 m_cbExportTracks->SetValue( m_exportTracks );
182 m_cbExportZones->SetValue( m_exportZones );
184 m_cbRemoveDNP->SetValue( m_noDNP );
187
188 m_STEP_OrgUnitChoice->SetSelection( m_originUnits );
189 wxString tmpStr;
190 tmpStr << m_userOriginX;
191 m_STEP_Xorg->SetValue( tmpStr );
192 tmpStr = wxEmptyString;
193 tmpStr << m_userOriginY;
194 m_STEP_Yorg->SetValue( tmpStr );
195
196 wxString bad_scales;
197 size_t bad_count = 0;
198
199 for( FOOTPRINT* fp : aParent->GetBoard()->Footprints() )
200 {
201 for( const FP_3DMODEL& model : fp->Models() )
202 {
203 if( model.m_Scale.x != 1.0 || model.m_Scale.y != 1.0 || model.m_Scale.z != 1.0 )
204 {
205 bad_scales.Append( wxS("\n") );
206 bad_scales.Append( model.m_Filename );
207 bad_count++;
208 }
209 }
210
211 if( bad_count >= 5 )
212 break;
213 }
214
215 if( !bad_scales.empty()
216 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning )
217 {
218 wxString extendedMsg = _( "Non-unity scaled models:" ) + wxT( "\n" ) + bad_scales;
219
220 KIDIALOG msgDlg( m_parent, _( "Scaled models detected. "
221 "Model scaling is not reliable for mechanical export." ),
222 _( "Model Scale Warning" ), wxOK | wxICON_WARNING );
223 msgDlg.SetExtendedMessage( extendedMsg );
224 msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
225
226 msgDlg.ShowModal();
227
228 if( msgDlg.DoNotShowAgain() )
229 Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning = true;
230 }
231
232 if( m_toleranceLastChoice >= 0 )
234
235 // Now all widgets have the size fixed, call FinishDialogSettings
237}
238
239
241{
242 GetOriginOption(); // Update m_origin member.
243
244 PCBNEW_SETTINGS* cfg = nullptr;
245
246 try
247 {
249 }
250 catch( const std::runtime_error& e )
251 {
252 wxFAIL_MSG( e.what() );
253 }
254
255 if( cfg )
256 {
257 cfg->m_ExportStep.origin_mode = static_cast<int>( m_origin );
258 cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
261
262 double val = 0.0;
263
264 m_STEP_Xorg->GetValue().ToDouble( &val );
265 cfg->m_ExportStep.origin_x = val;
266
267 m_STEP_Yorg->GetValue().ToDouble( &val );
268 cfg->m_ExportStep.origin_y = val;
269
271 cfg->m_ExportStep.no_dnp = m_cbRemoveDNP->GetValue();
272 }
273
275 m_optimizeStep = m_cbOptimizeStep->GetValue();
276 m_exportTracks = m_cbExportTracks->GetValue();
277 m_exportZones = m_cbExportZones->GetValue();
278}
279
280
282{
284
285 if( m_rbDrillAndPlotOrigin->GetValue() )
287 else if( m_rbGridOrigin->GetValue() )
289 else if( m_rbUserDefinedOrigin->GetValue() )
291 else if( m_rbBoardCenterOrigin->GetValue() )
293
294 return m_origin;
295}
296
297
298void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
299{
300 wxFileName brdFile = GetBoard()->GetFileName();
301
302 if( GetScreen()->IsContentModified() || brdFile.GetFullPath().empty() )
303 {
304 if( !doAutoSave() )
305 {
306 DisplayErrorMessage( this, _( "STEP export failed! "
307 "Please save the PCB and try again" ) );
308 return;
309 }
310
311 // Use auto-saved board for export
312 brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
313 }
314
315 DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
316 dlg.ShowModal();
317}
318
319
320void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
321{
322 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
323}
324
325
326void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
327{
328 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
329}
330
331
332void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
333{
334 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
335}
336
337
338void DIALOG_EXPORT_STEP::onBrowseClicked( wxCommandEvent& aEvent )
339{
340 wxString filter = _( "STEP files" )
342 + _( "Binary glTF files" )
344
345 // Build the absolute path of current output directory to preselect it in the file browser.
346 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
347 wxFileName fn( Prj().AbsolutePath( path ) );
348
349 wxFileDialog dlg( this, _( "STEP Output File" ), fn.GetPath(), fn.GetFullName(), filter,
350 wxFD_SAVE );
351
352 if( dlg.ShowModal() == wxID_CANCEL )
353 return;
354
355 m_outputFileName->SetValue( dlg.GetPath() );
356}
357
358
359void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
360{
361 wxString path = m_outputFileName->GetValue();
363
365
366 if( path.IsEmpty() )
367 {
368 DisplayErrorMessage( this, _( "No filename for output file" ) );
369 return;
370 }
371
372 double tolerance; // default value in mm
374 m_optimizeStep = m_cbOptimizeStep->GetValue();
375 m_exportTracks = m_cbExportTracks->GetValue();
376 m_exportZones = m_cbExportZones->GetValue();
377
378 switch( m_choiceTolerance->GetSelection() )
379 {
380 case 0: tolerance = 0.001; break;
381 default:
382 case 1: tolerance = 0.01; break;
383 case 2: tolerance = 0.1; break;
384 }
385
386 SHAPE_POLY_SET outline;
387 wxString msg;
388
389 // Check if the board outline is continuous
390 // max dist from one endPt to next startPt to build a closed shape:
391 int chainingEpsilon = pcbIUScale.mmToIU( tolerance );
392
393 // Arc to segment approx error (not critical here: we do not use the outline shape):
394 int maxError = pcbIUScale.mmToIU( 0.005 );
395 bool success = BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, maxError,
396 chainingEpsilon, nullptr );
397 if( !success )
398 {
399 DisplayErrorMessage( this, wxString::Format(
400 _( "Board outline is missing or not closed using %.3f mm tolerance.\n"
401 "Run DRC for a full analysis." ), tolerance ) );
402 return;
403 }
404
405 wxFileName fn( Prj().AbsolutePath( path ) );
406
407 if( fn.FileExists() && !GetOverwriteFile() )
408 {
409 msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
410 fn.GetFullPath() );
411
412 if( wxMessageBox( msg, _( "STEP/GLTF Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
413 return;
414 }
415
416 wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
417#ifdef __WXMAC__
418 // On macOS, we have standalone applications inside the main bundle, so we handle that here:
419 if( appK2S.GetPath().Find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
420 {
421 appK2S.AppendDir( wxT( ".." ) );
422 appK2S.AppendDir( wxT( ".." ) );
423 appK2S.AppendDir( wxT( ".." ) );
424 appK2S.AppendDir( wxT( ".." ) );
425 appK2S.AppendDir( wxT( "MacOS" ) );
426 }
427#else
428 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
429 {
430 appK2S.RemoveLastDir();
431 appK2S.AppendDir( "kicad" );
432 }
433#endif
434
435 appK2S.SetName( wxT( "kicad-cli" ) );
436 appK2S.Normalize( FN_NORMALIZE_FLAGS );
437
438 wxString cmdK2S = wxT( "\"" );
439 cmdK2S.Append( appK2S.GetFullPath() );
440 cmdK2S.Append( wxT( "\"" ) );
441
442 cmdK2S.Append( wxT( " pcb" ) );
443 cmdK2S.Append( wxT( " export" ) );
444
445 if( fn.GetExt() == FILEEXT::GltfBinaryFileExtension )
446 cmdK2S.Append( wxT( " glb" ) );
447 else
448 cmdK2S.Append( wxT( " step" ) );
449
451 cmdK2S.Append( wxT( " --no-unspecified" ) );
452
453 if( GetNoDNPOption() )
454 cmdK2S.Append( wxT( " --no-dnp" ) );
455
456 if( GetSubstOption() )
457 cmdK2S.Append( wxT( " --subst-models" ) );
458
459 if( !m_optimizeStep )
460 cmdK2S.Append( wxT( " --no-optimize-step" ) );
461
462 if( m_exportTracks )
463 cmdK2S.Append( wxT( " --include-tracks" ) );
464
465 if( m_exportZones )
466 cmdK2S.Append( wxT( " --include-zones" ) );
467
468 // Note: for some reason, using \" to insert a quote in a format string, under MacOS
469 // wxString::Format does not work. So use a %c format in string
470 int quote = '\'';
471 int dblquote = '"';
472
473 switch( GetOriginOption() )
474 {
475 case STEP_ORIGIN_0:
476 wxFAIL_MSG( wxT( "Unsupported origin option: how did we get here?" ) );
477 break;
478
480 cmdK2S.Append( wxT( " --drill-origin" ) );
481 break;
482
484 cmdK2S.Append( wxT( " --grid-origin" ) );
485 break;
486
487 case STEP_ORIGIN_USER:
488 {
489 double xOrg = GetXOrg();
490 double yOrg = GetYOrg();
491
492 if( GetOrgUnitsChoice() == 1 )
493 {
494 // selected reference unit is in inches, and STEP units are mm
495 xOrg *= 25.4;
496 yOrg *= 25.4;
497 }
498
500 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
501 quote, xOrg, yOrg, quote ) );
502 break;
503 }
504
506 {
507 BOX2I bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
508 double xOrg = pcbIUScale.IUTomm( bbox.GetCenter().x );
509 double yOrg = pcbIUScale.IUTomm( bbox.GetCenter().y );
511
512 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
513 quote, xOrg, yOrg, quote ) );
514 break;
515 }
516 }
517
518 {
520 cmdK2S.Append( wxString::Format( wxT( " --min-distance=%c%.3fmm%c" ),
521 quote, tolerance, quote ) );
522 }
523
524 // Output file path.
525 cmdK2S.Append( wxString::Format( wxT( " -f -o %c%s%c" ),
526 dblquote, fn.GetFullPath(), dblquote ) );
527
528
529 // Input file path.
530 cmdK2S.Append( wxString::Format( wxT( " %c%s%c" ), dblquote, m_boardPath, dblquote ) );
531
532 wxLogTrace( traceKiCad2Step, wxT( "export step command: %s" ), cmdK2S );
533
534 DIALOG_EXPORT_STEP_LOG* log = new DIALOG_EXPORT_STEP_LOG( this, cmdK2S );
535 log->ShowModal();
536 Close();
537}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1425
FOOTPRINTS & Footprints()
Definition: board.h:318
const wxString & GetFileName() const
Definition: board.h:313
const Vec GetCenter() const
Definition: box2.h:196
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...
static wxString GetAutoSaveFilePrefix()
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.
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
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 StepFileAbrvExtension
static const std::string GltfBinaryFileExtension
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:565
see class PGM_BASE
@ LAST_PATH_STEP
Definition: project_file.h:50
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119
std::vector< FAB_LAYER_COLOR > dummy
constexpr double IUTomm(int iu) const
Definition: base_units.h:87
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
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