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-2020 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 #include <wx/process.h>
28 
29 #include "pcb_edit_frame.h"
30 #include "kiface_i.h"
31 #include "confirm.h"
32 #include "reporter.h"
33 #include "board.h"
35 #include <locale_io.h>
36 #include <pcbnew_settings.h>
37 #include <project/project_file.h> // LAST_PATH_TYPE
38 #include <widgets/text_ctrl_eval.h>
39 
40 
42 {
43 public:
45  {
46  STEP_ORG_0, // absolute coordinates
47  STEP_ORG_PLOT_AXIS, // origin is plot/drill axis origin
48  STEP_ORG_GRID_AXIS, // origin is grid origin
49  STEP_ORG_BOARD_CENTER, // origin is board center
50  STEP_ORG_USER, // origin is entered by user
51  };
52 
53 private:
55  // The last preference for STEP Origin:
57  bool m_noVirtual; // remember last preference for No Virtual Component
58  static bool m_overwriteFile; // remember last preference for overwrite file
59  int m_OrgUnits; // remember last units for User Origin
60  double m_XOrg; // remember last User Origin X value
61  double m_YOrg; // remember last User Origin Y value
62  wxString m_boardPath; // path to the exported board file
63 
64 protected:
65  void onUpdateUnits( wxUpdateUIEvent& aEvent ) override;
66  void onUpdateXPos( wxUpdateUIEvent& aEvent ) override;
67  void onUpdateYPos( wxUpdateUIEvent& aEvent ) override;
68  void onExportButton( wxCommandEvent& aEvent ) override;
69 
70  int GetOrgUnitsChoice() const
71  {
72  return m_STEP_OrgUnitChoice->GetSelection();
73  }
74 
75  double GetXOrg() const
76  {
78  }
79 
80  double GetYOrg()
81  {
83  }
84 
86 
88  {
89  return m_cbRemoveVirtual->GetValue();
90  }
91 
93  {
94  return m_cbOverwriteFile->GetValue();
95  }
96 
97 public:
98  DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath );
99 
101  {
102  GetOriginOption(); // Update m_STEP_org_opt member.
103 
104  auto cfg = m_parent->GetPcbNewSettings();
105 
106  cfg->m_ExportStep.origin_mode = static_cast<int>( m_STEP_org_opt );
107  cfg->m_ExportStep.origin_units = m_STEP_OrgUnitChoice->GetSelection();
108 
109  double val = 0.0;
110 
111  m_STEP_Xorg->GetValue().ToDouble( &val );
112  cfg->m_ExportStep.origin_x = val;
113 
114  m_STEP_Yorg->GetValue().ToDouble( &val );
115  cfg->m_ExportStep.origin_y = val;
116 
117  cfg->m_ExportStep.no_virtual = m_cbRemoveVirtual->GetValue();
118  }
119 };
120 
122 
123 
124 DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
125  DIALOG_EXPORT_STEP_BASE( aParent )
126 {
127  m_parent = aParent;
128  m_boardPath = aBoardPath;
129  m_sdbSizerCancel->SetLabel( _( "Close" ) );
130  m_sdbSizerOK->SetLabel( _( "Export" ) );
131  m_sdbSizer->Layout();
132 
133  // Build default output file name
134  wxString path = m_parent->GetLastPath( LAST_PATH_STEP );
135 
136  if( path.IsEmpty() )
137  {
138  wxFileName brdFile = m_parent->GetBoard()->GetFileName();
139  brdFile.SetExt( "step" );
140  path = brdFile.GetFullPath();
141  }
142 
143  m_filePickerSTEP->SetPath( path );
144 
145  SetFocus();
146 
147  auto cfg = m_parent->GetPcbNewSettings();
148 
149  m_STEP_org_opt = static_cast<STEP_ORG_OPT>( cfg->m_ExportStep.origin_mode );
150 
151  switch( m_STEP_org_opt )
152  {
153  default: break;
154  case STEP_ORG_PLOT_AXIS: m_rbDrillAndPlotOrigin->SetValue( true ); break;
155  case STEP_ORG_GRID_AXIS: m_rbGridOrigin->SetValue( true ); break;
156  case STEP_ORG_USER: m_rbUserDefinedOrigin->SetValue( true ); break;
157  case STEP_ORG_BOARD_CENTER: m_rbBoardCenterOrigin->SetValue( true ); break;
158  }
159 
160  m_OrgUnits = cfg->m_ExportStep.origin_units;
161  m_XOrg = cfg->m_ExportStep.origin_x;
162  m_YOrg = cfg->m_ExportStep.origin_y;
163  m_noVirtual = cfg->m_ExportStep.no_virtual;
164 
165  m_cbRemoveVirtual->SetValue( m_noVirtual );
166  m_cbOverwriteFile->SetValue( m_overwriteFile );
167 
168  m_STEP_OrgUnitChoice->SetSelection( m_OrgUnits );
169  wxString tmpStr;
170  tmpStr << m_XOrg;
171  m_STEP_Xorg->SetValue( tmpStr );
172  tmpStr = "";
173  tmpStr << m_YOrg;
174  m_STEP_Yorg->SetValue( tmpStr );
175 
176  // Now all widgets have the size fixed, call FinishDialogSettings
178 }
179 
180 
182 {
184 
185  if( m_rbDrillAndPlotOrigin->GetValue() )
187  else if( m_rbGridOrigin->GetValue() )
189  else if( m_rbUserDefinedOrigin->GetValue() )
191  else if( m_rbBoardCenterOrigin->GetValue() )
193 
194  return m_STEP_org_opt;
195 }
196 
197 
198 void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
199 {
200  wxFileName brdFile = GetBoard()->GetFileName();
201 
202  if( GetScreen()->IsModify() || brdFile.GetFullPath().empty() )
203  {
204  if( !doAutoSave() )
205  {
206  DisplayErrorMessage( this, _( "STEP export failed! "
207  "Please save the PCB and try again" ) );
208  return;
209  }
210 
211  // Use auto-saved board for export
212  brdFile.SetName( GetAutoSaveFilePrefix() + brdFile.GetName() );
213  }
214 
215  DIALOG_EXPORT_STEP dlg( this, brdFile.GetFullPath() );
216  dlg.ShowModal();
217 }
218 
219 
220 void DIALOG_EXPORT_STEP::onUpdateUnits( wxUpdateUIEvent& aEvent )
221 {
222  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
223 }
224 
225 
226 void DIALOG_EXPORT_STEP::onUpdateXPos( wxUpdateUIEvent& aEvent )
227 {
228  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
229 }
230 
231 
232 void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
233 {
234  aEvent.Enable( m_rbUserDefinedOrigin->GetValue() );
235 }
236 
237 
238 void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
239 {
241 
242  double tolerance = 0.01; // default value in mm
243 
244  switch( m_tolerance->GetSelection() )
245  {
246  case 0: // small
247  tolerance = 0.001;
248  break;
249 
250  default:
251  case 1: break; // Normal
252 
253  case 2: // large
254  tolerance = 0.1;
255  break;
256  }
257 
258  SHAPE_POLY_SET outline;
259  wxString msg;
260 
261  // Check if the board outline is continuous
262  // max dist from one endPt to next startPt to build a closed shape:
263  int chainingEpsilon = Millimeter2iu( tolerance );
264  // Arc to segment approx error (not critical here: we do not use the outline shape):
265  int maxError = Millimeter2iu( 0.005 );
266  bool success = BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, maxError,
267  chainingEpsilon, nullptr );
268  if( !success )
269  {
271  _( "Board outline is missing or not closed using %.3f mm tolerance.\n"
272  "Run DRC for a full analysis." ), tolerance ) );
273  return;
274  }
275 
276  wxFileName fn = m_filePickerSTEP->GetFileName();
277 
278  if( fn.FileExists() && !GetOverwriteFile() )
279  {
280  msg.Printf( _( "File '%s' already exists. Do you want overwrite this file?" ),
281  fn.GetFullPath() );
282 
283  if( wxMessageBox( msg, _( "STEP Export" ), wxYES_NO | wxICON_QUESTION, this ) == wxNO )
284  return;
285  }
286 
288  double xOrg = 0.0;
289  double yOrg = 0.0;
290 
291  wxFileName appK2S( wxStandardPaths::Get().GetExecutablePath() );
292 
293 #ifdef __WXMAC__
294  // On macOS, we have standalone applications inside the main bundle, so we handle that here:
295  if( appK2S.GetPath().Find( "/Contents/Applications/pcbnew.app/Contents/MacOS" ) != wxNOT_FOUND )
296  {
297  appK2S.AppendDir( ".." );
298  appK2S.AppendDir( ".." );
299  appK2S.AppendDir( ".." );
300  appK2S.AppendDir( ".." );
301  appK2S.AppendDir( "MacOS" );
302  }
303 #endif
304 
305  appK2S.SetName( "kicad2step" );
306 
307  wxString cmdK2S = "\"";
308  cmdK2S.Append( appK2S.GetFullPath() );
309  cmdK2S.Append( "\"" );
310 
311  if( GetNoVirtOption() )
312  cmdK2S.Append( " --no-virtual" );
313 
314  switch( orgOpt )
315  {
317  break;
318 
320  cmdK2S.Append( " --drill-origin" );
321  break;
322 
324  cmdK2S.Append( " --grid-origin" );
325  break;
326 
328  {
329  xOrg = GetXOrg();
330  yOrg = GetYOrg();
331 
332  if( GetOrgUnitsChoice() == 1 )
333  {
334  // selected reference unit is in inches, and STEP units are mm
335  xOrg *= 25.4;
336  yOrg *= 25.4;
337  }
338 
340  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
341  }
342  break;
343 
345  {
346  EDA_RECT bbox = m_parent->GetBoard()->ComputeBoundingBox( true );
347  xOrg = Iu2Millimeter( bbox.GetCenter().x );
348  yOrg = Iu2Millimeter( bbox.GetCenter().y );
350  cmdK2S.Append( wxString::Format( " --user-origin=\"%.6f x %.6f\"", xOrg, yOrg ) );
351  }
352  break;
353  }
354 
355  {
357  cmdK2S.Append( wxString::Format( " --min-distance=\"%.3f mm\"", tolerance ) );
358  }
359 
360  cmdK2S.Append( " -f -o " );
361  cmdK2S.Append( wxString::Format("\"%s\"", m_filePickerSTEP->GetPath() ) ); // input file path
362 
363  cmdK2S.Append( " " );
364  cmdK2S.Append( wxString::Format("\"%s\"", m_boardPath ) ); // output file path
365 
366  wxExecute( cmdK2S, wxEXEC_ASYNC | wxEXEC_SHOW_CONSOLE );
367 
368  aEvent.Skip(); // Close the dialog
369 }
DIALOG_EXPORT_STEP m_ExportStep
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:265
void onUpdateXPos(wxUpdateUIEvent &aEvent) override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
This file is part of the common library.
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.
wxRadioButton * m_rbDrillAndPlotOrigin
PCB_EDIT_FRAME * m_parent
static LIB_PART * dummy()
Used to draw a dummy shape when a LIB_PART is not found in library.
Definition: sch_symbol.cpp:69
const wxString & GetFileName() const
Definition: board.h:300
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
Represent a set of closed polygons.
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 ...
void onUpdateUnits(wxUpdateUIEvent &aEvent) override
wxStdDialogButtonSizer * m_sdbSizer
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 wxString GetAutoSaveFilePrefix()
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
#define _(s)
Definition: 3d_actions.cpp:33
Class DIALOG_EXPORT_STEP_BASE.
Handle the component boundary box.
Definition: eda_rect.h:42
The main frame for Pcbnew.
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
void onUpdateYPos(wxUpdateUIEvent &aEvent) override
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1052
BOARD * GetBoard() const
const wxPoint GetCenter() const
Definition: eda_rect.h:109
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:293
static constexpr int Millimeter2iu(double mm)
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)