KiCad PCB EDA Suite
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-2022 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/choicdlg.h>
26#include <wx/log.h>
27#include <wx/stdpaths.h>
28#include <wx/process.h>
29#include <wx/string.h>
30
31#include <pgm_base.h>
32#include <board.h>
33#include <confirm.h>
35#include <footprint.h>
36#include <kiface_base.h>
37#include <locale_io.h>
38#include <math/vector3.h>
39#include <pcb_edit_frame.h>
40#include <pcbnew_settings.h>
41#include <project/project_file.h> // LAST_PATH_TYPE
42#include <reporter.h>
43#include <trace_helpers.h>
46#include <filename_resolver.h>
47
48
49#ifdef KICAD_STEP_EXPORT_LIB
50#include <kicad2step.h>
51#endif
52
53
55{
56public:
58 {
59 STEP_ORG_0, // absolute coordinates
60 STEP_ORG_PLOT_AXIS, // origin is plot/drill axis origin
61 STEP_ORG_GRID_AXIS, // origin is grid origin
62 STEP_ORG_BOARD_CENTER, // origin is board center
63 STEP_ORG_USER, // origin is entered by user
64 };
65
66 DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
68
69protected:
70 void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
71 void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
72 void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
73 void onExportButton( wxCommandEvent& aEvent ) override;
74
76 {
77 return m_STEP_OrgUnitChoice->GetSelection();
78 }
79
80 double GetXOrg() const
81 {
83 }
84
85 double GetYOrg()
86 {
88 }
89
91
93 {
94 return m_cbRemoveVirtual->GetValue();
95 }
96
98 {
99 return m_cbSubstModels->GetValue();
100 }
101
103 {
104 return m_cbOverwriteFile->GetValue();
105 }
106
107private:
109 STEP_ORG_OPT m_STEP_org_opt; // The last preference for STEP Origin:
110 bool m_noVirtual; // remember last preference for No Virtual Component
111 int m_OrgUnits; // remember last units for User Origin
112 double m_XOrg; // remember last User Origin X value
113 double m_YOrg; // remember last User Origin Y value
114 wxString m_boardPath; // path to the exported board file
115 static int m_toleranceLastChoice; // Store m_tolerance option during a session
116};
117
118
119int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
120
121
122DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
123 DIALOG_EXPORT_STEP_BASE( aParent )
124{
125 m_parent = aParent;
126 m_boardPath = aBoardPath;
127 m_sdbSizerCancel->SetLabel( _( "Close" ) );
128 m_sdbSizerOK->SetLabel( _( "Export" ) );
129 m_sdbSizer->Layout();
130
131 // Build default output file name
133
134 if( path.IsEmpty() )
135 {
136 wxFileName brdFile = m_parent->GetBoard()->GetFileName();
137 brdFile.SetExt( wxT( "step" ) );
138 path = brdFile.GetFullPath();
139 }
140
141 // Reset this picker bc wxFormBuilder doesn't allow untranslated strings
142 wxSizerItem* sizer_item = bSizerTop->GetItem( 1UL );
143 wxWindow* widget = sizer_item->GetWindow();
144 bSizerTop->Hide( widget );
145 widget->Destroy();
146
147 m_filePickerSTEP = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString,
148 _( "Select a STEP export filename" ),
149 _( "STEP files" ) + AddFileExtListToFilter( { "STEP", "STP" } ),
150 wxDefaultPosition,
151 wxSize( -1, -1 ), wxFLP_SAVE | wxFLP_USE_TEXTCTRL );
152 bSizerTop->Add( m_filePickerSTEP, 1, wxTOP | wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 5 );
153
154 m_filePickerSTEP->SetPath( path );
155
156 Layout();
157 bSizerSTEPFile->Fit( this );
158
159 SetFocus();
160
162
164
165 switch( m_STEP_org_opt )
166 {
167 default: break;
168 case STEP_ORG_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
169 case STEP_ORG_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
170 case STEP_ORG_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
171 case STEP_ORG_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
172 }
173
178
179 m_cbRemoveVirtual->SetValue( m_noVirtual );
182
183 m_STEP_OrgUnitChoice->SetSelection( m_OrgUnits );
184 wxString tmpStr;
185 tmpStr << m_XOrg;
186 m_STEP_Xorg->SetValue( tmpStr );
187 tmpStr = wxEmptyString;
188 tmpStr << m_YOrg;
189 m_STEP_Yorg->SetValue( tmpStr );
190
191 wxString bad_scales;
192 size_t bad_count = 0;
193
194 for( FOOTPRINT* fp : aParent->GetBoard()->Footprints() )
195 {
196 for( const FP_3DMODEL& model : fp->Models() )
197 {
198
199 if( model.m_Scale.x != 1.0 ||
200 model.m_Scale.y != 1.0 ||
201 model.m_Scale.z != 1.0 )
202 {
203 bad_scales.Append( wxS("\n") );
204 bad_scales.Append( model.m_Filename );
205 bad_count++;
206 }
207 }
208
209 if( bad_count >= 5 )
210 break;
211 }
212
213 if( !bad_scales.empty()
214 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning )
215 {
216 wxString extendedMsg = _( "Non-unity scaled models:" ) + wxT( "\n" ) + bad_scales;
217
218 KIDIALOG msgDlg( m_parent, _( "Scaled models detected. "
219 "Model scaling is not reliable for mechanical export." ),
220 _( "Model Scale Warning" ), wxOK | wxICON_WARNING );
221 msgDlg.SetExtendedMessage( extendedMsg );
222 msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
223
224 msgDlg.ShowModal();
225
226 if( msgDlg.DoNotShowAgain() )
227 Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning = true;
228 }
229
230 if( m_toleranceLastChoice >= 0 )
231 m_tolerance->SetSelection( m_toleranceLastChoice );
232
233 // Now all widgets have the size fixed, call FinishDialogSettings
235}
236
237
239{
240 GetOriginOption(); // Update m_STEP_org_opt member.
241
243
244 cfg->m_ExportStep.origin_mode = static_cast<int>( m_STEP_org_opt );
245 cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
248
249 double val = 0.0;
250
251 m_STEP_Xorg->GetValue().ToDouble( &val );
252 cfg->m_ExportStep.origin_x = val;
253
254 m_STEP_Yorg->GetValue().ToDouble( &val );
255 cfg->m_ExportStep.origin_y = val;
256
257 cfg->m_ExportStep.no_virtual = m_cbRemoveVirtual->GetValue();
258
259 m_toleranceLastChoice = m_tolerance->GetSelection();
260}
261
262
264{
266
267 if( m_rbDrillAndPlotOrigin->GetValue() )
269 else if( m_rbGridOrigin->GetValue() )
271 else if( m_rbUserDefinedOrigin->GetValue() )
273 else if( m_rbBoardCenterOrigin->GetValue() )
275
276 return m_STEP_org_opt;
277}
278
279
280void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
281{
282 wxFileName brdFile = GetBoard()->GetFileName();
283
284 if( GetScreen()->IsContentModified() || brdFile.GetFullPath().empty() )
285 {
286 if( !doAutoSave() )
287 {
288 DisplayErrorMessage( this, _( "STEP export failed! "
289 "Please save the PCB and try again" ) );
290 return;
291 }
292
293 // Use auto-saved board for export
294 brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
295 }
296
297 DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
298 dlg.ShowModal();
299}
300
301
302void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
303{
304 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
305}
306
307
308void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
309{
310 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
311}
312
313
314void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
315{
316 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
317}
318
319
320void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
321{
323
324 double tolerance; // default value in mm
325 m_toleranceLastChoice = m_tolerance->GetSelection();
326
327 switch( m_tolerance->GetSelection() )
328 {
329 case 0: tolerance = 0.001; break;
330 default:
331 case 1: tolerance = 0.01; break;
332 case 2: tolerance = 0.1; break;
333 }
334
335 SHAPE_POLY_SET outline;
336 wxString msg;
337
338 // Check if the board outline is continuous
339 // max dist from one endPt to next startPt to build a closed shape:
340 int chainingEpsilon = pcbIUScale.mmToIU( tolerance );
341
342 // Arc to segment approx error (not critical here: we do not use the outline shape):
343 int maxError = pcbIUScale.mmToIU( 0.005 );
344 bool success = BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, maxError,
345 chainingEpsilon, nullptr );
346 if( !success )
347 {
349 _( "Board outline is missing or not closed using %.3f mm tolerance.\n"
350 "Run DRC for a full analysis." ), tolerance ) );
351 return;
352 }
353
354 wxFileName fn = m_filePickerSTEP->GetFileName();
355
356 if( fn.FileExists() && !GetOverwriteFile() )
357 {
358 msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
359 fn.GetFullPath() );
360
361 if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
362 return;
363 }
364
365 FILENAME_RESOLVER* fnResolver = m_parent->Prj().Get3DFilenameResolver();
366
367 fnResolver->WritePathList( wxStandardPaths::Get().GetTempDir(), wxT( "ExportPaths.cfg" ),
368 true );
369
371 double xOrg = 0.0;
372 double yOrg = 0.0;
373
374#ifndef KICAD_STEP_EXPORT_LIB
375 wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
376#ifdef __WXMAC__
377 // On macOS, we have standalone applications inside the main bundle, so we handle that here:
378 if( appK2S.GetPath().Find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
379 {
380 appK2S.AppendDir( wxT( ".." ) );
381 appK2S.AppendDir( wxT( ".." ) );
382 appK2S.AppendDir( wxT( ".." ) );
383 appK2S.AppendDir( wxT( ".." ) );
384 appK2S.AppendDir( wxT( "MacOS" ) );
385 }
386#endif
387
388 appK2S.SetName( wxT( "kicad2step" ) );
389
390 wxString cmdK2S = wxT( "\"" );
391 cmdK2S.Append( appK2S.GetFullPath() );
392 cmdK2S.Append( wxT( "\"" ) );
393
394 if( GetNoVirtOption() )
395 cmdK2S.Append( wxT( " --no-virtual" ) );
396
397 if( GetSubstOption() )
398 cmdK2S.Append( wxT( " --subst-models" ) );
399
400 // Note: for some reason, using \" to insert a quote in a format string, under MacOS
401 // wxString::Format does not work. So use a %c format in string
402 int quote = '"';
403
404 switch( orgOpt )
405 {
407 break;
408
410 cmdK2S.Append( wxT( " --drill-origin" ) );
411 break;
412
414 cmdK2S.Append( wxT( " --grid-origin" ) );
415 break;
416
418 {
419 xOrg = GetXOrg();
420 yOrg = GetYOrg();
421
422 if( GetOrgUnitsChoice() == 1 )
423 {
424 // selected reference unit is in inches, and STEP units are mm
425 xOrg *= 25.4;
426 yOrg *= 25.4;
427 }
428
430 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
431 quote, xOrg, yOrg, quote ) );
432 break;
433 }
434
436 {
437 BOX2I bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
438 xOrg = pcbIUScale.IUTomm( bbox.GetCenter().x );
439 yOrg = pcbIUScale.IUTomm( bbox.GetCenter().y );
441 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
442 quote, xOrg, yOrg, quote ) );
443 break;
444 }
445 }
446
447 {
449 cmdK2S.Append( wxString::Format( wxT( " --min-distance=%c%.3fmm%c" ),
450 quote, tolerance, quote ) );
451 }
452
453 // Input file path.
454 cmdK2S.Append( wxString::Format( wxT( " -f -o %c%s%c" ),
455 quote, m_filePickerSTEP->GetPath(), quote ) );
456
457
458 // Output file path.
459 cmdK2S.Append( wxString::Format( wxT( " %c%s%c" ), quote, m_boardPath, quote ) );
460
461 wxLogTrace( traceKiCad2Step, wxT( "KiCad2Step command: %s" ), cmdK2S );
462 wxExecute( cmdK2S, wxEXEC_ASYNC | wxEXEC_SHOW_CONSOLE );
463
464#else
465
466 KICAD2MCAD_PRMS params;
467 params.m_filename = m_boardPath;
468 params.m_outputFile = m_filePickerSTEP->GetPath();
469
470 params.m_includeVirtual = !GetNoVirtOption();
471
472 params.m_substModels = GetSubstOption();
473 params.m_minDistance = tolerance;
474 params.m_overwrite = true;
475
476 switch( orgOpt )
477 {
479 break;
480
482 params.m_useDrillOrigin = true;
483 break;
484
486 params.m_useGridOrigin = true;
487 break;
488
490 {
491 xOrg = GetXOrg();
492 yOrg = GetYOrg();
493
494 if( GetOrgUnitsChoice() == 1 )
495 {
496 // selected reference unit is in inches, and STEP units are mm
497 xOrg *= 25.4;
498 yOrg *= 25.4;
499 }
500
501 params.m_xOrigin = xOrg;
502 params.m_yOrigin = yOrg;
503 break;
504 }
505
507 {
508 BOX2I bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
509 xOrg = pcbIUScale.IUTomm( bbox.GetCenter().x );
510 yOrg = pcbIUScale.IUTomm( bbox.GetCenter().y );
511 params.m_xOrigin = xOrg;
512 params.m_yOrigin = yOrg;
513 break;
514 }
515 }
516
517 KICAD2STEP converter( params );
518 converter.Run();
519#endif
520
521 aEvent.Skip(); // Close the dialog
522}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1167
FOOTPRINTS & Footprints()
Definition: board.h:307
const wxString & GetFileName() const
Definition: board.h:302
const Vec GetCenter() const
Definition: box2.h:195
Class DIALOG_EXPORT_STEP_BASE.
wxStdDialogButtonSizer * m_sdbSizer
wxFilePickerCtrl * m_filePickerSTEP
wxRadioButton * m_rbDrillAndPlotOrigin
PCB_EDIT_FRAME * m_parent
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
STEP_ORG_OPT GetOriginOption()
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 finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
static wxString GetAutoSaveFilePrefix()
Provide an extensible class to resolve 3D model paths.
bool WritePathList(const wxString &aDir, const wxString &aFilename, bool aResolvePaths)
Write the current path list to a config file.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition: confirm.h:46
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:41
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 SetValue(const wxString &aValue) override
Set a new value in evaluator buffer, and display it in the wxTextCtrl.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:299
This file is part of the common library.
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Extracts the board outlines and build a closed polygon from lines, arcs and circle items on edge cut ...
#define _(s)
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
const wxChar *const traceKiCad2Step
Flag to enable KiCad2Step debug tracing.
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:451
see class PGM_BASE
@ LAST_PATH_STEP
Definition: project_file.h:49
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:74
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
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.
Definition of file extensions used in Kicad.