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-2019 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/stdpaths.h>
27 
28 #include "pcb_edit_frame.h"
29 #include "kiface_i.h"
30 #include "confirm.h"
31 #include "reporter.h"
32 #include "class_board.h"
34 #include <pcbnew_settings.h>
35 #include <widgets/text_ctrl_eval.h>
36 #include <wx_html_report_panel.h>
37 
38 
40 {
41 public:
43  {
44  STEP_ORG_0, // absolute coordinates
45  STEP_ORG_PLOT_AXIS, // origin is plot/drill axis origin
46  STEP_ORG_GRID_AXIS, // origin is grid origin
47  STEP_ORG_BOARD_CENTER, // origin is board center
48  STEP_ORG_USER, // origin is entered by user
49  };
50 
51 private:
53  // The last preference for STEP Origin:
55  bool m_noVirtual; // remember last preference for No Virtual Component
56  int m_OrgUnits; // remember last units for User Origin
57  double m_XOrg; // remember last User Origin X value
58  double m_YOrg; // remember last User Origin Y value
59  wxString m_boardPath; // path to the exported board file
60 
61 protected:
62  void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
63  void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
64  void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
65  void onExportButton( wxCommandEvent& aEvent ) override;
66 
67  int GetOrgUnitsChoice() const
68  {
69  return m_STEP_OrgUnitChoice->GetSelection();
70  }
71 
72  double GetXOrg() const
73  {
75  }
76 
77  double GetYOrg()
78  {
80  }
81 
83 
85  {
86  return m_cbRemoveVirtual->GetValue();
87  }
88 
89 public:
90  DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
91 
93  {
94  GetOriginOption(); // Update m_STEP_org_opt member.
95 
96  auto cfg = m_parent->GetSettings();
97 
98  cfg->m_ExportStep.origin_mode = static_cast<int>( m_STEP_org_opt );
99  cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
100 
101  double val = 0.0;
102 
103  m_STEP_Xorg->GetValue().ToDouble( &val );
104  cfg->m_ExportStep.origin_x = val;
105 
106  m_STEP_Yorg->GetValue().ToDouble( &val );
107  cfg->m_ExportStep.origin_y = val;
108 
109  cfg->m_ExportStep.no_virtual = m_cbRemoveVirtual->GetValue();
110  }
111 };
112 
113 
114 DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
115  DIALOG_EXPORT_STEP_BASE( aParent )
116 {
117  m_parent = aParent;
118  m_boardPath = aBoardPath;
119  m_sdbSizerCancel->SetLabel( _( "Close" ) );
120  m_sdbSizerOK->SetLabel( _( "Export" ) );
121  m_sdbSizer->Layout();
122 
123  // Build default output file name
124  wxString path = m_parent->GetLastPath( LAST_PATH_STEP );
125 
126  if( path.IsEmpty() )
127  {
128  wxFileName brdFile = m_parent->GetBoard()->GetFileName();
129  brdFile.SetExt( "step" );
130  path = brdFile.GetFullPath();
131  }
132 
133  m_filePickerSTEP->SetPath( path );
134 
135  SetFocus();
136 
137  auto cfg = m_parent->GetSettings();
138 
139  m_STEP_org_opt = static_cast<STEP_ORG_OPT>( cfg->m_ExportStep.origin_mode );
140 
141  switch( m_STEP_org_opt )
142  {
143  default: break;
144  case STEP_ORG_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
145  case STEP_ORG_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
146  case STEP_ORG_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
147  case STEP_ORG_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
148  }
149 
150  m_OrgUnits = cfg->m_ExportStep.origin_units;
151  m_XOrg = cfg->m_ExportStep.origin_x;
152  m_YOrg = cfg->m_ExportStep.origin_y;
153  m_noVirtual = cfg->m_ExportStep.no_virtual;
154 
155  m_cbRemoveVirtual->SetValue( m_noVirtual );
156 
157  m_STEP_OrgUnitChoice->SetSelection( m_OrgUnits );
158  wxString tmpStr;
159  tmpStr << m_XOrg;
160  m_STEP_Xorg->SetValue( tmpStr );
161  tmpStr = "";
162  tmpStr << m_YOrg;
163  m_STEP_Yorg->SetValue( tmpStr );
164 
165  // Now all widgets have the size fixed, call FinishDialogSettings
167 }
168 
169 
171 {
173 
174  if( m_rbDrillAndPlotOrigin->GetValue() )
176  else if( m_rbGridOrigin->GetValue() )
178  else if( m_rbUserDefinedOrigin->GetValue() )
180  else if( m_rbBoardCenterOrigin->GetValue() )
182 
183  return m_STEP_org_opt;
184 }
185 
186 
187 void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
188 {
189  wxFileName brdFile = GetBoard()->GetFileName();
190 
191  if( GetScreen()->IsModify() || brdFile.GetFullPath().empty() )
192  {
193  if( !doAutoSave() )
194  {
195  DisplayErrorMessage( this,
196  _( "STEP export failed! Please save the PCB and try again" ) );
197  return;
198  }
199 
200  // Use auto-saved board for export
201  brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
202  }
203 
204  DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
205  dlg.ShowModal();
206 }
207 
208 
209 void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
210 {
211  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
212 }
213 
214 
215 void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
216 {
217  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
218 }
219 
220 
221 void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
222 {
223  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
224 }
225 
226 extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
227  wxString* aErrorText, unsigned int aTolerance,
228  wxPoint* aErrorLocation = nullptr );
229 
230 void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
231 {
233 
234  SHAPE_POLY_SET outline;
235  wxString msg;
236 
237  // Check if the board outline is continuous
238  if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, &msg, Millimeter2iu( 0.01 ) ) )
239  {
240  DisplayErrorMessage( this, _( "Cannot determine the board outline." ), msg );
241  return;
242  }
243 
244  wxFileName fn = m_filePickerSTEP->GetFileName();
245 
246  if( fn.FileExists() )
247  {
248  msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
249  fn.GetFullPath().GetData() );
250 
251  if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
252  return;
253  }
254 
256  double xOrg = 0.0;
257  double yOrg = 0.0;
258 
259  wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
260 
261 #ifdef __WXMAC__
262  // On macOS, we have standalone applications inside the main bundle, so we handle that here:
263  if( appK2S.GetPath().find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
264  {
265  appK2S.AppendDir( ".." );
266  appK2S.AppendDir( ".." );
267  appK2S.AppendDir( ".." );
268  appK2S.AppendDir( ".." );
269  appK2S.AppendDir( "MacOS" );
270  }
271 #endif
272 
273  appK2S.SetName( "kicad2step" );
274 
275  wxString cmdK2S = "\"";
276  cmdK2S.Append( appK2S.GetFullPath() );
277  cmdK2S.Append( "\"" );
278 
279  if( GetNoVirtOption() )
280  cmdK2S.Append( " --no-virtual" );
281 
282  switch( orgOpt )
283  {
285  break;
286 
288  cmdK2S.Append( " --drill-origin" );
289  break;
290 
292  cmdK2S.Append( " --grid-origin" );
293  break;
294 
296  {
297  xOrg = GetXOrg();
298  yOrg = GetYOrg();
299 
300  if( GetOrgUnitsChoice() == 1 )
301  {
302  // selected reference unit is in inches, and STEP units are mm
303  xOrg *= 25.4;
304  yOrg *= 25.4;
305  }
306 
308  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
309  }
310  break;
311 
313  {
314  EDA_RECT bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
315  xOrg = Iu2Millimeter( bbox.GetCenter().x );
316  yOrg = Iu2Millimeter( bbox.GetCenter().y );
318  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
319  }
320  break;
321  }
322 
323  if( m_tolerance->GetSelection() != 1 )
324  {
326  double tolerance = 0.001 * std::pow<double>( 10.0, m_tolerance->GetSelection() - 1 );
327  cmdK2S.Append( wxString::Format( " --min-distance=\"%.4f mm\"", tolerance ) );
328  }
329 
330  cmdK2S.Append( " -f -o " );
331  cmdK2S.Append( wxString::Format("\"%s\"", m_filePickerSTEP->GetPath() ) ); // input file path
332 
333  cmdK2S.Append( " " );
334  cmdK2S.Append( wxString::Format("\"%s\"", m_boardPath ) ); // output file path
335 
336  int result = 0;
337  bool success = false;
338  wxArrayString output, errors;
339  REPORTER& reporter = m_messagesPanel->Reporter();
340  reporter.ReportHead( wxString::Format( _( "Executing '%s'" ), cmdK2S ), RPT_SEVERITY_ACTION );
341 
342  {
343  wxBusyCursor dummy;
344  result = wxExecute( cmdK2S, output, errors, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE );
345  }
346 
347  // Check the output log for an indication of success,
348  // the value returned by wxExecute is not conclusive
349  for( auto& l : output )
350  {
351  if( l.Contains( "Done" ) )
352  {
353  success = true;
354  break;
355  }
356  }
357 
358  for( auto& err : errors )
359  reporter.Report( err, RPT_SEVERITY_WARNING );
360 
361  if( result ) // Any troubles?
362  {
363  if( !success )
364  {
365  reporter.ReportTail( _( "Unable to create STEP file. Check that the board has a "
366  "valid outline and models." ), RPT_SEVERITY_ERROR );
367  }
368  else
369  {
370  reporter.ReportTail( _( "STEP file has been created, but there are warnings." ),
372  }
373  }
374  else
375  {
376  reporter.ReportTail( _( "STEP file has been created successfully." ), RPT_SEVERITY_INFO );
377  }
378 }
DIALOG_EXPORT_STEP m_ExportStep
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:252
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:202
This file is part of the common library.
bool doAutoSave() override
Function doAutoSave performs auto save when the board has been modified and not saved within the auto...
void OnExportSTEP(wxCommandEvent &event)
Function OnExportSTEP Exports the current BOARD to a STEP assembly.
wxRadioButton * m_rbDrillAndPlotOrigin
PCB_EDIT_FRAME * m_parent
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Function ReportTail Places the report at the end of the list, for objects that support report orderin...
Definition: reporter.h:92
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:62
const wxString & GetFileName() const
Definition: class_board.h:218
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
void SetValue(const wxString &aValue) override
Set a new value in evaluator buffer, and display it in the wxTextCtrl.
wxString GetLastPath(LAST_PATH_TYPE aType)
Get the last path for a particular type.
STEP_ORG_OPT GetOriginOption()
void onExportButton(wxCommandEvent &aEvent) override
wxFilePickerCtrl * m_filePickerSTEP
SHAPE_POLY_SET.
REPORTER & Reporter()
returns the reporter object that reports to this panel
void onUpdateUnits(wxUpdateUIEvent &aEvent) override
WX_HTML_REPORT_PANEL * m_messagesPanel
wxStdDialogButtonSizer * m_sdbSizer
virtual REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Function ReportHead Places the report at the beginning of the list for objects that support ordering.
Definition: reporter.h:101
static wxString GetAutoSaveFilePrefix()
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:163
#define _(s)
Definition: 3d_actions.cpp:33
Class DIALOG_EXPORT_STEP_BASE.
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
PCB_EDIT_FRAME is the main frame for Pcbnew.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation=nullptr)
void onUpdateYPos(wxUpdateUIEvent &aEvent) override
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Function ComputeBoundingBox calculates the bounding box containing all board items (or board edge seg...
BOARD * GetBoard() const
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:331
const wxPoint GetCenter() const
Definition: eda_rect.h:117
void SetLastPath(LAST_PATH_TYPE aType, const wxString &aLastPath)
Set the path of the last file successfully read.
DIALOG_EXPORT_STEP(PCB_EDIT_FRAME *aParent, const wxString &aBoardPath)
PCBNEW_SETTINGS * GetSettings()