Pcbnew VRML export improvements.
* Improve VRML export dialog lay out. * Make the OK button the default action in the export dialog. * Make 3D model export work correctly and avoid redundant file copies. * Fix embedded path separators in wrl files on windows. * Fix absolute model file path creation in wrl files. * Clean up VRML export code.
This commit is contained in:
parent
40468bffa8
commit
431ed318e0
|
@ -934,12 +934,15 @@ public:
|
|||
* @param aMMtoWRMLunit = the VRML scaling factor:
|
||||
* 1.0 to export in mm. 0.001 for meters
|
||||
* @param aExport3DFiles = true to copy 3D shapes in the subir a3D_Subdir
|
||||
* @param a3D_Subdir = sub directory where 3D shapes files are copied
|
||||
* used only when aExport3DFiles == true
|
||||
* @param aUseRelativePaths set to true to use relative paths instead of absolute paths
|
||||
* in the board VRML file URLs.
|
||||
* @param a3D_Subdir = sub directory where 3D shapes files are copied. This is only used
|
||||
* when aExport3DFiles == true
|
||||
* @return true if Ok.
|
||||
*/
|
||||
bool ExportVRML_File( const wxString & aFullFileName, double aMMtoWRMLunit,
|
||||
bool aExport3DFiles, const wxString & a3D_Subdir );
|
||||
bool aExport3DFiles, bool aUseRelativePaths,
|
||||
const wxString & a3D_Subdir );
|
||||
|
||||
/**
|
||||
* Function ExportToIDF3
|
||||
|
|
|
@ -38,19 +38,19 @@
|
|||
*/
|
||||
#include <dialog_export_vrml_base.h> // the wxFormBuilder header file
|
||||
|
||||
#define OPTKEY_OUTPUT_UNIT wxT("VrmlExportUnit" )
|
||||
#define OPTKEY_3DFILES_OPT wxT("VrmlExport3DShapeFilesOpt" )
|
||||
#define OPTKEY_OUTPUT_UNIT wxT( "VrmlExportUnit" )
|
||||
#define OPTKEY_3DFILES_OPT wxT( "VrmlExportCopyFiles" )
|
||||
#define OPTKEY_USE_ABS_PATHS wxT( "VrmlUseRelativePaths" )
|
||||
|
||||
|
||||
class DIALOG_EXPORT_3DFILE : public DIALOG_EXPORT_3DFILE_BASE
|
||||
{
|
||||
private:
|
||||
PCB_EDIT_FRAME* m_parent;
|
||||
wxConfigBase* m_config;
|
||||
int m_unitsOpt; // to remember last option
|
||||
int m_3DFilesOpt; // to remember last option
|
||||
|
||||
void OnCancelClick( wxCommandEvent& event ){ EndModal( wxID_CANCEL ); }
|
||||
void OnOkClick( wxCommandEvent& event ){ EndModal( wxID_OK ); }
|
||||
wxConfigBase* m_config;
|
||||
int m_unitsOpt; // Remember last units option
|
||||
bool m_copy3DFilesOpt; // Remember last copy model files option
|
||||
bool m_useRelativePathsOpt; // Remember last use absolut paths option
|
||||
|
||||
public:
|
||||
DIALOG_EXPORT_3DFILE( PCB_EDIT_FRAME* parent ) :
|
||||
|
@ -58,30 +58,42 @@ public:
|
|||
{
|
||||
m_parent = parent;
|
||||
m_config = Kiface().KifaceSettings();
|
||||
SetFocus();
|
||||
m_filePicker->SetFocus();
|
||||
m_config->Read( OPTKEY_OUTPUT_UNIT, &m_unitsOpt );
|
||||
m_config->Read( OPTKEY_3DFILES_OPT, &m_3DFilesOpt );
|
||||
m_rbSelectUnits->SetSelection(m_unitsOpt);
|
||||
m_rb3DFilesOption->SetSelection(m_3DFilesOpt);
|
||||
m_config->Read( OPTKEY_3DFILES_OPT, &m_copy3DFilesOpt );
|
||||
m_config->Read( OPTKEY_USE_ABS_PATHS, &m_useRelativePathsOpt );
|
||||
m_rbSelectUnits->SetSelection( m_unitsOpt );
|
||||
m_cbCopyFiles->SetValue( m_copy3DFilesOpt );
|
||||
m_cbUseAbsolutePaths->SetValue( m_useRelativePathsOpt );
|
||||
wxButton* okButton = (wxButton*) FindWindowByLabel( wxT( "OK" ) );
|
||||
|
||||
if( okButton )
|
||||
SetDefaultItem( okButton );
|
||||
|
||||
GetSizer()->SetSizeHints( this );
|
||||
Centre();
|
||||
|
||||
Connect( ID_USE_ABS_PATH, wxEVT_UPDATE_UI,
|
||||
wxUpdateUIEventHandler( DIALOG_EXPORT_3DFILE::OnUpdateUseAbsolutPath ) );
|
||||
}
|
||||
|
||||
~DIALOG_EXPORT_3DFILE()
|
||||
{
|
||||
m_unitsOpt = GetUnits( );
|
||||
m_3DFilesOpt = Get3DFilesOption( );
|
||||
m_unitsOpt = GetUnits();
|
||||
m_copy3DFilesOpt = GetCopyFilesOption();
|
||||
m_config->Write( OPTKEY_OUTPUT_UNIT, m_unitsOpt );
|
||||
m_config->Write( OPTKEY_3DFILES_OPT, m_3DFilesOpt );
|
||||
m_config->Write( OPTKEY_3DFILES_OPT, m_copy3DFilesOpt );
|
||||
m_config->Write( OPTKEY_USE_ABS_PATHS, m_useRelativePathsOpt );
|
||||
};
|
||||
|
||||
void SetSubdir( const wxString & aDir )
|
||||
{
|
||||
m_SubdirNameCtrl->SetValue( aDir);
|
||||
m_SubdirNameCtrl->SetValue( aDir );
|
||||
}
|
||||
|
||||
wxString GetSubdir( )
|
||||
wxString GetSubdir()
|
||||
{
|
||||
return m_SubdirNameCtrl->GetValue( );
|
||||
return m_SubdirNameCtrl->GetValue();
|
||||
}
|
||||
|
||||
wxFilePickerCtrl* FilePicker()
|
||||
|
@ -89,26 +101,43 @@ public:
|
|||
return m_filePicker;
|
||||
}
|
||||
|
||||
int GetUnits( )
|
||||
int GetUnits()
|
||||
{
|
||||
return m_unitsOpt = m_rbSelectUnits->GetSelection();
|
||||
}
|
||||
|
||||
int Get3DFilesOption( )
|
||||
bool GetCopyFilesOption()
|
||||
{
|
||||
return m_3DFilesOpt = m_rb3DFilesOption->GetSelection();
|
||||
return m_copy3DFilesOpt = m_cbCopyFiles->GetValue();
|
||||
}
|
||||
|
||||
bool GetUseAbsolutePathsOption()
|
||||
{
|
||||
return m_useRelativePathsOpt = m_cbUseAbsolutePaths->GetValue();
|
||||
}
|
||||
|
||||
void OnUpdateUseAbsolutPath( wxUpdateUIEvent& event )
|
||||
{
|
||||
// Making path relative or absolute has no meaning when VRML files are not copied.
|
||||
event.Enable( m_cbCopyFiles->GetValue() );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function OnExportVRML
|
||||
* will export the current BOARD to a VRML file.
|
||||
*/
|
||||
void PCB_EDIT_FRAME::OnExportVRML( wxCommandEvent& event )
|
||||
{
|
||||
wxFileName fn;
|
||||
static wxString subDirFor3Dshapes = wxT("shapes3D");
|
||||
wxString projectPath;
|
||||
|
||||
if( !wxGetEnv( wxT( "KIPRJMOD" ), &projectPath ) )
|
||||
projectPath = wxFileName::GetCwd();
|
||||
|
||||
static wxString subDirFor3Dshapes;
|
||||
|
||||
if( subDirFor3Dshapes.IsEmpty() )
|
||||
{
|
||||
subDirFor3Dshapes = wxT( "shapes3D" );
|
||||
}
|
||||
|
||||
// The general VRML scale factor
|
||||
// Assuming the VRML default unit is the mm
|
||||
|
@ -116,9 +145,8 @@ void PCB_EDIT_FRAME::OnExportVRML( wxCommandEvent& event )
|
|||
double scaleList[3] = { 1.0/25.4, 1, 0.001 };
|
||||
|
||||
// Build default file name
|
||||
wxString ext = wxT( "wrl" );
|
||||
fn = GetBoard()->GetFileName();
|
||||
fn.SetExt( ext );
|
||||
fn.SetExt( wxT( "wrl" ) );
|
||||
|
||||
DIALOG_EXPORT_3DFILE dlg( this );
|
||||
dlg.FilePicker()->SetPath( fn.GetFullPath() );
|
||||
|
@ -127,18 +155,26 @@ void PCB_EDIT_FRAME::OnExportVRML( wxCommandEvent& event )
|
|||
if( dlg.ShowModal() != wxID_OK )
|
||||
return;
|
||||
|
||||
double scale = scaleList[dlg.GetUnits( )]; // final scale export
|
||||
bool export3DFiles = dlg.Get3DFilesOption( ) == 0;
|
||||
|
||||
double scale = scaleList[dlg.GetUnits()]; // final scale export
|
||||
bool export3DFiles = dlg.GetCopyFilesOption();
|
||||
bool useRelativePaths = dlg.GetUseAbsolutePathsOption();
|
||||
wxString fullFilename = dlg.FilePicker()->GetPath();
|
||||
wxFileName modelPath = fullFilename;
|
||||
wxBusyCursor dummy;
|
||||
|
||||
wxString fullFilename = dlg.FilePicker()->GetPath();
|
||||
modelPath.AppendDir( dlg.GetSubdir() );
|
||||
subDirFor3Dshapes = dlg.GetSubdir();
|
||||
|
||||
if( export3DFiles && !wxDirExists( subDirFor3Dshapes ) )
|
||||
wxMkdir( subDirFor3Dshapes );
|
||||
wxLogDebug( wxT( "Exporting enabled=%d to %s." ),
|
||||
export3DFiles, GetChars( subDirFor3Dshapes ) );
|
||||
|
||||
if( ! ExportVRML_File( fullFilename, scale, export3DFiles, subDirFor3Dshapes ) )
|
||||
if( export3DFiles && !modelPath.DirExists() )
|
||||
{
|
||||
modelPath.Mkdir();
|
||||
}
|
||||
|
||||
if( !ExportVRML_File( fullFilename, scale, export3DFiles, useRelativePaths,
|
||||
modelPath.GetPath() ) )
|
||||
{
|
||||
wxString msg = _( "Unable to create " ) + fullFilename;
|
||||
wxMessageBox( msg );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 8 2012)
|
||||
// C++ code generated with wxFormBuilder (version Nov 6 2013)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO "NOT" EDIT THIS FILE!
|
||||
|
@ -19,15 +19,14 @@ DIALOG_EXPORT_3DFILE_BASE::DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindow
|
|||
wxBoxSizer* bUpperSizer;
|
||||
bUpperSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
bUpperSizer->SetMinSize( wxSize( 450,-1 ) );
|
||||
m_staticText1 = new wxStaticText( this, wxID_ANY, _("Vrml main file filename:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_staticText1 = new wxStaticText( this, wxID_ANY, _("File Name:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_staticText1->Wrap( -1 );
|
||||
bUpperSizer->Add( m_staticText1, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
|
||||
|
||||
m_filePicker = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, _("Save VRML Board File"), wxT("*.wrl"), wxDefaultPosition, wxDefaultSize, wxFLP_SAVE|wxFLP_USE_TEXTCTRL );
|
||||
bUpperSizer->Add( m_filePicker, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND, 5 );
|
||||
|
||||
m_staticText3 = new wxStaticText( this, wxID_ANY, _("Vrml 3D footprints shapes subdir:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_staticText3 = new wxStaticText( this, wxID_ANY, _("Footprint 3D model path:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_staticText3->Wrap( -1 );
|
||||
bUpperSizer->Add( m_staticText3, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
|
||||
|
||||
|
@ -36,25 +35,33 @@ DIALOG_EXPORT_3DFILE_BASE::DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindow
|
|||
bUpperSizer->Add( m_SubdirNameCtrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
|
||||
|
||||
|
||||
bSizer1->Add( bUpperSizer, 0, wxEXPAND, 5 );
|
||||
bSizer1->Add( bUpperSizer, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bLowerSizer;
|
||||
bLowerSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxBoxSizer* bSizer4;
|
||||
bSizer4 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_cbCopyFiles = new wxCheckBox( this, wxID_ANY, _("Copy 3D model files to 3D model path"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_cbCopyFiles->SetValue(true);
|
||||
bSizer4->Add( m_cbCopyFiles, 0, wxALL, 5 );
|
||||
|
||||
m_cbUseAbsolutePaths = new wxCheckBox( this, ID_USE_ABS_PATH, _("Use absolute paths to model files in board VRML file"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_cbUseAbsolutePaths->SetValue(true);
|
||||
bSizer4->Add( m_cbUseAbsolutePaths, 0, wxALL, 5 );
|
||||
|
||||
|
||||
bLowerSizer->Add( bSizer4, 3, wxEXPAND, 5 );
|
||||
|
||||
wxString m_rbSelectUnitsChoices[] = { _("Inch"), _("mm"), _("Meter") };
|
||||
int m_rbSelectUnitsNChoices = sizeof( m_rbSelectUnitsChoices ) / sizeof( wxString );
|
||||
m_rbSelectUnits = new wxRadioBox( this, wxID_ANY, _("Units:"), wxDefaultPosition, wxDefaultSize, m_rbSelectUnitsNChoices, m_rbSelectUnitsChoices, 1, wxRA_SPECIFY_COLS );
|
||||
m_rbSelectUnits->SetSelection( 0 );
|
||||
bLowerSizer->Add( m_rbSelectUnits, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
wxString m_rb3DFilesOptionChoices[] = { _("Copy 3D Shapes Files in Subdir"), _("Use Absolute Path in Vrml File ") };
|
||||
int m_rb3DFilesOptionNChoices = sizeof( m_rb3DFilesOptionChoices ) / sizeof( wxString );
|
||||
m_rb3DFilesOption = new wxRadioBox( this, wxID_ANY, _("3D Shapes Files Option:"), wxDefaultPosition, wxDefaultSize, m_rb3DFilesOptionNChoices, m_rb3DFilesOptionChoices, 1, wxRA_SPECIFY_COLS );
|
||||
m_rb3DFilesOption->SetSelection( 1 );
|
||||
bLowerSizer->Add( m_rb3DFilesOption, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer1->Add( bLowerSizer, 1, wxEXPAND, 5 );
|
||||
bSizer1->Add( bLowerSizer, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
|
||||
|
||||
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
bSizer1->Add( m_staticline1, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
|
||||
|
@ -71,6 +78,7 @@ DIALOG_EXPORT_3DFILE_BASE::DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindow
|
|||
|
||||
this->SetSizer( bSizer1 );
|
||||
this->Layout();
|
||||
bSizer1->Fit( this );
|
||||
|
||||
// Connect Events
|
||||
m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_3DFILE_BASE::OnCancelClick ), NULL, this );
|
||||
|
|
|
@ -20,8 +20,10 @@
|
|||
<property name="path">.</property>
|
||||
<property name="precompiled_header"></property>
|
||||
<property name="relative_path">1</property>
|
||||
<property name="skip_lua_events">1</property>
|
||||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_enum">1</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Dialog" expanded="1">
|
||||
|
@ -42,10 +44,10 @@
|
|||
<property name="minimum_size"></property>
|
||||
<property name="name">DIALOG_EXPORT_3DFILE_BASE</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size">370,252</property>
|
||||
<property name="size">-1,-1</property>
|
||||
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
|
||||
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
|
||||
<property name="title">Vrml Board Export Options:</property>
|
||||
<property name="title">VRML Export Options</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
|
@ -93,10 +95,10 @@
|
|||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size">450,-1</property>
|
||||
<property name="minimum_size">-1,-1</property>
|
||||
<property name="name">bUpperSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
|
@ -132,7 +134,7 @@
|
|||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Vrml main file filename:</property>
|
||||
<property name="label">File Name:</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
|
@ -304,7 +306,7 @@
|
|||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Vrml 3D footprints shapes subdir:</property>
|
||||
<property name="label">Footprint 3D model path:</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
|
@ -450,13 +452,200 @@
|
|||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND</property>
|
||||
<property name="flag">wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bLowerSizer</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND</property>
|
||||
<property name="proportion">3</property>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer4</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxCheckBox" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="checked">1</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Copy 3D model files to 3D model path</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_cbCopyFiles</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnCheckBox"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxCheckBox" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="checked">1</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">ID_USE_ABS_PATH</property>
|
||||
<property name="label">Use absolute paths to model files in board VRML file</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_cbUseAbsolutePaths</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnCheckBox"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
|
@ -547,96 +736,6 @@
|
|||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxRadioBox" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="choices">"Copy 3D Shapes Files in Subdir" "Use Absolute Path in Vrml File "</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">3D Shapes Files Option:</property>
|
||||
<property name="majorDimension">1</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_rb3DFilesOption</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="selection">1</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxRA_SPECIFY_COLS</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRadioBox"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version Oct 8 2012)
|
||||
// C++ code generated with wxFormBuilder (version Nov 6 2013)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO "NOT" EDIT THIS FILE!
|
||||
|
@ -23,6 +23,7 @@ class DIALOG_SHIM;
|
|||
#include <wx/filepicker.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/radiobox.h>
|
||||
#include <wx/statline.h>
|
||||
#include <wx/button.h>
|
||||
|
@ -38,12 +39,18 @@ class DIALOG_EXPORT_3DFILE_BASE : public DIALOG_SHIM
|
|||
private:
|
||||
|
||||
protected:
|
||||
enum
|
||||
{
|
||||
ID_USE_ABS_PATH = 1000
|
||||
};
|
||||
|
||||
wxStaticText* m_staticText1;
|
||||
wxFilePickerCtrl* m_filePicker;
|
||||
wxStaticText* m_staticText3;
|
||||
wxTextCtrl* m_SubdirNameCtrl;
|
||||
wxCheckBox* m_cbCopyFiles;
|
||||
wxCheckBox* m_cbUseAbsolutePaths;
|
||||
wxRadioBox* m_rbSelectUnits;
|
||||
wxRadioBox* m_rb3DFilesOption;
|
||||
wxStaticLine* m_staticline1;
|
||||
wxStdDialogButtonSizer* m_sdbSizer1;
|
||||
wxButton* m_sdbSizer1OK;
|
||||
|
@ -56,7 +63,7 @@ class DIALOG_EXPORT_3DFILE_BASE : public DIALOG_SHIM
|
|||
|
||||
public:
|
||||
|
||||
DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Vrml Board Export Options:"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 370,252 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
|
||||
DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("VRML Export Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
|
||||
~DIALOG_EXPORT_3DFILE_BASE();
|
||||
|
||||
};
|
||||
|
|
|
@ -59,11 +59,6 @@
|
|||
// offset for art layers, mm (silk, paste, etc)
|
||||
#define ART_OFFSET 0.025
|
||||
|
||||
/* helper function:
|
||||
* some characters cannot be used in names,
|
||||
* this function change them to "_"
|
||||
*/
|
||||
static void ChangeIllegalCharacters( wxString& aFileName, bool aDirSepIsIllegal );
|
||||
|
||||
struct VRML_COLOR
|
||||
{
|
||||
|
@ -122,6 +117,7 @@ struct VRML_COLOR
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
enum VRML_COLOR_INDEX
|
||||
{
|
||||
VRML_COLOR_PCB = 0,
|
||||
|
@ -139,10 +135,9 @@ private:
|
|||
VRML_COLOR colors[VRML_COLOR_LAST];
|
||||
|
||||
int iMaxSeg; // max. sides to a small circle
|
||||
double arcMinLen, arcMaxLen; // min and max lengths of an arc chord
|
||||
double arcMinLen, arcMaxLen; // min and max lengths of an arc chord
|
||||
|
||||
public:
|
||||
|
||||
VRML_LAYER holes;
|
||||
VRML_LAYER board;
|
||||
VRML_LAYER top_copper;
|
||||
|
@ -185,7 +180,7 @@ public:
|
|||
colors[ VRML_COLOR_SILK ] = VRML_COLOR( .9, .9, .9, .9, .9, .9,
|
||||
0, 0, 0, 1, 0, 0.2 );
|
||||
// pad silver
|
||||
colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
|
||||
colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
|
||||
0, 0, 0, 0.8, 0, 0.8 );
|
||||
|
||||
precision = 5;
|
||||
|
@ -268,6 +263,7 @@ public:
|
|||
// static var. for dealing with text
|
||||
static MODEL_VRML* model_vrml;
|
||||
|
||||
|
||||
// select the VRML layer object to draw on; return true if
|
||||
// a layer has been selected.
|
||||
static bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer )
|
||||
|
@ -299,8 +295,8 @@ static bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer )
|
|||
|
||||
|
||||
static void write_triangle_bag( std::ofstream& output_file, VRML_COLOR& color,
|
||||
VRML_LAYER* layer, bool plane, bool top,
|
||||
double top_z, double bottom_z, int aPrecision )
|
||||
VRML_LAYER* layer, bool plane, bool top,
|
||||
double top_z, double bottom_z, int aPrecision )
|
||||
{
|
||||
/* A lot of nodes are not required, but blender sometimes chokes
|
||||
* without them */
|
||||
|
@ -405,27 +401,26 @@ static void write_layers( MODEL_VRML& aModel, std::ofstream& output_file, BOARD*
|
|||
double brdz = aModel.board_thickness / 2.0
|
||||
- ( Millimeter2iu( ART_OFFSET / 2.0 ) ) * aModel.scale;
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_PCB ),
|
||||
&aModel.board, false, false, brdz, -brdz, aModel.precision );
|
||||
&aModel.board, false, false, brdz, -brdz, aModel.precision );
|
||||
|
||||
// VRML_LAYER top_copper;
|
||||
aModel.top_copper.Tesselate( &aModel.holes );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
|
||||
&aModel.top_copper, true, true,
|
||||
aModel.GetLayerZ( F_Cu ), 0, aModel.precision );
|
||||
&aModel.top_copper, true, true,
|
||||
aModel.GetLayerZ( F_Cu ), 0, aModel.precision );
|
||||
|
||||
// VRML_LAYER top_tin;
|
||||
aModel.top_tin.Tesselate( &aModel.holes );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
|
||||
&aModel.top_tin, true, true,
|
||||
aModel.GetLayerZ( F_Cu )
|
||||
+ Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
0, aModel.precision );
|
||||
&aModel.top_tin, true, true,
|
||||
aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
0, aModel.precision );
|
||||
|
||||
// VRML_LAYER bot_copper;
|
||||
aModel.bot_copper.Tesselate( &aModel.holes );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
|
||||
&aModel.bot_copper, true, false,
|
||||
aModel.GetLayerZ( B_Cu ), 0, aModel.precision );
|
||||
&aModel.bot_copper, true, false,
|
||||
aModel.GetLayerZ( B_Cu ), 0, aModel.precision );
|
||||
|
||||
// VRML_LAYER bot_tin;
|
||||
aModel.bot_tin.Tesselate( &aModel.holes );
|
||||
|
@ -439,23 +434,19 @@ static void write_layers( MODEL_VRML& aModel, std::ofstream& output_file, BOARD*
|
|||
aModel.plated_holes.Tesselate( NULL, true );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
|
||||
&aModel.plated_holes, false, false,
|
||||
aModel.GetLayerZ( F_Cu )
|
||||
+ Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
aModel.GetLayerZ( B_Cu )
|
||||
- Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
aModel.GetLayerZ( F_Cu ) + Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
aModel.GetLayerZ( B_Cu ) - Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
|
||||
aModel.precision );
|
||||
|
||||
// VRML_LAYER top_silk;
|
||||
aModel.top_silk.Tesselate( &aModel.holes );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
|
||||
&aModel.top_silk, true, true,
|
||||
aModel.GetLayerZ( F_SilkS ), 0, aModel.precision );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ), &aModel.top_silk,
|
||||
true, true, aModel.GetLayerZ( F_SilkS ), 0, aModel.precision );
|
||||
|
||||
// VRML_LAYER bot_silk;
|
||||
aModel.bot_silk.Tesselate( &aModel.holes );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
|
||||
&aModel.bot_silk, true, false,
|
||||
aModel.GetLayerZ( B_SilkS ), 0, aModel.precision );
|
||||
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ), &aModel.bot_silk,
|
||||
true, false, aModel.GetLayerZ( B_SilkS ), 0, aModel.precision );
|
||||
}
|
||||
|
||||
|
||||
|
@ -498,8 +489,8 @@ static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb )
|
|||
|
||||
|
||||
static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
|
||||
double startx, double starty,
|
||||
double endx, double endy, double width )
|
||||
double startx, double starty,
|
||||
double endx, double endy, double width )
|
||||
{
|
||||
VRML_LAYER* vlayer;
|
||||
|
||||
|
@ -523,8 +514,8 @@ static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
|
|||
|
||||
|
||||
static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
|
||||
double startx, double starty,
|
||||
double endx, double endy, double width )
|
||||
double startx, double starty,
|
||||
double endx, double endy, double width )
|
||||
{
|
||||
VRML_LAYER* vlayer;
|
||||
|
||||
|
@ -554,9 +545,9 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
|
|||
|
||||
|
||||
static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
|
||||
double centerx, double centery,
|
||||
double arc_startx, double arc_starty,
|
||||
double width, double arc_angle )
|
||||
double centerx, double centery,
|
||||
double arc_startx, double arc_starty,
|
||||
double width, double arc_angle )
|
||||
{
|
||||
VRML_LAYER* vlayer;
|
||||
|
||||
|
@ -569,8 +560,7 @@ static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
|
|||
centery = -centery;
|
||||
arc_starty = -arc_starty;
|
||||
|
||||
if( !vlayer->AddArc( centerx, centery, arc_startx, arc_starty,
|
||||
width, arc_angle, false ) )
|
||||
if( !vlayer->AddArc( centerx, centery, arc_startx, arc_starty, width, arc_angle, false ) )
|
||||
throw( std::runtime_error( vlayer->GetError() ) );
|
||||
|
||||
}
|
||||
|
@ -593,11 +583,11 @@ static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg )
|
|||
{
|
||||
case S_ARC:
|
||||
export_vrml_arc( aModel, layer,
|
||||
(double) drawseg->GetCenter().x,
|
||||
(double) drawseg->GetCenter().y,
|
||||
(double) drawseg->GetArcStart().x,
|
||||
(double) drawseg->GetArcStart().y,
|
||||
w, drawseg->GetAngle() / 10 );
|
||||
(double) drawseg->GetCenter().x,
|
||||
(double) drawseg->GetCenter().y,
|
||||
(double) drawseg->GetArcStart().x,
|
||||
(double) drawseg->GetArcStart().y,
|
||||
w, drawseg->GetAngle() / 10 );
|
||||
break;
|
||||
|
||||
case S_CIRCLE:
|
||||
|
@ -620,9 +610,9 @@ static void vrml_text_callback( int x0, int y0, int xf, int yf )
|
|||
double scale = model_vrml->scale;
|
||||
|
||||
export_vrml_line( *model_vrml, s_text_layer,
|
||||
x0 * scale, y0 * scale,
|
||||
xf * scale, yf * scale,
|
||||
s_text_width * scale );
|
||||
x0 * scale, y0 * scale,
|
||||
xf * scale, yf * scale,
|
||||
s_text_width * scale );
|
||||
}
|
||||
|
||||
|
||||
|
@ -708,8 +698,7 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
// Build a polygon from edge cut items
|
||||
wxString msg;
|
||||
|
||||
if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines,
|
||||
allLayerHoles, &msg ) )
|
||||
if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
|
||||
{
|
||||
msg << wxT( "\n\n" ) <<
|
||||
_( "Unable to calculate the board outlines;\n"
|
||||
|
@ -744,7 +733,7 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
break;
|
||||
|
||||
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale,
|
||||
-(bufferPcbOutlines[i].y * scale ) );
|
||||
-(bufferPcbOutlines[i].y * scale ) );
|
||||
|
||||
++i;
|
||||
}
|
||||
|
@ -776,7 +765,7 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
break;
|
||||
|
||||
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale,
|
||||
-(allLayerHoles[i].y * scale ) );
|
||||
-( allLayerHoles[i].y * scale ) );
|
||||
|
||||
++i;
|
||||
}
|
||||
|
@ -788,9 +777,9 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
|
|||
|
||||
|
||||
static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
|
||||
double x, double y, double r,
|
||||
LAYER_NUM bottom_layer, LAYER_NUM top_layer,
|
||||
double hole )
|
||||
double x, double y, double r,
|
||||
LAYER_NUM bottom_layer, LAYER_NUM top_layer,
|
||||
double hole )
|
||||
{
|
||||
LAYER_NUM layer = top_layer;
|
||||
bool thru = true;
|
||||
|
@ -809,7 +798,7 @@ static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
|
|||
aModel.bot_copper.AddCircle( x, -y, r );
|
||||
|
||||
if( hole > 0 && !thru )
|
||||
aModel.bot_copper.AddCircle( x, -y, hole, true );
|
||||
aModel.bot_copper.AddCircle( x, -y, hole, true );
|
||||
|
||||
}
|
||||
else if( layer == F_Cu )
|
||||
|
@ -859,11 +848,11 @@ static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
|
|||
}
|
||||
else if( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
|
||||
export_vrml_line( aModel, track->GetLayer(),
|
||||
track->GetStart().x * aModel.scale,
|
||||
track->GetStart().y * aModel.scale,
|
||||
track->GetEnd().x * aModel.scale,
|
||||
track->GetEnd().y * aModel.scale,
|
||||
track->GetWidth() * aModel.scale );
|
||||
track->GetStart().x * aModel.scale,
|
||||
track->GetStart().y * aModel.scale,
|
||||
track->GetEnd().x * aModel.scale,
|
||||
track->GetEnd().y * aModel.scale,
|
||||
track->GetWidth() * aModel.scale );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -888,8 +877,8 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
|
|||
zone->SetFillMode( 0 ); // use filled polygons
|
||||
zone->BuildFilledSolidAreasPolygons( aPcb );
|
||||
}
|
||||
const CPOLYGONS_LIST& poly = zone->GetFilledPolysList();
|
||||
|
||||
const CPOLYGONS_LIST& poly = zone->GetFilledPolysList();
|
||||
int nvert = poly.GetCornersCount();
|
||||
int i = 0;
|
||||
|
||||
|
@ -941,11 +930,11 @@ static void export_vrml_text_module( TEXTE_MODULE* module )
|
|||
model_vrml->s_text_width = module->GetThickness();
|
||||
|
||||
DrawGraphicText( NULL, NULL, module->GetTextPosition(), BLACK,
|
||||
module->GetShownText(), module->GetDrawRotation(), size,
|
||||
module->GetHorizJustify(), module->GetVertJustify(),
|
||||
module->GetThickness(), module->IsItalic(),
|
||||
true,
|
||||
vrml_text_callback );
|
||||
module->GetShownText(), module->GetDrawRotation(), size,
|
||||
module->GetHorizJustify(), module->GetVertJustify(),
|
||||
module->GetThickness(), module->IsItalic(),
|
||||
true,
|
||||
vrml_text_callback );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1053,45 +1042,46 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_P
|
|||
pad_dy = 0;
|
||||
|
||||
case PAD_TRAPEZOID:
|
||||
{
|
||||
double coord[8] =
|
||||
{
|
||||
double coord[8] =
|
||||
{
|
||||
-pad_w + pad_dy, -pad_h - pad_dx,
|
||||
-pad_w - pad_dy, pad_h + pad_dx,
|
||||
+pad_w - pad_dy, -pad_h + pad_dx,
|
||||
+pad_w + pad_dy, pad_h - pad_dx
|
||||
};
|
||||
-pad_w + pad_dy, -pad_h - pad_dx,
|
||||
-pad_w - pad_dy, pad_h + pad_dx,
|
||||
+pad_w - pad_dy, -pad_h + pad_dx,
|
||||
+pad_w + pad_dy, pad_h - pad_dx
|
||||
};
|
||||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
|
||||
coord[i * 2] += pad_x;
|
||||
coord[i * 2 + 1] += pad_y;
|
||||
}
|
||||
|
||||
int lines;
|
||||
|
||||
lines = aTinLayer->NewContour();
|
||||
|
||||
if( lines < 0 )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[2], -coord[3] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->EnsureWinding( lines, false ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
|
||||
coord[i * 2] += pad_x;
|
||||
coord[i * 2 + 1] += pad_y;
|
||||
}
|
||||
|
||||
int lines;
|
||||
|
||||
lines = aTinLayer->NewContour();
|
||||
|
||||
if( lines < 0 )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->AddVertex( lines, coord[2], -coord[3] ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
if( !aTinLayer->EnsureWinding( lines, false ) )
|
||||
throw( std::runtime_error( aTinLayer->GetError() ) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -1103,9 +1093,9 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
|
|||
{
|
||||
double hole_drill_w = (double) aPad->GetDrillSize().x * aModel.scale / 2.0;
|
||||
double hole_drill_h = (double) aPad->GetDrillSize().y * aModel.scale / 2.0;
|
||||
double hole_drill = std::min( hole_drill_w, hole_drill_h );
|
||||
double hole_x = aPad->GetPosition().x * aModel.scale;
|
||||
double hole_y = aPad->GetPosition().y * aModel.scale;
|
||||
double hole_drill = std::min( hole_drill_w, hole_drill_h );
|
||||
double hole_x = aPad->GetPosition().x * aModel.scale;
|
||||
double hole_y = aPad->GetPosition().y * aModel.scale;
|
||||
|
||||
// Export the hole on the edge layer
|
||||
if( hole_drill > 0 )
|
||||
|
@ -1119,7 +1109,7 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
|
|||
{
|
||||
// Oblong hole (slot)
|
||||
aModel.holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
|
||||
aPad->GetOrientation()/10.0, true, pth );
|
||||
aPad->GetOrientation()/10.0, true, pth );
|
||||
|
||||
if( pth )
|
||||
aModel.plated_holes.AddSlot( hole_x, -hole_y,
|
||||
|
@ -1194,9 +1184,9 @@ static void compose_quat( double q1[4], double q2[4], double qr[4] )
|
|||
|
||||
|
||||
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule,
|
||||
std::ofstream& aOutputFile,
|
||||
double aVRMLModelsToBiu,
|
||||
bool aExport3DFiles, const wxString& a3D_Subdir )
|
||||
std::ofstream& aOutputFile, double aVRMLModelsToBiu,
|
||||
bool aExport3DFiles, bool aUseRelativePaths,
|
||||
const wxString& a3D_Subdir )
|
||||
{
|
||||
// Reference and value
|
||||
if( aModule->Reference().IsVisible() )
|
||||
|
@ -1236,119 +1226,121 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
|
|||
if( !vrmlm->Is3DType( S3D_MASTER::FILE3D_VRML ) )
|
||||
continue;
|
||||
|
||||
wxString fname = vrmlm->GetShape3DFullFilename();
|
||||
wxFileName modelFileName = vrmlm->GetShape3DFullFilename();
|
||||
wxFileName destFileName( a3D_Subdir, modelFileName.GetName(), modelFileName.GetExt() );
|
||||
|
||||
fname.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||
wxString source_fname = fname;
|
||||
|
||||
if( aExport3DFiles )
|
||||
// Only copy VRML files.
|
||||
if( modelFileName.FileExists() && modelFileName.GetExt() == wxT( "wrl" ) )
|
||||
{
|
||||
// Change illegal characters in filenames
|
||||
ChangeIllegalCharacters( fname, true );
|
||||
fname = a3D_Subdir + wxT( "/" ) + fname;
|
||||
|
||||
if( !wxFileExists( fname ) )
|
||||
wxCopyFile( source_fname, fname );
|
||||
}
|
||||
|
||||
/* Calculate 3D shape rotation:
|
||||
* this is the rotation parameters, with an additional 180 deg rotation
|
||||
* for footprints that are flipped
|
||||
* When flipped, axis rotation is the horizontal axis (X axis)
|
||||
*/
|
||||
double rotx = -vrmlm->m_MatRotation.x;
|
||||
double roty = -vrmlm->m_MatRotation.y;
|
||||
double rotz = -vrmlm->m_MatRotation.z;
|
||||
|
||||
if( isFlipped )
|
||||
{
|
||||
rotx += 180.0;
|
||||
NEGATE( roty );
|
||||
NEGATE( rotz );
|
||||
}
|
||||
|
||||
// Do some quaternion munching
|
||||
double q1[4], q2[4], rot[4];
|
||||
build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
|
||||
build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
|
||||
// Note here aModule->GetOrientation() is in 0.1 degrees,
|
||||
// so module rotation has to be converted to radians
|
||||
build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
from_quat( q1, rot );
|
||||
|
||||
aOutputFile << "Transform {\n";
|
||||
|
||||
// A null rotation would fail the acos!
|
||||
if( rot[3] != 0.0 )
|
||||
{
|
||||
aOutputFile << " rotation " << std::setprecision( 3 );
|
||||
aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
|
||||
}
|
||||
|
||||
// adjust 3D shape local offset position
|
||||
// they are given in inch, so they are converted in board IU.
|
||||
double offsetx = vrmlm->m_MatPosition.x * IU_PER_MILS * 1000.0;
|
||||
double offsety = vrmlm->m_MatPosition.y * IU_PER_MILS * 1000.0;
|
||||
double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0;
|
||||
|
||||
if( isFlipped )
|
||||
NEGATE( offsetz );
|
||||
else // In normal mode, Y axis is reversed in Pcbnew.
|
||||
NEGATE( offsety );
|
||||
|
||||
RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
|
||||
|
||||
aOutputFile << " translation " << std::setprecision( aModel.precision );
|
||||
aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " ";
|
||||
aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " ";
|
||||
aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n";
|
||||
|
||||
aOutputFile << " scale ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n";
|
||||
|
||||
if( fname.EndsWith( wxT( "x3d" ) ) )
|
||||
{
|
||||
X3D_MODEL_PARSER* parser = new X3D_MODEL_PARSER( vrmlm );
|
||||
|
||||
if( parser )
|
||||
if( aExport3DFiles )
|
||||
{
|
||||
// embed x3d model in vrml format
|
||||
double vrml_to_x3d = aVRMLModelsToBiu;
|
||||
parser->Load( fname, vrml_to_x3d );
|
||||
wxDateTime srcModTime = modelFileName.GetModificationTime();
|
||||
wxDateTime destModTime = srcModTime;
|
||||
|
||||
try
|
||||
destModTime.SetToCurrent();
|
||||
|
||||
if( destFileName.FileExists() )
|
||||
destModTime = destFileName.GetModificationTime();
|
||||
|
||||
// Only copy the file if it doesn't exist or has been modified. This eliminates
|
||||
// the redundant file copies.
|
||||
if( srcModTime != destModTime )
|
||||
{
|
||||
aOutputFile << " children [\n ";
|
||||
aOutputFile << TO_UTF8( parser->VRML2_representation() ) << " ]\n";
|
||||
aOutputFile << " }\n";
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
delete parser;
|
||||
throw;
|
||||
wxLogDebug( wxT( "Copying 3D model %s to %s." ),
|
||||
GetChars( modelFileName.GetFullPath() ),
|
||||
GetChars( destFileName.GetFullPath() ) );
|
||||
|
||||
if( !wxCopyFile( modelFileName.GetFullPath(), destFileName.GetFullPath() ) )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/* Calculate 3D shape rotation:
|
||||
* this is the rotation parameters, with an additional 180 deg rotation
|
||||
* for footprints that are flipped
|
||||
* When flipped, axis rotation is the horizontal axis (X axis)
|
||||
*/
|
||||
double rotx = -vrmlm->m_MatRotation.x;
|
||||
double roty = -vrmlm->m_MatRotation.y;
|
||||
double rotz = -vrmlm->m_MatRotation.z;
|
||||
|
||||
if( isFlipped )
|
||||
{
|
||||
rotx += 180.0;
|
||||
NEGATE( roty );
|
||||
NEGATE( rotz );
|
||||
}
|
||||
|
||||
// Do some quaternion munching
|
||||
double q1[4], q2[4], rot[4];
|
||||
build_quat( 1, 0, 0, DEG2RAD( rotx ), q1 );
|
||||
build_quat( 0, 1, 0, DEG2RAD( roty ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
build_quat( 0, 0, 1, DEG2RAD( rotz ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
|
||||
// Note here aModule->GetOrientation() is in 0.1 degrees,
|
||||
// so module rotation has to be converted to radians
|
||||
build_quat( 0, 0, 1, DECIDEG2RAD( aModule->GetOrientation() ), q2 );
|
||||
compose_quat( q1, q2, q1 );
|
||||
from_quat( q1, rot );
|
||||
|
||||
aOutputFile << "Transform {\n";
|
||||
|
||||
// A null rotation would fail the acos!
|
||||
if( rot[3] != 0.0 )
|
||||
{
|
||||
aOutputFile << " rotation " << std::setprecision( 3 );
|
||||
aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
|
||||
}
|
||||
|
||||
// adjust 3D shape local offset position
|
||||
// they are given in inch, so they are converted in board IU.
|
||||
double offsetx = vrmlm->m_MatPosition.x * IU_PER_MILS * 1000.0;
|
||||
double offsety = vrmlm->m_MatPosition.y * IU_PER_MILS * 1000.0;
|
||||
double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0;
|
||||
|
||||
if( isFlipped )
|
||||
NEGATE( offsetz );
|
||||
else // In normal mode, Y axis is reversed in Pcbnew.
|
||||
NEGATE( offsety );
|
||||
|
||||
RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
|
||||
|
||||
aOutputFile << " translation " << std::setprecision( aModel.precision );
|
||||
aOutputFile << ( ( offsetx + aModule->GetPosition().x ) *
|
||||
aModel.scale + aModel.tx ) << " ";
|
||||
aOutputFile << ( -(offsety + aModule->GetPosition().y) *
|
||||
aModel.scale - aModel.ty ) << " ";
|
||||
aOutputFile << ( (offsetz * aModel.scale ) +
|
||||
aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n";
|
||||
aOutputFile << " scale ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " ";
|
||||
aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n";
|
||||
aOutputFile << " children [\n Inline {\n url \"";
|
||||
aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n";
|
||||
|
||||
if( aUseRelativePaths )
|
||||
{
|
||||
wxFileName tmp = destFileName;
|
||||
tmp.SetExt( wxT( "" ) );
|
||||
tmp.SetName( wxT( "" ) );
|
||||
tmp.RemoveLastDir();
|
||||
destFileName.MakeRelativeTo( tmp.GetPath() );
|
||||
}
|
||||
|
||||
wxString fn = destFileName.GetFullPath();
|
||||
fn.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||
aOutputFile << TO_UTF8( fn ) << "\"\n } ]\n";
|
||||
aOutputFile << " }\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
|
||||
double aMMtoWRMLunit, bool aExport3DFiles,
|
||||
const wxString& a3D_Subdir )
|
||||
bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, double aMMtoWRMLunit,
|
||||
bool aExport3DFiles, bool aUseRelativePaths,
|
||||
const wxString& a3D_Subdir )
|
||||
{
|
||||
wxString msg;
|
||||
BOARD* pcb = GetBoard();
|
||||
|
@ -1368,14 +1360,11 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
|
|||
SetLocaleTo_C_standard();
|
||||
|
||||
// Begin with the usual VRML boilerplate
|
||||
wxString name = aFullFileName;
|
||||
|
||||
name.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||
ChangeIllegalCharacters( name, false );
|
||||
|
||||
wxString fn = aFullFileName;
|
||||
fn.Replace( wxT( "\\" ), wxT( "/" ) );
|
||||
output_file << "#VRML V2.0 utf8\n";
|
||||
output_file << "WorldInfo {\n";
|
||||
output_file << " title \"" << TO_UTF8( name ) << " - Generated by Pcbnew\"\n";
|
||||
output_file << " title \"" << TO_UTF8( fn ) << " - Generated by Pcbnew\"\n";
|
||||
output_file << "}\n";
|
||||
|
||||
// Set the VRML world scale factor
|
||||
|
@ -1387,8 +1376,7 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
|
|||
// XXX - NOTE: we should allow the user a GUI option to specify the offset
|
||||
EDA_RECT bbbox = pcb->ComputeBoundingBox();
|
||||
|
||||
model3d.SetOffset( -model3d.scale * bbbox.Centre().x,
|
||||
model3d.scale * bbbox.Centre().y );
|
||||
model3d.SetOffset( -model3d.scale * bbbox.Centre().x, model3d.scale * bbbox.Centre().y );
|
||||
|
||||
output_file << " children [\n";
|
||||
|
||||
|
@ -1417,9 +1405,8 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
|
|||
|
||||
// Export footprints
|
||||
for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
|
||||
export_vrml_module( model3d, pcb, module, output_file,
|
||||
wrml_3D_models_scaling_factor,
|
||||
aExport3DFiles, a3D_Subdir );
|
||||
export_vrml_module( model3d, pcb, module, output_file, wrml_3D_models_scaling_factor,
|
||||
aExport3DFiles, aUseRelativePaths, a3D_Subdir );
|
||||
|
||||
// write out the board and all layers
|
||||
write_layers( model3d, output_file, pcb );
|
||||
|
@ -1443,17 +1430,3 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
|
|||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* some characters cannot be used in filenames,
|
||||
* this function change them to "_"
|
||||
*/
|
||||
static void ChangeIllegalCharacters( wxString& aFileName, bool aDirSepIsIllegal )
|
||||
{
|
||||
if( aDirSepIsIllegal )
|
||||
aFileName.Replace( wxT( "/" ), wxT( "_" ) );
|
||||
|
||||
aFileName.Replace( wxT( " " ), wxT( "_" ) );
|
||||
aFileName.Replace( wxT( ":" ), wxT( "_" ) );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue