KiCad PCB EDA Suite
dialog_export_step.cpp
Go to the documentation of this file.
1 
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 2016 Cirilo Bernardo
9  * Copyright (C) 2016-2018 KiCad Developers, see AUTHORS.txt for contributors.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include <wx/choicdlg.h>
30 #include <wx/stdpaths.h>
31 
32 #include "pcb_edit_frame.h"
33 #include "kiface_i.h"
34 #include "confirm.h"
35 #include "reporter.h"
36 
37 #include "pcbnew.h"
38 #include "class_board.h"
40 #include <widgets/text_ctrl_eval.h>
41 #include <wx_html_report_panel.h>
42 
43 #define OPTKEY_STEP_ORIGIN_OPT "STEP_Origin_Opt"
44 #define OPTKEY_STEP_UORG_UNITS "STEP_UserOriginUnits"
45 #define OPTKEY_STEP_UORG_X "STEP_UserOriginX"
46 #define OPTKEY_STEP_UORG_Y "STEP_UserOriginY"
47 #define OPTKEY_STEP_NOVIRT "STEP_NoVirtual"
48 
49 
51 {
52 public:
54  {
55  STEP_ORG_0, // absolute coordinates
56  STEP_ORG_PLOT_AXIS, // origin is plot/drill axis origin
57  STEP_ORG_GRID_AXIS, // origin is grid origin
58  STEP_ORG_BOARD_CENTER, // origin is board center
59  STEP_ORG_USER, // origin is entered by user
60  };
61 
62 private:
64  wxConfigBase* m_config;
65  // The last preference for STEP Origin:
67  bool m_noVirtual; // remember last preference for No Virtual Component
68  int m_OrgUnits; // remember last units for User Origin
69  double m_XOrg; // remember last User Origin X value
70  double m_YOrg; // remember last User Origin Y value
71  wxString m_boardPath; // path to the exported board file
72 
73 protected:
74  void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
75  void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
76  void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
77  void onExportButton( wxCommandEvent& aEvent ) override;
78 
79  int GetOrgUnitsChoice() const
80  {
81  return m_STEP_OrgUnitChoice->GetSelection();
82  }
83 
84  double GetXOrg() const
85  {
86  return DoubleValueFromString( UNSCALED_UNITS, m_STEP_Xorg->GetValue() );
87  }
88 
89  double GetYOrg()
90  {
91  return DoubleValueFromString( UNSCALED_UNITS, m_STEP_Yorg->GetValue() );
92  }
93 
95 
97  {
98  return m_cbRemoveVirtual->GetValue();
99  }
100 
101 public:
102  DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
103 
105  {
106  GetOriginOption(); // Update m_STEP_org_opt member.
107  m_config->Write( OPTKEY_STEP_ORIGIN_OPT, (int)m_STEP_org_opt );
108  m_config->Write( OPTKEY_STEP_NOVIRT, m_cbRemoveVirtual->GetValue() );
109  m_config->Write( OPTKEY_STEP_UORG_UNITS, m_STEP_OrgUnitChoice->GetSelection() );
110  m_config->Write( OPTKEY_STEP_UORG_X, m_STEP_Xorg->GetValue() );
111  m_config->Write( OPTKEY_STEP_UORG_Y, m_STEP_Yorg->GetValue() );
112  }
113 };
114 
115 
116 DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
117  DIALOG_EXPORT_STEP_BASE( aParent )
118 {
119  m_parent = aParent;
120  m_boardPath = aBoardPath;
122  m_sdbSizerCancel->SetLabel( _( "Close" ) );
123  m_sdbSizerOK->SetLabel( _( "Export" ) );
124  m_sdbSizer->Layout();
125 
126  // Build default output file name
127  wxFileName brdFile = m_parent->GetBoard()->GetFileName();
128  brdFile.SetExt( "step" );
129  m_filePickerSTEP->SetPath( brdFile.GetFullPath() );
130 
131  SetFocus();
132 
134  int tmp = STEP_ORG_0;
135 
136  if( m_config->Read( OPTKEY_STEP_ORIGIN_OPT, &tmp ) )
138 
139  switch( m_STEP_org_opt )
140  {
141  default: break;
142  case STEP_ORG_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
143  case STEP_ORG_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
144  case STEP_ORG_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
145  case STEP_ORG_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
146  }
147 
149  m_config->Read( OPTKEY_STEP_UORG_X, &m_XOrg, 0.0 );
150  m_config->Read( OPTKEY_STEP_UORG_Y, &m_YOrg, 0.0 );
152  m_cbRemoveVirtual->SetValue( m_noVirtual );
153 
154  m_STEP_OrgUnitChoice->SetSelection( m_OrgUnits );
155  wxString tmpStr;
156  tmpStr << m_XOrg;
157  m_STEP_Xorg->SetValue( tmpStr );
158  tmpStr = "";
159  tmpStr << m_YOrg;
160  m_STEP_Yorg->SetValue( tmpStr );
161 
162  // Now all widgets have the size fixed, call FinishDialogSettings
164 }
165 
166 
168 {
170 
171  if( m_rbDrillAndPlotOrigin->GetValue() )
173  else if( m_rbGridOrigin->GetValue() )
175  else if( m_rbUserDefinedOrigin->GetValue() )
177  else if( m_rbBoardCenterOrigin->GetValue() )
179 
180  return m_STEP_org_opt;
181 }
182 
183 
184 void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
185 {
186  wxFileName brdFile = GetBoard()->GetFileName();
187 
188  if( GetScreen()->IsModify() || brdFile.GetFullPath().empty() )
189  {
190  if( !doAutoSave() )
191  {
192  DisplayErrorMessage( this,
193  _( "STEP export failed! Please save the PCB and try again" ) );
194  return;
195  }
196 
197  // Use auto-saved board for export
198  brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
199  }
200 
201  DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
202  dlg.ShowModal();
203 }
204 
205 
206 void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
207 {
208  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
209 }
210 
211 
212 void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
213 {
214  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
215 }
216 
217 
218 void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
219 {
220  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
221 }
222 
223 extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
224  wxString* aErrorText, unsigned int aTolerance,
225  wxPoint* aErrorLocation = nullptr );
226 
227 void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
228 {
229  SHAPE_POLY_SET outline;
230  wxString msg;
231 
232  // Check if the board outline is continuous
233  if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, &msg, Millimeter2iu( 0.01 ) ) )
234  {
235  DisplayErrorMessage( this, _( "Cannot determine the board outline." ), msg );
236  return;
237  }
238 
239  wxFileName fn = m_filePickerSTEP->GetFileName();
240 
241  if( fn.FileExists() )
242  {
243  msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
244  fn.GetFullPath().GetData() );
245 
246  if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
247  return;
248  }
249 
251  double xOrg = 0.0;
252  double yOrg = 0.0;
253 
254  wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
255 
256 #ifdef __WXMAC__
257  // On macOS, we have standalone applications inside the main bundle, so we handle that here:
258  if( appK2S.GetPath().find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
259  {
260  appK2S.AppendDir( ".." );
261  appK2S.AppendDir( ".." );
262  appK2S.AppendDir( ".." );
263  appK2S.AppendDir( ".." );
264  appK2S.AppendDir( "MacOS" );
265  }
266 #endif
267 
268  appK2S.SetName( "kicad2step" );
269 
270  wxString cmdK2S = "\"";
271  cmdK2S.Append( appK2S.GetFullPath() );
272  cmdK2S.Append( "\"" );
273 
274  if( GetNoVirtOption() )
275  cmdK2S.Append( " --no-virtual" );
276 
277  switch( orgOpt )
278  {
280  break;
281 
283  cmdK2S.Append( " --drill-origin" );
284  break;
285 
287  cmdK2S.Append( " --grid-origin" );
288  break;
289 
291  {
292  xOrg = GetXOrg();
293  yOrg = GetYOrg();
294 
295  if( GetOrgUnitsChoice() == 1 )
296  {
297  // selected reference unit is in inches, and STEP units are mm
298  xOrg *= 25.4;
299  yOrg *= 25.4;
300  }
301 
303  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
304  }
305  break;
306 
308  {
309  EDA_RECT bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
310  xOrg = Iu2Millimeter( bbox.GetCenter().x );
311  yOrg = Iu2Millimeter( bbox.GetCenter().y );
313  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
314  }
315  break;
316  }
317 
318  cmdK2S.Append( " -f -o " );
319  cmdK2S.Append( wxString::Format("\"%s\"", m_filePickerSTEP->GetPath() ) ); // input file path
320 
321  cmdK2S.Append( " " );
322  cmdK2S.Append( wxString::Format("\"%s\"", m_boardPath ) ); // output file path
323 
324  int result = 0;
325  bool success = false;
326  wxArrayString output, errors;
327  REPORTER& reporter = m_messagesPanel->Reporter();
328  reporter.ReportHead( wxString::Format( _( "Executing '%s'" ), cmdK2S ), REPORTER::RPT_ACTION );
329 
330  {
331  wxBusyCursor dummy;
332  result = wxExecute( cmdK2S, output, errors, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE );
333  }
334 
335  // Check the output log for an indication of success,
336  // the value returned by wxExecute is not conclusive
337  for( auto& l : output )
338  {
339  if( l.Contains( "Done" ) )
340  {
341  success = true;
342  break;
343  }
344  }
345 
346  for( auto& err : errors )
347  reporter.Report( err, REPORTER::RPT_WARNING );
348 
349  if( result ) // Any troubles?
350  {
351  if( !success )
352  {
353  reporter.ReportTail( _( "Unable to create STEP file. Check that the board has a "
354  "valid outline and models." ), REPORTER::RPT_ERROR );
355  }
356  else
357  {
358  reporter.ReportTail( _( "STEP file has been created, but there are warnings." ),
360  }
361  }
362  else
363  {
364  reporter.ReportTail( _( "STEP file has been created successfully." ), REPORTER::RPT_INFO );
365  }
366 }
#define OPTKEY_STEP_UORG_X
double GetXOrg() const
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:259
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown...
Definition: common.h:180
#define OPTKEY_STEP_NOVIRT
This file is part of the common library.
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Function ComputeBoundingBox calculates the bounding box containing all board items (or board edge seg...
Class BOARD to handle a board.
void OnExportSTEP(wxCommandEvent &event)
Function OnExportSTEP Exports the current BOARD to a STEP assembly.
wxRadioButton * m_rbDrillAndPlotOrigin
PCB_EDIT_FRAME * m_parent
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
BOARD * GetBoard() const
Class REPORTER is a pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:61
#define OPTKEY_STEP_ORIGIN_OPT
void SetValue(const wxString &aValue) override
Set a new value in evaluator buffer, and display it in the wxTextCtrl.
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Definition: kicad.cpp:52
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
const wxString & GetFileName() const
Definition: class_board.h:238
Class SHAPE_POLY_SET.
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:302
REPORTER & Reporter()
returns the reporter object that reports to this panel
void onUpdateUnits(wxUpdateUIEvent &aEvent) override
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:103
string & err
Definition: json11.cpp:598
WX_HTML_REPORT_PANEL * m_messagesPanel
wxStdDialogButtonSizer * m_sdbSizer
BOARD * GetBoard()
int GetOrgUnitsChoice() const
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 when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
Class BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:171
Class DIALOG_EXPORT_STEP_BASE.
Class EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
Class PCB_EDIT_FRAME is the main frame for Pcbnew.
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
#define OPTKEY_STEP_UORG_Y
DIALOG_EXPORT_STEP(PCB_EDIT_FRAME *aParent, const wxString &aBoardPath)
const wxPoint GetCenter() const
Definition: eda_rect.h:115