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 <widgets/text_ctrl_eval.h>
35 #include <wx_html_report_panel.h>
36 
37 #define OPTKEY_STEP_ORIGIN_OPT "STEP_Origin_Opt"
38 #define OPTKEY_STEP_UORG_UNITS "STEP_UserOriginUnits"
39 #define OPTKEY_STEP_UORG_X "STEP_UserOriginX"
40 #define OPTKEY_STEP_UORG_Y "STEP_UserOriginY"
41 #define OPTKEY_STEP_NOVIRT "STEP_NoVirtual"
42 
43 
45 {
46 public:
48  {
49  STEP_ORG_0, // absolute coordinates
50  STEP_ORG_PLOT_AXIS, // origin is plot/drill axis origin
51  STEP_ORG_GRID_AXIS, // origin is grid origin
52  STEP_ORG_BOARD_CENTER, // origin is board center
53  STEP_ORG_USER, // origin is entered by user
54  };
55 
56 private:
58  wxConfigBase* m_config;
59  // The last preference for STEP Origin:
61  bool m_noVirtual; // remember last preference for No Virtual Component
62  int m_OrgUnits; // remember last units for User Origin
63  double m_XOrg; // remember last User Origin X value
64  double m_YOrg; // remember last User Origin Y value
65  wxString m_boardPath; // path to the exported board file
66 
67 protected:
68  void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
69  void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
70  void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
71  void onExportButton( wxCommandEvent& aEvent ) override;
72 
73  int GetOrgUnitsChoice() const
74  {
75  return m_STEP_OrgUnitChoice->GetSelection();
76  }
77 
78  double GetXOrg() const
79  {
81  }
82 
83  double GetYOrg()
84  {
86  }
87 
89 
91  {
92  return m_cbRemoveVirtual->GetValue();
93  }
94 
95 public:
96  DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
97 
99  {
100  GetOriginOption(); // Update m_STEP_org_opt member.
102  m_config->Write( OPTKEY_STEP_NOVIRT, m_cbRemoveVirtual->GetValue() );
103  m_config->Write( OPTKEY_STEP_UORG_UNITS, m_STEP_OrgUnitChoice->GetSelection() );
104  m_config->Write( OPTKEY_STEP_UORG_X, m_STEP_Xorg->GetValue() );
105  m_config->Write( OPTKEY_STEP_UORG_Y, m_STEP_Yorg->GetValue() );
106  }
107 };
108 
109 
110 DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
111  DIALOG_EXPORT_STEP_BASE( aParent )
112 {
113  m_parent = aParent;
114  m_boardPath = aBoardPath;
116  m_sdbSizerCancel->SetLabel( _( "Close" ) );
117  m_sdbSizerOK->SetLabel( _( "Export" ) );
118  m_sdbSizer->Layout();
119 
120  // Build default output file name
121  wxString path = m_parent->GetLastPath( LAST_PATH_STEP );
122 
123  if( path.IsEmpty() )
124  {
125  wxFileName brdFile = m_parent->GetBoard()->GetFileName();
126  brdFile.SetExt( "step" );
127  path = brdFile.GetFullPath();
128  }
129 
130  m_filePickerSTEP->SetPath( path );
131 
132  SetFocus();
133 
135  int tmp = STEP_ORG_0;
136 
137  if( m_config->Read( OPTKEY_STEP_ORIGIN_OPT, &tmp ) )
139 
140  switch( m_STEP_org_opt )
141  {
142  default: break;
143  case STEP_ORG_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
144  case STEP_ORG_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
145  case STEP_ORG_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
146  case STEP_ORG_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
147  }
148 
150  m_config->Read( OPTKEY_STEP_UORG_X, &m_XOrg, 0.0 );
151  m_config->Read( OPTKEY_STEP_UORG_Y, &m_YOrg, 0.0 );
153  m_cbRemoveVirtual->SetValue( m_noVirtual );
154 
155  m_STEP_OrgUnitChoice->SetSelection( m_OrgUnits );
156  wxString tmpStr;
157  tmpStr << m_XOrg;
158  m_STEP_Xorg->SetValue( tmpStr );
159  tmpStr = "";
160  tmpStr << m_YOrg;
161  m_STEP_Yorg->SetValue( tmpStr );
162 
163  // Now all widgets have the size fixed, call FinishDialogSettings
165 }
166 
167 
169 {
171 
172  if( m_rbDrillAndPlotOrigin->GetValue() )
174  else if( m_rbGridOrigin->GetValue() )
176  else if( m_rbUserDefinedOrigin->GetValue() )
178  else if( m_rbBoardCenterOrigin->GetValue() )
180 
181  return m_STEP_org_opt;
182 }
183 
184 
185 void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
186 {
187  wxFileName brdFile = GetBoard()->GetFileName();
188 
189  if( GetScreen()->IsModify() || brdFile.GetFullPath().empty() )
190  {
191  if( !doAutoSave() )
192  {
193  DisplayErrorMessage( this,
194  _( "STEP export failed! Please save the PCB and try again" ) );
195  return;
196  }
197 
198  // Use auto-saved board for export
199  brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
200  }
201 
202  DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
203  dlg.ShowModal();
204 }
205 
206 
207 void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
208 {
209  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
210 }
211 
212 
213 void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
214 {
215  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
216 }
217 
218 
219 void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
220 {
221  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
222 }
223 
224 extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
225  wxString* aErrorText, unsigned int aTolerance,
226  wxPoint* aErrorLocation = nullptr );
227 
228 void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
229 {
231 
232  SHAPE_POLY_SET outline;
233  wxString msg;
234 
235  // Check if the board outline is continuous
236  if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, &msg, Millimeter2iu( 0.01 ) ) )
237  {
238  DisplayErrorMessage( this, _( "Cannot determine the board outline." ), msg );
239  return;
240  }
241 
242  wxFileName fn = m_filePickerSTEP->GetFileName();
243 
244  if( fn.FileExists() )
245  {
246  msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
247  fn.GetFullPath().GetData() );
248 
249  if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
250  return;
251  }
252 
254  double xOrg = 0.0;
255  double yOrg = 0.0;
256 
257  wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
258 
259 #ifdef __WXMAC__
260  // On macOS, we have standalone applications inside the main bundle, so we handle that here:
261  if( appK2S.GetPath().find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
262  {
263  appK2S.AppendDir( ".." );
264  appK2S.AppendDir( ".." );
265  appK2S.AppendDir( ".." );
266  appK2S.AppendDir( ".." );
267  appK2S.AppendDir( "MacOS" );
268  }
269 #endif
270 
271  appK2S.SetName( "kicad2step" );
272 
273  wxString cmdK2S = "\"";
274  cmdK2S.Append( appK2S.GetFullPath() );
275  cmdK2S.Append( "\"" );
276 
277  if( GetNoVirtOption() )
278  cmdK2S.Append( " --no-virtual" );
279 
280  switch( orgOpt )
281  {
283  break;
284 
286  cmdK2S.Append( " --drill-origin" );
287  break;
288 
290  cmdK2S.Append( " --grid-origin" );
291  break;
292 
294  {
295  xOrg = GetXOrg();
296  yOrg = GetYOrg();
297 
298  if( GetOrgUnitsChoice() == 1 )
299  {
300  // selected reference unit is in inches, and STEP units are mm
301  xOrg *= 25.4;
302  yOrg *= 25.4;
303  }
304 
306  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
307  }
308  break;
309 
311  {
312  EDA_RECT bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
313  xOrg = Iu2Millimeter( bbox.GetCenter().x );
314  yOrg = Iu2Millimeter( bbox.GetCenter().y );
316  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
317  }
318  break;
319  }
320 
321  if( m_tolerance->GetSelection() != 1 )
322  {
324  double tolerance = 0.001 * std::pow<double>( 10.0, m_tolerance->GetSelection() - 1 );
325  cmdK2S.Append( wxString::Format( " --min-distance=\"%.4f mm\"", tolerance ) );
326  }
327 
328  cmdK2S.Append( " -f -o " );
329  cmdK2S.Append( wxString::Format("\"%s\"", m_filePickerSTEP->GetPath() ) ); // input file path
330 
331  cmdK2S.Append( " " );
332  cmdK2S.Append( wxString::Format("\"%s\"", m_boardPath ) ); // output file path
333 
334  int result = 0;
335  bool success = false;
336  wxArrayString output, errors;
337  REPORTER& reporter = m_messagesPanel->Reporter();
338  reporter.ReportHead( wxString::Format( _( "Executing '%s'" ), cmdK2S ), REPORTER::RPT_ACTION );
339 
340  {
341  wxBusyCursor dummy;
342  result = wxExecute( cmdK2S, output, errors, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE );
343  }
344 
345  // Check the output log for an indication of success,
346  // the value returned by wxExecute is not conclusive
347  for( auto& l : output )
348  {
349  if( l.Contains( "Done" ) )
350  {
351  success = true;
352  break;
353  }
354  }
355 
356  for( auto& err : errors )
357  reporter.Report( err, REPORTER::RPT_WARNING );
358 
359  if( result ) // Any troubles?
360  {
361  if( !success )
362  {
363  reporter.ReportTail( _( "Unable to create STEP file. Check that the board has a "
364  "valid outline and models." ), REPORTER::RPT_ERROR );
365  }
366  else
367  {
368  reporter.ReportTail( _( "STEP file has been created, but there are warnings." ),
370  }
371  }
372  else
373  {
374  reporter.ReportTail( _( "STEP file has been created successfully." ), REPORTER::RPT_INFO );
375  }
376 }
#define OPTKEY_STEP_UORG_X
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:249
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:90
#define OPTKEY_STEP_NOVIRT
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
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
#define OPTKEY_STEP_UORG_UNITS
REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:61
const wxString & GetFileName() const
Definition: class_board.h:215
#define OPTKEY_STEP_ORIGIN_OPT
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
virtual REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED)
Function ReportHead Places the report at the beginning of the list for objects that support ordering.
Definition: reporter.h:120
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED)
Function ReportTail Places the report at the end of the list, for objects that support report orderin...
Definition: reporter.h:111
SHAPE_POLY_SET.
REPORTER & Reporter()
returns the reporter object that reports to this panel
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
void onUpdateUnits(wxUpdateUIEvent &aEvent) override
WX_HTML_REPORT_PANEL * m_messagesPanel
wxStdDialogButtonSizer * m_sdbSizer
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:160
#define _(s)
Definition: 3d_actions.cpp:31
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)
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_UNDEFINED)=0
Function Report is a pure virtual function to override in the derived object.
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...
#define OPTKEY_STEP_UORG_Y
BOARD * GetBoard() const
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:316
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)