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>
36#include <footprint.h>
37#include <kiface_base.h>
38#include <locale_io.h>
39#include <math/vector3.h>
40#include <pcb_edit_frame.h>
41#include <pcbnew_settings.h>
42#include <project/project_file.h> // LAST_PATH_TYPE
43#include <reporter.h>
44#include <trace_helpers.h>
47#include <filename_resolver.h>
48
49
51{
52public:
54 {
55 STEP_ORIGIN_0, // absolute coordinates
56 STEP_ORIGIN_PLOT_AXIS, // origin is plot/drill axis origin
57 STEP_ORIGIN_GRID_AXIS, // origin is grid origin
58 STEP_ORIGIN_BOARD_CENTER, // origin is board center
59 STEP_ORIGIN_USER, // origin is entered by user
60 };
61
62 DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
64
65protected:
66 void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
67 void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
68 void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
69 void onExportButton( wxCommandEvent& aEvent ) override;
70
72 {
73 return m_STEP_OrgUnitChoice->GetSelection();
74 }
75
76 double GetXOrg() const
77 {
79 }
80
81 double GetYOrg()
82 {
84 }
85
87
89 {
90 return m_cbRemoveVirtual->GetValue();
91 }
92
94 {
95 return m_cbSubstModels->GetValue();
96 }
97
99 {
100 return m_cbOverwriteFile->GetValue();
101 }
102
103private:
105 STEP_ORIGIN_OPTION m_origin; // The last preference for STEP origin option
106 double m_userOriginX; // remember last User Origin X value
107 double m_userOriginY; // remember last User Origin Y value
108 int m_originUnits; // remember last units for User Origin
109 bool m_noVirtual; // remember last preference for No Virtual Component
110 wxString m_boardPath; // path to the exported board file
111 static int m_toleranceLastChoice; // Store m_tolerance option during a session
112};
113
114
115int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
116
117
118DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
119 DIALOG_EXPORT_STEP_BASE( aParent )
120{
121 m_parent = aParent;
122 m_boardPath = aBoardPath;
123 m_sdbSizerCancel->SetLabel( _( "Close" ) );
124 m_sdbSizerOK->SetLabel( _( "Export" ) );
125 m_sdbSizer->Layout();
126
127 // Build default output file name
129
130 if( path.IsEmpty() )
131 {
132 wxFileName brdFile = m_parent->GetBoard()->GetFileName();
133 brdFile.SetExt( wxT( "step" ) );
134 path = brdFile.GetFullPath();
135 }
136
137 // Reset this picker bc wxFormBuilder doesn't allow untranslated strings
138 wxSizerItem* sizer_item = bSizerTop->GetItem( 1UL );
139 wxWindow* widget = sizer_item->GetWindow();
140 bSizerTop->Hide( widget );
141 widget->Destroy();
142
143 m_filePickerSTEP = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString,
144 _( "Select a STEP export filename" ),
145 _( "STEP files" ) + AddFileExtListToFilter( { "STEP", "STP" } ),
146 wxDefaultPosition,
147 wxSize( -1, -1 ), wxFLP_SAVE | wxFLP_USE_TEXTCTRL );
148 bSizerTop->Add( m_filePickerSTEP, 1, wxTOP | wxRIGHT | wxLEFT | wxALIGN_CENTER_VERTICAL, 5 );
149
150 m_filePickerSTEP->SetPath( path );
151
152 Layout();
153 bSizerSTEPFile->Fit( this );
154
155 SetFocus();
156
158
160
161 switch( m_origin )
162 {
163 default:
164 case STEP_ORIGIN_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
165 case STEP_ORIGIN_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
166 case STEP_ORIGIN_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
167 case STEP_ORIGIN_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
168 }
169
174
175 m_cbRemoveVirtual->SetValue( m_noVirtual );
178
179 m_STEP_OrgUnitChoice->SetSelection( m_originUnits );
180 wxString tmpStr;
181 tmpStr << m_userOriginX;
182 m_STEP_Xorg->SetValue( tmpStr );
183 tmpStr = wxEmptyString;
184 tmpStr << m_userOriginY;
185 m_STEP_Yorg->SetValue( tmpStr );
186
187 wxString bad_scales;
188 size_t bad_count = 0;
189
190 for( FOOTPRINT* fp : aParent->GetBoard()->Footprints() )
191 {
192 for( const FP_3DMODEL& model : fp->Models() )
193 {
194
195 if( model.m_Scale.x != 1.0 ||
196 model.m_Scale.y != 1.0 ||
197 model.m_Scale.z != 1.0 )
198 {
199 bad_scales.Append( wxS("\n") );
200 bad_scales.Append( model.m_Filename );
201 bad_count++;
202 }
203 }
204
205 if( bad_count >= 5 )
206 break;
207 }
208
209 if( !bad_scales.empty()
210 && !Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning )
211 {
212 wxString extendedMsg = _( "Non-unity scaled models:" ) + wxT( "\n" ) + bad_scales;
213
214 KIDIALOG msgDlg( m_parent, _( "Scaled models detected. "
215 "Model scaling is not reliable for mechanical export." ),
216 _( "Model Scale Warning" ), wxOK | wxICON_WARNING );
217 msgDlg.SetExtendedMessage( extendedMsg );
218 msgDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
219
220 msgDlg.ShowModal();
221
222 if( msgDlg.DoNotShowAgain() )
223 Pgm().GetCommonSettings()->m_DoNotShowAgain.scaled_3d_models_warning = true;
224 }
225
226 if( m_toleranceLastChoice >= 0 )
227 m_tolerance->SetSelection( m_toleranceLastChoice );
228
229 // Now all widgets have the size fixed, call FinishDialogSettings
231}
232
233
235{
236 GetOriginOption(); // Update m_origin member.
237
239
240 cfg->m_ExportStep.origin_mode = static_cast<int>( m_origin );
241 cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
244
245 double val = 0.0;
246
247 m_STEP_Xorg->GetValue().ToDouble( &val );
248 cfg->m_ExportStep.origin_x = val;
249
250 m_STEP_Yorg->GetValue().ToDouble( &val );
251 cfg->m_ExportStep.origin_y = val;
252
253 cfg->m_ExportStep.no_virtual = m_cbRemoveVirtual->GetValue();
254
255 m_toleranceLastChoice = m_tolerance->GetSelection();
256}
257
258
260{
262
263 if( m_rbDrillAndPlotOrigin->GetValue() )
265 else if( m_rbGridOrigin->GetValue() )
267 else if( m_rbUserDefinedOrigin->GetValue() )
269 else if( m_rbBoardCenterOrigin->GetValue() )
271
272 return m_origin;
273}
274
275
276void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
277{
278 wxFileName brdFile = GetBoard()->GetFileName();
279
280 if( GetScreen()->IsContentModified() || brdFile.GetFullPath().empty() )
281 {
282 if( !doAutoSave() )
283 {
284 DisplayErrorMessage( this, _( "STEP export failed! "
285 "Please save the PCB and try again" ) );
286 return;
287 }
288
289 // Use auto-saved board for export
290 brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
291 }
292
293 DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
294 dlg.ShowModal();
295}
296
297
298void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
299{
300 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
301}
302
303
304void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
305{
306 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
307}
308
309
310void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
311{
312 aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
313}
314
315
316void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
317{
319
320 double tolerance; // default value in mm
321 m_toleranceLastChoice = m_tolerance->GetSelection();
322
323 switch( m_tolerance->GetSelection() )
324 {
325 case 0: tolerance = 0.001; break;
326 default:
327 case 1: tolerance = 0.01; break;
328 case 2: tolerance = 0.1; break;
329 }
330
331 SHAPE_POLY_SET outline;
332 wxString msg;
333
334 // Check if the board outline is continuous
335 // max dist from one endPt to next startPt to build a closed shape:
336 int chainingEpsilon = pcbIUScale.mmToIU( tolerance );
337
338 // Arc to segment approx error (not critical here: we do not use the outline shape):
339 int maxError = pcbIUScale.mmToIU( 0.005 );
340 bool success = BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, maxError,
341 chainingEpsilon, nullptr );
342 if( !success )
343 {
345 _( "Board outline is missing or not closed using %.3f mm tolerance.\n"
346 "Run DRC for a full analysis." ), tolerance ) );
347 return;
348 }
349
350 wxFileName fn = m_filePickerSTEP->GetFileName();
351
352 if( fn.FileExists() && !GetOverwriteFile() )
353 {
354 msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
355 fn.GetFullPath() );
356
357 if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
358 return;
359 }
360
361 wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
362#ifdef __WXMAC__
363 // On macOS, we have standalone applications inside the main bundle, so we handle that here:
364 if( appK2S.GetPath().Find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
365 {
366 appK2S.AppendDir( wxT( ".." ) );
367 appK2S.AppendDir( wxT( ".." ) );
368 appK2S.AppendDir( wxT( ".." ) );
369 appK2S.AppendDir( wxT( ".." ) );
370 appK2S.AppendDir( wxT( "MacOS" ) );
371 }
372#else
373 if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
374 {
375 appK2S.RemoveLastDir();
376 appK2S.AppendDir( "kicad" );
377 }
378#endif
379
380 appK2S.SetName( wxT( "kicad-cli" ) );
381 appK2S.Normalize( FN_NORMALIZE_FLAGS );
382
383 wxString cmdK2S = wxT( "\"" );
384 cmdK2S.Append( appK2S.GetFullPath() );
385 cmdK2S.Append( wxT( "\"" ) );
386
387 cmdK2S.Append( wxT( " pcb" ) );
388 cmdK2S.Append( wxT( " export" ) );
389 cmdK2S.Append( wxT( " step" ) );
390
391 if( GetNoVirtOption() )
392 cmdK2S.Append( wxT( " --no-virtual" ) );
393
394 if( GetSubstOption() )
395 cmdK2S.Append( wxT( " --subst-models" ) );
396
397 // Note: for some reason, using \" to insert a quote in a format string, under MacOS
398 // wxString::Format does not work. So use a %c format in string
399 int quote = '\'';
400 int dblquote = '"';
401
402 switch( GetOriginOption() )
403 {
404 case STEP_ORIGIN_0:
405 wxFAIL_MSG( wxT( "Unsupported origin option: how did we get here?" ) );
406 break;
407
409 cmdK2S.Append( wxT( " --drill-origin" ) );
410 break;
411
413 cmdK2S.Append( wxT( " --grid-origin" ) );
414 break;
415
416 case STEP_ORIGIN_USER:
417 {
418 double xOrg = GetXOrg();
419 double yOrg = GetYOrg();
420
421 if( GetOrgUnitsChoice() == 1 )
422 {
423 // selected reference unit is in inches, and STEP units are mm
424 xOrg *= 25.4;
425 yOrg *= 25.4;
426 }
427
429 cmdK2S.Append( wxString::Format( wxT( " --user-origin=%c%.6fx%.6fmm%c" ),
430 quote, xOrg, yOrg, quote ) );
431 break;
432 }
433
435 {
436 BOX2I bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
437 double xOrg = pcbIUScale.IUTomm( bbox.GetCenter().x );
438 double yOrg = pcbIUScale.IUTomm( bbox.GetCenter().y );
440
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 dblquote, m_filePickerSTEP->GetPath(), dblquote ) );
456
457
458 // Output file path.
459 cmdK2S.Append( wxString::Format( wxT( " %c%s%c" ), dblquote, m_boardPath, dblquote ) );
460
461 wxLogTrace( traceKiCad2Step, wxT( "export step command: %s" ), cmdK2S );
462
463 DIALOG_EXPORT_STEP_LOG* log = new DIALOG_EXPORT_STEP_LOG( this, cmdK2S );
464 log->ShowModal();
465 Close();
466}
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:1231
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
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 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:46
bool DoNotShowAgain() const
Definition: confirm.cpp:84
void DoNotShowCheckbox(wxString file, int line)
Checks the 'do not show again' setting for the dialog.
Definition: confirm.cpp:76
int ShowModal() override
Definition: confirm.cpp:120
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:325
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:456
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
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:111
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.
Definition of file extensions used in Kicad.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition: wx_filename.h:38