Commit bd381d94 authored by jean-pierre charras's avatar jean-pierre charras

Pcbnew: Added VRML export, from the patch sent by Lorenzo Marcantonio (...

Pcbnew: Added VRML export, from the patch sent by Lorenzo Marcantonio ( october 2009, 11). See changelog for more info.
parents 99ab2dca b4480408
...@@ -4,6 +4,17 @@ KiCad ChangeLog 2010 ...@@ -4,6 +4,17 @@ KiCad ChangeLog 2010
Please add newer entries at the top, list the date and your name with Please add newer entries at the top, list the date and your name with
email address. email address.
2010-apr-19, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
================================================================================
++Pcbnew:
* Added VRML export, from the patch sent by Lorenzo Marcantonio ( october 2009, 11)
* Fixed :
options to control vrml export.
flipped footprints
* Tested using Cortona and Blender.
Needs more testing and refinements
2010-apr-16, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr> 2010-apr-16, UPDATE Jean-Pierre Charras <jean-pierre.charras@gipsa-lab.inpg.fr>
================================================================================ ================================================================================
++Cvpcb: ++Cvpcb:
......
...@@ -113,7 +113,7 @@ void InitKiCadAbout( wxAboutDialogInfo& info ) ...@@ -113,7 +113,7 @@ void InitKiCadAbout( wxAboutDialogInfo& info )
info.AddDeveloper( SetMsg( wxT( "Jerry Jacobs <xor.gate.engineering@gmail.com>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Jerry Jacobs <xor.gate.engineering@gmail.com>" ) ) );
info.AddDeveloper( SetMsg( wxT( "Jonas Diemer <diemer@gmx.de>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Jonas Diemer <diemer@gmx.de>" ) ) );
info.AddDeveloper( SetMsg( wxT( "KBool Library <http://boolean.klaasholwerda.nl/bool.html>" ) ) ); info.AddDeveloper( SetMsg( wxT( "KBool Library <http://boolean.klaasholwerda.nl/bool.html>" ) ) );
info.AddDeveloper( SetMsg( wxT( "Lorenzo <lomarcan@tin.it>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Lorenzo Marcantonio <lomarcan@tin.it>" ) ) );
info.AddDeveloper( SetMsg( wxT( "Marco Serantoni <marco.serantoni@gmail.com>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Marco Serantoni <marco.serantoni@gmail.com>" ) ) );
info.AddDeveloper( SetMsg( wxT( "Rok Markovic <rok@kanardia.eu>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Rok Markovic <rok@kanardia.eu>" ) ) );
info.AddDeveloper( SetMsg( wxT( "Tim Hanson <sideskate@gmail.com>" ) ) ); info.AddDeveloper( SetMsg( wxT( "Tim Hanson <sideskate@gmail.com>" ) ) );
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#endif #endif
#ifndef KICAD_BUILD_VERSION #ifndef KICAD_BUILD_VERSION
#define KICAD_BUILD_VERSION "(2010-04-13 BZR 23xx)" #define KICAD_BUILD_VERSION "(2010-04-19 BZR 23xx)"
#endif #endif
#define VERSION_STABILITY "unstable" #define VERSION_STABILITY "unstable"
......
...@@ -479,6 +479,25 @@ public: ...@@ -479,6 +479,25 @@ public:
void ExportToGenCAD( wxCommandEvent& event ); void ExportToGenCAD( wxCommandEvent& event );
/**
* Function OnExportVRML
* will export the current BOARD to a VRML file.
*/
void OnExportVRML( wxCommandEvent& event );
/**
* Function ExportVRML_File
* Creates the file(s) exporting current BOARD to a VRML file.
* @param aFullFileName = the full filename of the file to create
* @param aScale = the general scaling factor. 1.0 to export in inches
* @param aExport3DFiles = true to copy 3D shapes in the subir a3D_Subdir
* @param a3D_Subdir = sub directory where 3D sahpes files are copied
* used only when aExport3DFiles == true
* @return true if Ok.
*/
bool ExportVRML_File( const wxString & aFullFileName, double aScale,
bool aExport3DFiles, const wxString & a3D_Subdir );
/** /**
* Function ExporttoSPECCTRA * Function ExporttoSPECCTRA
* will export the current BOARD to a specctra dsn file. See * will export the current BOARD to a specctra dsn file. See
......
...@@ -51,6 +51,7 @@ set(PCBNEW_SRCS ...@@ -51,6 +51,7 @@ set(PCBNEW_SRCS
dialog_edit_module_text.cpp dialog_edit_module_text.cpp
dialog_edit_module_text_base.cpp dialog_edit_module_text_base.cpp
dialog_exchange_modules_base.cpp dialog_exchange_modules_base.cpp
dialog_export_3Dfiles_base.cpp
dialog_freeroute_exchange.cpp dialog_freeroute_exchange.cpp
dialog_freeroute_exchange_base.cpp dialog_freeroute_exchange_base.cpp
# dialog_gendrill.cpp # dialog_gendrill.cpp
...@@ -99,6 +100,7 @@ set(PCBNEW_SRCS ...@@ -99,6 +100,7 @@ set(PCBNEW_SRCS
edtxtmod.cpp edtxtmod.cpp
event_handlers_tracks_vias_sizes.cpp event_handlers_tracks_vias_sizes.cpp
export_gencad.cpp export_gencad.cpp
export_vrml.cpp
files.cpp files.cpp
find.cpp find.cpp
gen_drill_report_files.cpp gen_drill_report_files.cpp
......
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 16 2008)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "dialog_export_3Dfiles_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_EXPORT_3DFILE_BASE::DIALOG_EXPORT_3DFILE_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bSizer1;
bSizer1 = new wxBoxSizer( wxVERTICAL );
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->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_DEFAULT_STYLE|wxFLP_SAVE );
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->Wrap( -1 );
bUpperSizer->Add( m_staticText3, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
m_SubdirNameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
bUpperSizer->Add( m_SubdirNameCtrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
bSizer1->Add( bUpperSizer, 0, wxEXPAND, 5 );
wxBoxSizer* bLowerSizer;
bLowerSizer = new wxBoxSizer( wxHORIZONTAL );
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 Wrml 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( 0 );
bLowerSizer->Add( m_rb3DFilesOption, 1, wxALL|wxEXPAND, 5 );
bSizer1->Add( bLowerSizer, 1, wxEXPAND, 5 );
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bSizer1->Add( m_staticline1, 0, wxEXPAND|wxRIGHT|wxLEFT, 5 );
m_sdbSizer1 = new wxStdDialogButtonSizer();
m_sdbSizer1OK = new wxButton( this, wxID_OK );
m_sdbSizer1->AddButton( m_sdbSizer1OK );
m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer1->AddButton( m_sdbSizer1Cancel );
m_sdbSizer1->Realize();
bSizer1->Add( m_sdbSizer1, 0, wxEXPAND|wxALL, 5 );
this->SetSizer( bSizer1 );
this->Layout();
// Connect Events
m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_3DFILE_BASE::OnCancelClick ), NULL, this );
m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_3DFILE_BASE::OnOkClick ), NULL, this );
}
DIALOG_EXPORT_3DFILE_BASE::~DIALOG_EXPORT_3DFILE_BASE()
{
// Disconnect Events
m_sdbSizer1Cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_3DFILE_BASE::OnCancelClick ), NULL, this );
m_sdbSizer1OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_3DFILE_BASE::OnOkClick ), NULL, this );
}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="9" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">dialog_export_3Dfiles_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="internationalize">1</property>
<property name="name">dialog_export_3Dfiles</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="use_enum">1</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1">
<property name="bg"></property>
<property name="center"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="extra_style"></property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<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="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
<property name="subclass"></property>
<property name="title">Vrml Board Export Options:</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnActivate"></event>
<event name="OnActivateApp"></event>
<event name="OnChar"></event>
<event name="OnClose"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnHibernate"></event>
<event name="OnIconize"></event>
<event name="OnIdle"></event>
<event name="OnInitDialog"></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 class="wxBoxSizer" expanded="1">
<property name="minimum_size">-1,-1</property>
<property name="name">bSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size">450,-1</property>
<property name="name">bUpperSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxTOP|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Vrml main file filename:</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_staticText1</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</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="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">wxBOTTOM|wxRIGHT|wxLEFT|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxFilePickerCtrl" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="message">Save VRML Board File</property>
<property name="minimum_size">-1,-1</property>
<property name="name">m_filePicker</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style">wxFLP_DEFAULT_STYLE|wxFLP_SAVE</property>
<property name="subclass"></property>
<property name="tooltip"></property>
<property name="value"></property>
<property name="wildcard">*.wrl</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="OnFileChanged"></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">wxTOP|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Vrml 3D footprints shapes subdir:</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_staticText3</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</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="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">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="maxlength">0</property>
<property name="minimum_size"></property>
<property name="name">m_SubdirNameCtrl</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="tooltip"></property>
<property name="value"></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="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnText"></event>
<event name="OnTextEnter"></event>
<event name="OnTextMaxLen"></event>
<event name="OnTextURL"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</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">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxRadioBox" expanded="1">
<property name="bg"></property>
<property name="choices">&quot;Inch&quot; &quot;mm&quot; &quot;Meter&quot;</property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Units:</property>
<property name="majorDimension">1</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_rbSelectUnits</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="selection">0</property>
<property name="size"></property>
<property name="style">wxRA_SPECIFY_COLS</property>
<property name="subclass"></property>
<property name="tooltip"></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 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="bg"></property>
<property name="choices">&quot;Copy 3D Shapes Files in Subdir&quot; &quot;Use Absolute Path in Wrml File &quot;</property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></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="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_rb3DFilesOption</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="selection">0</property>
<property name="size"></property>
<property name="style">wxRA_SPECIFY_COLS</property>
<property name="subclass"></property>
<property name="tooltip"></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">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="1">
<property name="bg"></property>
<property name="context_help"></property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">m_staticline1</property>
<property name="permission">protected</property>
<property name="pos"></property>
<property name="size"></property>
<property name="style">wxLI_HORIZONTAL</property>
<property name="subclass"></property>
<property name="tooltip"></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="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">wxEXPAND|wxALL</property>
<property name="proportion">0</property>
<object class="wxStdDialogButtonSizer" expanded="1">
<property name="Apply">0</property>
<property name="Cancel">1</property>
<property name="ContextHelp">0</property>
<property name="Help">0</property>
<property name="No">0</property>
<property name="OK">1</property>
<property name="Save">0</property>
<property name="Yes">0</property>
<property name="minimum_size"></property>
<property name="name">m_sdbSizer1</property>
<property name="permission">protected</property>
<event name="OnApplyButtonClick"></event>
<event name="OnCancelButtonClick">OnCancelClick</event>
<event name="OnContextHelpButtonClick"></event>
<event name="OnHelpButtonClick"></event>
<event name="OnNoButtonClick"></event>
<event name="OnOKButtonClick">OnOkClick</event>
<event name="OnSaveButtonClick"></event>
<event name="OnYesButtonClick"></event>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 16 2008)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __dialog_export_3Dfiles_base__
#define __dialog_export_3Dfiles_base__
#include <wx/intl.h>
#include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/filepicker.h>
#include <wx/textctrl.h>
#include <wx/sizer.h>
#include <wx/radiobox.h>
#include <wx/statline.h>
#include <wx/button.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_EXPORT_3DFILE_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_EXPORT_3DFILE_BASE : public wxDialog
{
private:
protected:
wxStaticText* m_staticText1;
wxFilePickerCtrl* m_filePicker;
wxStaticText* m_staticText3;
wxTextCtrl* m_SubdirNameCtrl;
wxRadioBox* m_rbSelectUnits;
wxRadioBox* m_rb3DFilesOption;
wxStaticLine* m_staticline1;
wxStdDialogButtonSizer* m_sdbSizer1;
wxButton* m_sdbSizer1OK;
wxButton* m_sdbSizer1Cancel;
// Virtual event handlers, overide them in your derived class
virtual void OnCancelClick( wxCommandEvent& event ){ event.Skip(); }
virtual void OnOkClick( wxCommandEvent& event ){ event.Skip(); }
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();
};
#endif //__dialog_export_3Dfiles_base__
#include "fctsys.h"
#include "common.h"
#include "confirm.h"
#include "kicad_string.h"
#include "gestfich.h"
#include "pcbnew.h"
#include "wxPcbStruct.h"
#include "drawtxt.h"
#include "trigo.h"
#include "appl_wxstruct.h"
#include "3d_struct.h"
#include <vector>
#include <cmath>
/* the dialog to create VRML files, derived from DIALOG_EXPORT_3DFILE_BASE,
* created by wxFormBuilder
*/
#include "dialog_export_3Dfiles_base.h" // the wxFormBuilder header file
#define OPTKEY_OUTPUT_UNIT wxT("VrmlExportUnit" )
#define OPTKEY_3DFILES_OPT wxT("VrmlExport3DShapeFilesOpt" )
class DIALOG_EXPORT_3DFILE : public DIALOG_EXPORT_3DFILE_BASE
{
private:
WinEDA_PcbFrame* m_parent;
wxConfig* m_config;
int m_unitsOpt; // to remember last option
int m_3DFilesOpt; // to remember last option
virtual void OnCancelClick( wxCommandEvent& event ){ EndModal( wxID_CANCEL ); }
virtual void OnOkClick( wxCommandEvent& event ){ EndModal( wxID_OK ); }
public:
DIALOG_EXPORT_3DFILE( WinEDA_PcbFrame* parent ) :
DIALOG_EXPORT_3DFILE_BASE( parent )
{
m_parent = parent;
m_config = wxGetApp().m_EDA_Config;
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);
GetSizer()->SetSizeHints( this );
Centre();
}
~DIALOG_EXPORT_3DFILE()
{
m_unitsOpt = GetUnits( );
m_3DFilesOpt = Get3DFilesOption( );
m_config->Write( OPTKEY_OUTPUT_UNIT, m_unitsOpt );
m_config->Write( OPTKEY_3DFILES_OPT, m_3DFilesOpt );
};
void SetSubdir( const wxString & aDir )
{
m_SubdirNameCtrl->SetValue( aDir);
}
wxString GetSubdir( )
{
return m_SubdirNameCtrl->GetValue( );
}
wxFilePickerCtrl* FilePicker()
{
return m_filePicker;
}
int GetUnits( )
{
return m_unitsOpt = m_rbSelectUnits->GetSelection();
}
int Get3DFilesOption( )
{
return m_3DFilesOpt = m_rb3DFilesOption->GetSelection();
}
};
/* I use this a lot... */
static const double PI2 = M_PI / 2;
/* Absolutely not optimized triangle bag :D */
struct VRMLPt
{
double x, y, z;
};
struct FlatPt
{
FlatPt( double _x = 0, double _y = 0 ) : x( _x ), y( _y ) { }
double x, y;
};
struct Triangle
{
Triangle( double x1, double y1, double z1,
double x2, double y2, double z2,
double x3, double y3, double z3 )
{
p1.x = x1; p1.y = y1; p1.z = z1;
p2.x = x2; p2.y = y2; p2.z = z2;
p3.x = x3; p3.y = y3; p3.z = z3;
}
Triangle() { }
VRMLPt p1, p2, p3;
};
typedef std::vector<Triangle> TriangleBag;
/* A flat triangle fan */
struct FlatFan
{
FlatPt c;
std::vector<FlatPt> pts;
void add( double x, double y )
{
pts.push_back( FlatPt( x, y ) );
}
void bag( int layer, bool close = true );
};
/* A flat quad ring */
struct FlatRing
{
std::vector<FlatPt> inner;
std::vector<FlatPt> outer;
void add_inner( double x, double y )
{
inner.push_back( FlatPt( x, y ) );
}
void add_outer( double x, double y )
{
outer.push_back( FlatPt( x, y ) );
}
void bag( int layer, bool close = true );
};
/* A vertical quad loop */
struct VLoop
{
std::vector<FlatPt> pts;
double z_top, z_bottom;
void add( double x, double y )
{
pts.push_back( FlatPt( x, y ) );
}
void bag( TriangleBag& triangles, bool close = true );
};
/* The bags for all the layers */
static TriangleBag layer_triangles[LAYER_COUNT];
static TriangleBag via_triangles[4];
static double layer_z[LAYER_COUNT];
static void bag_flat_triangle( int layer, /*{{{*/
double x1, double y1,
double x2, double y2,
double x3, double y3 )
{
double z = layer_z[layer];
layer_triangles[layer].push_back( Triangle( x1, y1, z,
x2, y2, z, x3, y3, z ) );
}
void FlatFan::bag( int layer, bool close ) /*{{{*/
{
unsigned i;
for( i = 0; i < pts.size() - 1; i++ )
bag_flat_triangle( layer, c.x, c.y, pts[i].x, pts[i].y,
pts[i + 1].x, pts[i + 1].y );
if( close )
bag_flat_triangle( layer, c.x, c.y, pts[i].x, pts[i].y,
pts[0].x, pts[0].y );
}
static void bag_flat_quad( int layer, /*{{{*/
double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4 )
{
bag_flat_triangle( layer, x1, y1, x3, y3, x2, y2 );
bag_flat_triangle( layer, x2, y2, x3, y3, x4, y4 );
}
void FlatRing::bag( int layer, bool close ) /*{{{*/
{
unsigned i;
for( i = 0; i < inner.size() - 1; i++ )
bag_flat_quad( layer,
inner[i].x, inner[i].y,
outer[i].x, outer[i].y,
inner[i + 1].x, inner[i + 1].y,
outer[i + 1].x, outer[i + 1].y );
if( close )
bag_flat_quad( layer,
inner[i].x, inner[i].y,
outer[i].x, outer[i].y,
inner[0].x, inner[0].y,
outer[0].x, outer[0].y );
}
static void bag_vquad( TriangleBag& triangles, /*{{{*/
double x1, double y1, double x2, double y2,
double z1, double z2 )
{
triangles.push_back( Triangle(
x1, y1, z1,
x2, y2, z1,
x2, y2, z2 ) );
triangles.push_back( Triangle(
x1, y1, z1,
x2, y2, z2,
x1, y1, z2 ) );
}
void VLoop::bag( TriangleBag& triangles, bool close ) /*{{{*/
{
unsigned i;
for( i = 0; i < pts.size() - 1; i++ )
bag_vquad( triangles, pts[i].x, pts[i].y,
pts[i + 1].x, pts[i + 1].y,
z_top, z_bottom );
if( close )
bag_vquad( triangles, pts[i].x, pts[i].y,
pts[0].x, pts[0].y,
z_top, z_bottom );
}
static void write_triangle_bag( FILE* output_file, int color_index, /*{{{*/
const TriangleBag& triangles )
{
/* A lot of nodes are not required, but blender sometimes chokes
* without them */
static const char* shape_boiler[] =
{
"Transform {\n",
" translation 0.0 0.0 0.0\n",
" rotation 1.0 0.0 0.0 0.0\n",
" scale 1.0 1.0 1.0\n",
" children [\n",
" Group {\n",
" children [\n",
" Shape {\n",
" appearance Appearance {\n",
" material Material {\n",
0, /* Material marker */
" ambientIntensity 0.8\n",
" transparency 0.2\n",
" shininess 0.2\n",
" }\n",
" }\n",
" geometry IndexedFaceSet {\n",
" solid TRUE\n",
" coord Coordinate {\n",
" point [\n",
0, /* Coordinates marker */
" ]\n",
" }\n",
" coordIndex [\n",
0, /* Index marker */
" ]\n",
" }\n",
" }\n",
" ]\n",
" }\n",
" ]\n",
"}\n",
0 /* End marker */
};
int marker_found = 0, lineno = 0;
while( marker_found < 4 )
{
if( shape_boiler[lineno] )
fputs( shape_boiler[lineno], output_file );
else
{
marker_found++;
switch( marker_found )
{
case 1: /* Material marker */
fprintf( output_file,
" diffuseColor %g %g %g\n",
(double) ColorRefs[color_index].m_Red / 255.0,
(double) ColorRefs[color_index].m_Green / 255.0,
(double) ColorRefs[color_index].m_Blue / 255.0 );
fprintf( output_file,
" specularColor %g %g %g\n",
(double) ColorRefs[color_index].m_Red / 255.0,
(double) ColorRefs[color_index].m_Green / 255.0,
(double) ColorRefs[color_index].m_Blue / 255.0 );
fprintf( output_file,
" emissiveColor %g %g %g\n",
(double) ColorRefs[color_index].m_Red / 255.0,
(double) ColorRefs[color_index].m_Green / 255.0,
(double) ColorRefs[color_index].m_Blue / 255.0 );
break;
case 2:
{
/* Coordinates marker */
for( TriangleBag::const_iterator i = triangles.begin();
i != triangles.end();
i++ )
{
fprintf( output_file, "%g %g %g\n",
i->p1.x, -i->p1.y, i->p1.z );
fprintf( output_file, "%g %g %g\n",
i->p2.x, -i->p2.y, i->p2.z );
fprintf( output_file, "%g %g %g\n",
i->p3.x, -i->p3.y, i->p3.z );
}
}
break;
case 3:
{
/* Index marker */
/* OK, that's sick ... */
int j = 0;
for( TriangleBag::const_iterator i = triangles.begin();
i != triangles.end();
i++ )
{
fprintf( output_file, "%d %d %d -1\n",
j, j + 1, j + 2 );
j += 3;
}
}
break;
default:
break;
}
}
lineno++;
}
}
static void compute_layer_Zs( BOARD* pcb ) /*{{{*/
{
int copper_layers = pcb->GetCopperLayerCount( );
// We call it 'layer' thickness, but it's the whole board thickness!
double board_thickness = pcb->GetBoardDesignSettings()->m_BoardThickness;
double half_thickness = board_thickness / 2;
/* Compute each layer's Z value, more or less like the 3d view */
for( int i = 0; i <= LAYER_N_FRONT; i++ )
{
if( i < copper_layers )
layer_z[i] = board_thickness * i / (copper_layers - 1) - half_thickness;
else
layer_z[i] = half_thickness; /* The component layer... */
}
/* To avoid rounding interference, we apply an epsilon to each
* successive layer */
const double epsilon_z = 10; /* That's 1 mils, about 1/50 mm */
layer_z[SOLDERPASTE_N_BACK] = -half_thickness - epsilon_z * 4;
layer_z[ADHESIVE_N_BACK] = -half_thickness - epsilon_z * 3;
layer_z[SILKSCREEN_N_BACK] = -half_thickness - epsilon_z * 2;
layer_z[SOLDERMASK_N_BACK] = -half_thickness - epsilon_z;
layer_z[SOLDERMASK_N_FRONT] = half_thickness + epsilon_z;
layer_z[SILKSCREEN_N_FRONT] = half_thickness + epsilon_z * 2;
layer_z[ADHESIVE_N_FRONT] = half_thickness + epsilon_z * 3;
layer_z[SOLDERPASTE_N_FRONT] = half_thickness + epsilon_z * 4;
layer_z[DRAW_N] = half_thickness + epsilon_z * 5;
layer_z[COMMENT_N] = half_thickness + epsilon_z * 6;
layer_z[ECO1_N] = half_thickness + epsilon_z * 7;
layer_z[ECO2_N] = half_thickness + epsilon_z * 8;
layer_z[EDGE_N] = 0;
}
static void export_vrml_line( int layer, double startx, double starty, /*{{{*/
double endx, double endy, double width, int divisions )
{
double r = width / 2;
double angle = atan2( endy - starty, endx - startx );
double alpha;
FlatFan fan;
/* Output the 'bone' as a triangle fan, this is the fan centre */
fan.c.x = (startx + endx) / 2;
fan.c.y = (starty + endy) / 2;
/* The 'end' side cap */
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
fan.add( endx + r * cos( alpha ), endy + r * sin( alpha ) );
alpha = angle + PI2;
fan.add( endx + r * cos( alpha ), endy + r * sin( alpha ) );
/* The 'start' side cap */
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
fan.add( startx + r * cos( alpha ), starty + r * sin( alpha ) );
alpha = angle + 3 * PI2;
fan.add( startx + r * cos( alpha ), starty + r * sin( alpha ) );
/* Export the fan */
fan.bag( layer );
}
static void export_vrml_circle( int layer, double startx, double starty, /*{{{*/
double endx, double endy, double width, int divisions )
{
double hole, rayon;
FlatRing ring;
rayon = hypot( startx - endx, starty - endy ) + ( width / 2);
hole = rayon - width;
for( double alpha = 0; alpha < M_PI * 2; alpha += M_PI * 2 / divisions )
{
ring.add_inner( startx + hole * cos( alpha ), starty + hole * sin( alpha ) );
ring.add_outer( startx + rayon * cos( alpha ), starty + rayon * sin( alpha ) );
}
ring.bag( layer );
}
static void export_vrml_slot( TriangleBag& triangles, /*{{{*/
int top_layer, int bottom_layer, double xc, double yc,
double dx, double dy, int orient, int divisions )
{
double capx, capy; /* Cap center */
VLoop loop;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
double angle = orient / 1800.0 * M_PI;
if( dy > dx )
{
EXCHG( dx, dy );
angle += PI2;
}
/* The exchange above means that cutter radius is alvays dy/2 */
double r = dy / 2;
double alpha;
/* The first side cap */
capx = xc + cos( angle ) * dx / 2;
capy = yc + sin( angle ) * dx / 2;
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + PI2;
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
/* The other side cap */
capx = xc - cos( angle ) * dx / 2;
capy = yc - sin( angle ) * dx / 2;
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + 3 * PI2;
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
loop.bag( triangles );
}
static void export_vrml_hole( TriangleBag& triangles, /*{{{*/
int top_layer, int bottom_layer, double xc, double yc, double hole,
int divisions )
{
VLoop loop;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
for( double alpha = 0; alpha < M_PI * 2; alpha += M_PI * 2 / divisions )
loop.add( xc + cos( alpha ) * hole, yc + sin( alpha ) * hole );
loop.bag( triangles );
}
static void export_vrml_varc( TriangleBag& triangles, /*{{{*/
int top_layer, int bottom_layer, double startx, double starty,
double endx, double endy, int divisions )
{
VLoop loop;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
double angle = atan2( endx - startx, endy - starty );
double rayon = hypot( startx - endx, starty - endy );
for( double alpha = angle; alpha < angle + PI2; alpha += PI2 / divisions )
{
loop.add( startx + cos( alpha ) * rayon, starty + sin( alpha ) * rayon );
}
loop.bag( triangles );
}
static void export_vrml_oval_pad( int layer, /*{{{*/
double xc, double yc,
double dx, double dy, int orient, int divisions )
{
double capx, capy; /* Cap center */
FlatFan fan;
fan.c.x = xc;
fan.c.y = yc;
double angle = orient / 1800.0 * M_PI;
if( dy > dx )
{
EXCHG( dx, dy );
angle += PI2;
}
/* The exchange above means that cutter radius is alvays dy/2 */
double r = dy / 2;
double alpha;
/* The first side cap */
capx = xc + cos( angle ) * dx / 2;
capy = yc + sin( angle ) * dx / 2;
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + PI2;
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
/* The other side cap */
capx = xc - cos( angle ) * dx / 2;
capy = yc - sin( angle ) * dx / 2;
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + 3 * PI2;
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
fan.bag( layer );
}
static void export_vrml_arc( int layer, double startx, double starty, /*{{{*/
double endx, double endy, double width, int divisions )
{
FlatRing ring;
double hole, rayon;
double angle = atan2( endx - startx, endy - starty );
rayon = hypot( startx - endx, starty - endy ) + ( width / 2);
hole = rayon - width;
for( double alpha = angle; alpha < angle + PI2; alpha += PI2 / divisions )
{
ring.add_inner( startx + cos( alpha ) * hole, starty + sin( alpha ) * hole );
ring.add_outer( startx + cos( alpha ) * rayon, starty + sin( alpha ) * rayon );
}
ring.bag( layer, false );
}
static void export_vrml_drawsegment( DRAWSEGMENT* drawseg ) /*{{{*/
{
int layer = drawseg->GetLayer();
double w = drawseg->m_Width;
double x = drawseg->m_Start.x;
double y = drawseg->m_Start.y;
double xf = drawseg->m_End.x;
double yf = drawseg->m_End.y;
/* Items on the edge layer are high, not thick */
if( layer == EDGE_N )
{
switch( drawseg->m_Shape )
{
/* There is a special 'varc' primitive for this */
case S_ARC:
export_vrml_varc( layer_triangles[layer],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
x, y, xf, yf, 4 );
break;
/* Circles on edge are usually important holes */
case S_CIRCLE:
export_vrml_hole( layer_triangles[layer],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER, x, y,
hypot( xf - x, yf - y ) / 2, 12 );
break;
default:
{
/* Simply a quad */
double z_top = layer_z[FIRST_COPPER_LAYER];
double z_bottom = layer_z[LAST_COPPER_LAYER];
bag_vquad( layer_triangles[layer], x, y, xf, yf, z_top, z_bottom );
break;
}
}
}
else
{
switch( drawseg->m_Shape )
{
case S_ARC:
export_vrml_arc( layer, x, y, xf, yf, w, 3 );
break;
case S_CIRCLE:
export_vrml_circle( layer, x, y, xf, yf, w, 12 );
break;
default:
export_vrml_line( layer, x, y, xf, yf, w, 1 );
break;
}
}
}
/* C++ doesn't have closures and neither continuation forms... this is
* for coupling the vrml_text_callback with the common parameters */
static int s_text_layer;
static int s_text_width;
static void vrml_text_callback( int x0, int y0, int xf, int yf )
{
export_vrml_line( s_text_layer, x0, y0, xf, yf, s_text_width, 1 );
}
static void export_vrml_pcbtext( TEXTE_PCB* text ) /*{{{*/
/*************************************************************/
{
/* Coupling by globals! Ewwww... */
s_text_layer = text->GetLayer();
s_text_width = text->m_Width;
wxSize size = text->m_Size;
if( text->m_Mirror )
NEGATE( size.x );
if( text->m_MultilineAllowed )
{
wxPoint pos = text->m_Pos;
wxArrayString* list = wxStringSplit( text->m_Text, '\n' );
wxPoint offset;
offset.y = text->GetInterline();
RotatePoint( &offset, text->m_Orient );
for( unsigned i = 0; i<list->Count(); i++ )
{
wxString txt = list->Item( i );
DrawGraphicText( NULL, NULL, pos, (EDA_Colors) 0,
txt, text->m_Orient, size,
text->m_HJustify, text->m_VJustify,
text->m_Width, text->m_Italic,
true,
vrml_text_callback );
pos += offset;
}
delete (list);
}
else
{
DrawGraphicText( NULL, NULL, text->m_Pos, (EDA_Colors) 0,
text->m_Text, text->m_Orient, size,
text->m_HJustify, text->m_VJustify,
text->m_Width, text->m_Italic,
true,
vrml_text_callback );
}
}
static void export_vrml_drawings( BOARD* pcb ) /*{{{*/
{
/* draw graphic items */
for( EDA_BaseStruct* drawing = pcb->m_Drawings;
drawing != 0;
drawing = drawing->Next() )
{
switch( drawing->Type() )
{
case TYPE_DRAWSEGMENT:
export_vrml_drawsegment( (DRAWSEGMENT*) drawing );
break;
case TYPE_TEXTE:
export_vrml_pcbtext( (TEXTE_PCB*) drawing );
break;
default:
break;
}
}
}
static void export_round_padstack( BOARD* pcb, double x, double y, double r, /*{{{*/
int bottom_layer, int top_layer, int divisions )
{
int copper_layers = pcb->GetCopperLayerCount( );
for( int layer = bottom_layer; layer < copper_layers; layer++ )
{
/* The last layer is always the component one, unless it's single face */
if( (layer > FIRST_COPPER_LAYER) && (layer == copper_layers - 1) )
layer = LAST_COPPER_LAYER;
if( layer <= top_layer )
export_vrml_circle( layer, x, y, x + r / 2, y, r, divisions );
}
}
static void export_vrml_via( BOARD* pcb, SEGVIA* via ) /*{{{*/
{
double x, y, r, hole;
int top_layer, bottom_layer;
r = via->m_Width / 2;
hole = via->GetDrillValue() / 2;
x = via->m_Start.x;
y = via->m_Start.y;
via->ReturnLayerPair( &top_layer, &bottom_layer );
/* Export the via padstack */
export_round_padstack( pcb, x, y, r, bottom_layer, top_layer, 8 );
/* Drill a rough hole */
export_vrml_hole( via_triangles[via->m_Shape], top_layer, bottom_layer, x, y, hole, 8 );
}
static void export_vrml_tracks( BOARD* pcb ) /*{{{*/
{
for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
{
if( track->Type() == TYPE_VIA )
export_vrml_via( pcb, (SEGVIA*) track );
else
export_vrml_line( track->GetLayer(), track->m_Start.x, track->m_Start.y,
track->m_End.x, track->m_End.y, track->m_Width, 4 );
}
}
static void export_vrml_zones( BOARD* pcb ) /*{{{*/
{
/* Export fill segments */
for( SEGZONE* segzone = pcb->m_Zone;
segzone != 0;
segzone = segzone->Next() )
{
/* Fill tracks are exported with low subdivisions */
if( segzone->Type() == TYPE_ZONE )
export_vrml_line( segzone->GetLayer(), segzone->m_Start.x, segzone->m_Start.y,
segzone->m_End.x, segzone->m_End.y, segzone->m_Width, 1 );
}
/* Export zone outlines */
for( int i = 0; i < pcb->GetAreaCount(); i++ )
{
ZONE_CONTAINER* zone = pcb->GetArea( i );
if( ( zone->m_FilledPolysList.size() == 0 )
||( zone->m_ZoneMinThickness <= 1 ) )
continue;
int width = zone->m_ZoneMinThickness;
if( width > 0 )
{
int imax = zone->m_FilledPolysList.size() - 1;
int layer = zone->GetLayer();
CPolyPt* firstcorner = &zone->m_FilledPolysList[0];
CPolyPt* begincorner = firstcorner;
/* I'm not really positive about what he's doing here... */
for( int ic = 1; ic <= imax; ic++ )
{
CPolyPt* endcorner = &zone->m_FilledPolysList[ic];
if( begincorner->utility == 0 ) // Draw only basic outlines, not extra segments
export_vrml_line( layer, begincorner->x, begincorner->y,
endcorner->x, endcorner->y, width, 1 );
if( (endcorner->end_contour) || (ic == imax) ) // the last corner of a filled area is found: draw it
{
if( endcorner->utility == 0 ) // Draw only basic outlines, not extra segments
export_vrml_line( layer, endcorner->x, endcorner->y,
firstcorner->x, firstcorner->y, width, 1 );
ic++;
/* A new contour? */
if( ic < imax - 1 )
begincorner = firstcorner = &zone->m_FilledPolysList[ic];
}
else
begincorner = endcorner;
}
}
}
}
static void export_vrml_text_module( TEXTE_MODULE* module ) /*{{{*/
{
if( !module->m_NoShow )
{
wxSize size = module->m_Size;
if( module->m_Mirror )
NEGATE( size.x ); // Text is mirrored
s_text_layer = module->GetLayer();
s_text_width = module->m_Width;
DrawGraphicText( NULL, NULL, module->m_Pos, (EDA_Colors) 0,
module->m_Text, module->GetDrawRotation(), size,
module->m_HJustify, module->m_VJustify,
module->m_Width, module->m_Italic,
true,
vrml_text_callback );
}
}
static void export_vrml_edge_module( EDGE_MODULE* module ) /*{{{*/
{
int layer = module->GetLayer();
double x = module->m_Start.x;
double y = module->m_Start.y;
double xf = module->m_End.x;
double yf = module->m_End.y;
double w = module->m_Width;
switch( module->m_Shape )
{
case S_ARC:
export_vrml_arc( layer, x, y, xf, yf, w, 3 );
break;
case S_CIRCLE:
export_vrml_circle( layer, x, y, xf, yf, w, 12 );
break;
default:
export_vrml_line( layer, x, y, xf, yf, w, 1 );
break;
}
}
static void export_vrml_pad( BOARD* pcb, D_PAD* pad ) /*{{{*/
{
double hole_drill_w = (double) pad->m_Drill.x / 2;
double hole_drill_h = (double) pad->m_Drill.y / 2;
double hole_drill = MIN( hole_drill_w, hole_drill_h );
double hole_x = pad->m_Pos.x;
double hole_y = pad->m_Pos.y;
/* Export the hole on the edge layer */
if( hole_drill > 0 )
{
if( pad->m_DrillShape == PAD_OVAL )
{
/* Oblong hole (slot) */
export_vrml_slot( layer_triangles[EDGE_N],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
hole_x, hole_y, hole_drill_w, hole_drill_h, pad->m_Orient, 6 );
}
else
{
// Drill a round hole
export_vrml_hole( layer_triangles[EDGE_N],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
hole_x, hole_y, hole_drill, 12 );
}
}
/* The pad proper, on the selected layers */
unsigned long layer_mask = pad->m_Masque_Layer;
int copper_layers = pcb->GetCopperLayerCount( );
/* The (maybe offseted) pad position */
wxPoint pad_pos = pad->ReturnShapePos();
double pad_x = pad_pos.x;
double pad_y = pad_pos.y;
wxSize pad_delta = pad->m_DeltaSize;
double pad_dx = pad_delta.x / 2;
double pad_dy = pad_delta.y / 2;
double pad_w = pad->m_Size.x / 2;
double pad_h = pad->m_Size.y / 2;
for( int layer = FIRST_COPPER_LAYER; layer < copper_layers; layer++ )
{
/* The last layer is always the component one, unless it's single face */
if( (layer > FIRST_COPPER_LAYER) && (layer == copper_layers - 1) )
layer = LAST_COPPER_LAYER;
if( layer_mask & (1 << layer) )
{
/* OK, the pad is on this layer, export it */
switch( pad->m_PadShape & 0x7F ) /* What is the masking for? */
{
case PAD_CIRCLE:
export_vrml_circle( layer, pad_x, pad_y,
pad_x + pad_w / 2, pad_y, pad_w, 12 );
break;
case PAD_OVAL:
export_vrml_oval_pad( layer,
pad_x, pad_y,
pad_w * 2, pad_h * 2, pad->m_Orient, 4 );
break;
case PAD_RECT:
/* Just to be sure :D */
pad_dx = 0;
pad_dy = 0;
case PAD_TRAPEZOID:
{
int 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,
};
for( int i = 0; i < 4; i++ )
{
RotatePoint( &coord[i * 2], &coord[i * 2 + 1], pad->m_Orient );
coord[i * 2] += pad_x;
coord[i * 2 + 1] += pad_y;
}
bag_flat_quad( layer, coord[0], coord[1],
coord[2], coord[3],
coord[4], coord[5],
coord[6], coord[7] );
}
break;
}
}
}
}
/* From axis/rot to quaternion */
static void build_quat( double x, double y, double z, double a, double q[4] )
{
double sina = sin( a / 2 );
q[0] = x * sina;
q[1] = y * sina;
q[2] = z * sina;
q[3] = cos( a / 2 );
}
/* From quaternion to axis/rot */
static void from_quat( double q[4], double rot[4] )
{
rot[3] = acos( q[3] ) * 2;
for( int i = 0; i < 3; i++ )
{
rot[i] = q[i] / sin( rot[3] / 2 );
}
}
/* Quaternion composition */
static void compose_quat( double q1[4], double q2[4], double qr[4] )
{
double tmp[4];
tmp[0] = q2[3] *q1[0] + q2[0] *q1[3] + q2[1] *q1[2] - q2[2] *q1[1];
tmp[1] = q2[3] *q1[1] + q2[1] *q1[3] + q2[2] *q1[0] - q2[0] *q1[2];
tmp[2] = q2[3] *q1[2] + q2[2] *q1[3] + q2[0] *q1[1] - q2[1] *q1[0];
tmp[3] = q2[3] *q1[3] - q2[0] *q1[0] - q2[1] *q1[1] - q2[2] *q1[2];
qr[0] = tmp[0]; qr[1] = tmp[1];
qr[2] = tmp[2]; qr[3] = tmp[3];
}
static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
FILE* aOutputFile, double aScalingFactor,
bool aExport3DFiles, const wxString & a3D_Subdir )
{
/* Reference and value */
export_vrml_text_module( aModule->m_Reference );
export_vrml_text_module( aModule->m_Value );
/* Export module edges */
for( EDA_BaseStruct* item = aModule->m_Drawings;
item != NULL;
item = item->Next() )
{
switch( item->Type() )
{
case TYPE_TEXTE_MODULE:
export_vrml_text_module( dynamic_cast<TEXTE_MODULE*>(item) );
break;
case TYPE_EDGE_MODULE:
export_vrml_edge_module( dynamic_cast<EDGE_MODULE*>(item) );
break;
default:
break;
}
}
/* Export pads */
for( D_PAD* pad = aModule->m_Pads;
pad != 0;
pad = pad->Next() )
export_vrml_pad( aPcb, pad );
bool isFlipped = aModule->GetLayer() == LAYER_N_BACK;
/* Export the object VRML model(s) */
for( S3D_MASTER* vrmlm = aModule->m_3D_Drawings;
vrmlm != 0; vrmlm = vrmlm->Next() )
{
wxString fname = vrmlm->m_Shape3DName;
if( fname.IsEmpty() )
continue;
if( ! wxFileName::FileExists( fname ) )
{
wxFileName fn = fname;
fname = wxGetApp().FindLibraryPath( fn );
if( fname.IsEmpty() ) // keep "short" name if full filemane not found
fname = vrmlm->m_Shape3DName;
}
fname.Replace(wxT("\\"), wxT("/" ) );
wxString source_fname = fname;
if( aExport3DFiles )
{
fname.Replace(wxT("/"), wxT("_" ) );
fname.Replace(wxT(":_"), wxT("_" ) );
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)
*/
int rotx = vrmlm->m_MatRotation.x;
if ( isFlipped )
rotx += 1800;
/* Do some quaternion munching */
double q1[4], q2[4], rot[4];
build_quat( 1, 0, 0, rotx / 1800.0 * M_PI, q1 );
build_quat( 0, 1, 0, vrmlm->m_MatRotation.y / 1800.0 * M_PI, q2 );
compose_quat( q1, q2, q1 );
build_quat( 0, 0, 1, vrmlm->m_MatRotation.z / 1800.0 * M_PI, q2 );
compose_quat( q1, q2, q1 );
build_quat( 0, 0, 1, aModule->m_Orient / 1800.0 * M_PI, q2 );
compose_quat( q1, q2, q1 );
from_quat( q1, rot );
fprintf( aOutputFile, "Transform {\n" );
/* A null rotation would fail the acos! */
if( rot[3] != 0.0 )
{
fprintf( aOutputFile, " rotation %g %g %g %g\n",
rot[0], rot[1], rot[2], rot[3] );
}
fprintf( aOutputFile, " scale %g %g %g\n",
vrmlm->m_MatScale.x * aScalingFactor,
vrmlm->m_MatScale.y * aScalingFactor,
vrmlm->m_MatScale.z * aScalingFactor );
fprintf( aOutputFile, " translation %g %g %g\n",
vrmlm->m_MatPosition.x + aModule->m_Pos.x,
-vrmlm->m_MatPosition.y - aModule->m_Pos.y,
vrmlm->m_MatPosition.z + layer_z[aModule->GetLayer()] );
fprintf( aOutputFile,
" children [\n Inline {\n url [ \"%s\" ]\n } ]\n",
CONV_TO_UTF8( fname ) );
fprintf( aOutputFile, " }\n" );
}
}
static void write_and_empty_triangle_bag( FILE* output_file,
TriangleBag& triangles, int color )
{
if( !triangles.empty() )
{
write_triangle_bag( output_file, color, triangles );
triangles.clear( );
}
}
/**
* Function OnExportVRML
* will export the current BOARD to a VRML file.
*/
void WinEDA_PcbFrame::OnExportVRML( wxCommandEvent& event )
{
wxFileName fn;
static wxString subDirFor3Dshapes = wxT("shapes3D");
double scaleList[3] = { 1.0, 25.4, 25.4/1000 };
// Build default file name
wxString ext = wxT( "wrl" );
fn = GetScreen()->m_FileName;
fn.SetExt( ext );
DIALOG_EXPORT_3DFILE dlg( this );
dlg.FilePicker()->SetPath( fn.GetFullPath() );
dlg.SetSubdir( subDirFor3Dshapes );
if( dlg.ShowModal() != wxID_OK )
return;
double scale = scaleList[dlg.GetUnits( )]; // final scale export
bool export3DFiles = dlg.Get3DFilesOption( ) == 0;
wxBusyCursor dummy;
wxString fullFilename = dlg.FilePicker()->GetPath();
subDirFor3Dshapes = dlg.GetSubdir();
if( ! wxDirExists( subDirFor3Dshapes ) )
wxMkdir( subDirFor3Dshapes );
if( ! ExportVRML_File( fullFilename, scale, export3DFiles, subDirFor3Dshapes ) )
{
wxString msg = _( "Unable to create " ) + fullFilename;
DisplayError( this, msg );
return;
}
}
/**
* Function ExportVRML_File
* Creates the file(s) exporting current BOARD to a VRML file.
* @param aFullFileName = the full filename of the file to create
* @param aScale = the general scaling factor. 1.0 to export in inch
* @param aExport3DFiles = true to copy 3D shapes in the subdir a3D_Subdir
* @param a3D_Subdir = sub directory where 3D shapes files are copied
* used only when aExport3DFiles == true
* @return true if Ok.
*/
/* When copying 3D shapes files, the new filename is build from
* the full path name, changing the separators by underscore.
* this is needed because files with the same shortname can exist in different directories
*/
bool WinEDA_PcbFrame::ExportVRML_File( const wxString & aFullFileName,
double aScale, bool aExport3DFiles,
const wxString & a3D_Subdir )
{
wxString msg;
FILE* output_file;
BOARD* pcb = GetBoard();
output_file = wxFopen( aFullFileName, wxT( "wt" ) );
if( output_file == NULL )
return false;
// Switch the locale to standard C (needed to print floating point numbers like 1.3)
SetLocaleTo_C_standard();
/* Begin with the usual VRML boilerplate */
fprintf( output_file, "#VRML V2.0 utf8\n"
"WorldInfo {\n"
" title \"%s - Generated by PCBNEW\"\n"
"}\n", CONV_TO_UTF8( aFullFileName ) );
/* The would be in decimils and not in meters, as the standard wants.
* It is trivial to embed everything in a transform node to
* fix it. For example here we build the world in inches...
*/
/* scaling factor to convert internal units (decimils) to inches
*/
double board_scaling_factor = 0.0001;
/* auxiliary scale to export to a different scale.
*/
double general_scaling_factor = board_scaling_factor * aScale;
fprintf(output_file, "Transform {\n");
fprintf(output_file, " scale %g %g %g\n",
general_scaling_factor, general_scaling_factor, general_scaling_factor );
/* Define the translation to have the board centre to the 2D axis origin
* more easy for rotations...
*/
pcb->ComputeBoundaryBox();
double dx = board_scaling_factor * pcb->m_BoundaryBox.Centre().x * aScale;
double dy = board_scaling_factor * pcb->m_BoundaryBox.Centre().y * aScale;
fprintf(output_file, " translation %g %g 0.0\n", -dx, dy );
fprintf(output_file, " children [\n" );
/* scaling factor to convert 3D models to board units (decimils)
* Usually we use Wings3D to create thems.
* One can consider the 3D units is 0.1 inch
* So the scaling factor from 0.1 inch to board units
* is 0.1 / board_scaling_factor
*/
double wrml_3D_models_scaling_factor = 0.1 / board_scaling_factor;
/* Preliminary computation: the z value for each layer */
compute_layer_Zs( pcb );
/* Drawing and text on the board, and edges which are special */
export_vrml_drawings( pcb );
/* Export vias and trackage */
export_vrml_tracks( pcb );
/* Export zone fills */
/* TODO export_vrml_zones(pcb);
*/
/* Export footprints */
for( MODULE* module = pcb->m_Modules;
module != 0;
module = module->Next() )
export_vrml_module( pcb, module, output_file,
wrml_3D_models_scaling_factor,
aExport3DFiles, a3D_Subdir );
/* Output the bagged triangles for each layer
* Each layer will be a separate shape */
for( int layer = 0; layer < LAYER_COUNT; layer++ )
write_and_empty_triangle_bag( output_file,
layer_triangles[layer],
pcb->GetLayerColor(layer) );
/* Same thing for the via layers */
for( int i = 0; i < 4; i++ )
write_and_empty_triangle_bag( output_file,
via_triangles[i],
pcb->GetVisibleElementColor( VIAS_VISIBLE + i ) );
/* Close the outer 'transform' node */
fputs( "]\n}\n", output_file );
// End of work
fclose( output_file );
SetLocaleTo_Default(); // revert to the current locale
return true;
}
...@@ -178,6 +178,13 @@ void WinEDA_PcbFrame::ReCreateMenuBar() ...@@ -178,6 +178,13 @@ void WinEDA_PcbFrame::ReCreateMenuBar()
_( "Create a report of all modules on the current board" ) ); _( "Create a report of all modules on the current board" ) );
item->SetBitmap( tools_xpm ); item->SetBitmap( tools_xpm );
submenuexport->Append( item ); submenuexport->Append( item );
item = new wxMenuItem( submenuexport, ID_GEN_EXPORT_FILE_VRML,
_( "&VRML" ),
_( "Export a VRML board representation" ) );
item->SetBitmap( show_3d_xpm );
submenuexport->Append( item );
ADD_MENUITEM_WITH_HELP_AND_SUBMENU( filesMenu, submenuexport, ADD_MENUITEM_WITH_HELP_AND_SUBMENU( filesMenu, submenuexport,
ID_GEN_EXPORT_FILE, _( "&Export" ), ID_GEN_EXPORT_FILE, _( "&Export" ),
_( "Export board" ), export_xpm ); _( "Export board" ), export_xpm );
......
...@@ -94,6 +94,7 @@ BEGIN_EVENT_TABLE( WinEDA_PcbFrame, WinEDA_BasePcbFrame ) ...@@ -94,6 +94,7 @@ BEGIN_EVENT_TABLE( WinEDA_PcbFrame, WinEDA_BasePcbFrame )
EVT_MENU( ID_GEN_EXPORT_FILE_GENCADFORMAT, WinEDA_PcbFrame::ExportToGenCAD ) EVT_MENU( ID_GEN_EXPORT_FILE_GENCADFORMAT, WinEDA_PcbFrame::ExportToGenCAD )
EVT_MENU( ID_GEN_EXPORT_FILE_MODULE_REPORT, EVT_MENU( ID_GEN_EXPORT_FILE_MODULE_REPORT,
WinEDA_PcbFrame::GenModuleReport ) WinEDA_PcbFrame::GenModuleReport )
EVT_MENU( ID_GEN_EXPORT_FILE_VRML, WinEDA_PcbFrame::OnExportVRML )
EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION, EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION,
WinEDA_PcbFrame::ImportSpecctraSession ) WinEDA_PcbFrame::ImportSpecctraSession )
......
...@@ -210,6 +210,7 @@ enum pcbnew_ids ...@@ -210,6 +210,7 @@ enum pcbnew_ids
ID_MENU_LIST_NETS, ID_MENU_LIST_NETS,
ID_MENU_PCB_CLEAN, ID_MENU_PCB_CLEAN,
ID_MENU_PCB_SWAP_LAYERS, ID_MENU_PCB_SWAP_LAYERS,
ID_GEN_EXPORT_FILE_VRML,
ID_TOOLBARH_PCB_AUTOPLACE, ID_TOOLBARH_PCB_AUTOPLACE,
ID_TOOLBARH_PCB_AUTOROUTE, ID_TOOLBARH_PCB_AUTOROUTE,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment