committed by
jean-pierre charras
12 changed files with 2401 additions and 0 deletions
-
6include/wxPcbStruct.h
-
4pcbnew/CMakeLists.txt
-
121pcbnew/dialogs/dialog_export_idf.cpp
-
49pcbnew/dialogs/dialog_export_idf_base.cpp
-
383pcbnew/dialogs/dialog_export_idf_base.fbp
-
52pcbnew/dialogs/dialog_export_idf_base.h
-
357pcbnew/export_idf.cpp
-
968pcbnew/idf.cpp
-
454pcbnew/idf.h
-
5pcbnew/menubar_pcbframe.cpp
-
1pcbnew/pcbframe.cpp
-
1pcbnew/pcbnew_id.h
@ -0,0 +1,121 @@ |
|||
/**
|
|||
* @file dialog_export_idf.cpp |
|||
*/ |
|||
|
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2013 Cirilo Bernardo |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <wxPcbStruct.h>
|
|||
#include <appl_wxstruct.h>
|
|||
#include <pcbnew.h>
|
|||
#include <class_board.h>
|
|||
|
|||
// IDF export header generated by wxFormBuilder
|
|||
#include <dialog_export_idf_base.h>
|
|||
|
|||
#define OPTKEY_IDF_THOU wxT( "IDFExportThou" )
|
|||
|
|||
|
|||
bool Export_IDF3( BOARD *aPcb, const wxString & aFullFileName, double aUseThou ); |
|||
|
|||
|
|||
class DIALOG_EXPORT_IDF3: public DIALOG_EXPORT_IDF3_BASE |
|||
{ |
|||
private: |
|||
PCB_EDIT_FRAME* m_parent; |
|||
wxConfig* m_config; |
|||
bool m_idfThouOpt; // remember last preference for units in THOU
|
|||
|
|||
void OnCancelClick( wxCommandEvent& event ) |
|||
{ |
|||
EndModal( wxID_CANCEL ); |
|||
} |
|||
void OnOkClick( wxCommandEvent& event ) |
|||
{ |
|||
EndModal( wxID_OK ); |
|||
} |
|||
|
|||
public: |
|||
DIALOG_EXPORT_IDF3( PCB_EDIT_FRAME* parent ) : |
|||
DIALOG_EXPORT_IDF3_BASE( parent ) |
|||
{ |
|||
m_parent = parent; |
|||
m_config = wxGetApp().GetSettings(); |
|||
SetFocus(); |
|||
m_idfThouOpt = false; |
|||
m_config->Read( OPTKEY_IDF_THOU, &m_idfThouOpt ); |
|||
m_chkThou->SetValue( m_idfThouOpt ); |
|||
|
|||
GetSizer()->SetSizeHints( this ); |
|||
Centre(); |
|||
} |
|||
|
|||
~DIALOG_EXPORT_IDF3() |
|||
{ |
|||
m_idfThouOpt = m_chkThou->GetValue(); |
|||
m_config->Write( OPTKEY_IDF_THOU, m_idfThouOpt ); |
|||
} |
|||
|
|||
bool GetThouOption() |
|||
{ |
|||
return m_chkThou->GetValue(); |
|||
} |
|||
|
|||
wxFilePickerCtrl* FilePicker() |
|||
{ |
|||
return m_filePickerIDF; |
|||
} |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* Function OnExportIDF3 |
|||
* will export the current BOARD to IDF board and lib files. |
|||
*/ |
|||
void PCB_EDIT_FRAME::ExportToIDF3( wxCommandEvent& event ) |
|||
{ |
|||
wxFileName fn; |
|||
|
|||
// Build default file name
|
|||
fn = GetBoard()->GetFileName(); |
|||
fn.SetExt( wxT( "emn" ) ); |
|||
|
|||
DIALOG_EXPORT_IDF3 dlg( this ); |
|||
dlg.FilePicker()->SetPath( fn.GetFullPath() ); |
|||
|
|||
if ( dlg.ShowModal() != wxID_OK ) |
|||
return; |
|||
|
|||
bool thou = dlg.GetThouOption(); |
|||
|
|||
wxBusyCursor dummy; |
|||
|
|||
wxString fullFilename = dlg.FilePicker()->GetPath(); |
|||
|
|||
if ( !Export_IDF3( GetBoard(), fullFilename, thou ) ) |
|||
{ |
|||
wxString msg = _("Unable to create ") + fullFilename; |
|||
wxMessageBox( msg ); |
|||
return; |
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
///////////////////////////////////////////////////////////////////////////
|
|||
// C++ code generated with wxFormBuilder (version Oct 8 2012)
|
|||
// http://www.wxformbuilder.org/
|
|||
//
|
|||
// PLEASE DO "NOT" EDIT THIS FILE!
|
|||
///////////////////////////////////////////////////////////////////////////
|
|||
|
|||
#include "dialog_export_idf_base.h"
|
|||
|
|||
///////////////////////////////////////////////////////////////////////////
|
|||
|
|||
DIALOG_EXPORT_IDF3_BASE::DIALOG_EXPORT_IDF3_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) |
|||
{ |
|||
this->SetSizeHints( wxDefaultSize, wxDefaultSize ); |
|||
|
|||
wxBoxSizer* bSizerIDFFile; |
|||
bSizerIDFFile = new wxBoxSizer( wxVERTICAL ); |
|||
|
|||
m_txtBrdFile = new wxStaticText( this, wxID_ANY, wxT("IDF Board file"), wxDefaultPosition, wxDefaultSize, 0 ); |
|||
m_txtBrdFile->Wrap( -1 ); |
|||
bSizerIDFFile->Add( m_txtBrdFile, 0, wxALL, 5 ); |
|||
|
|||
m_filePickerIDF = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, wxT("Select a board file"), wxT("*.emn"), wxDefaultPosition, wxDefaultSize, wxFLP_OVERWRITE_PROMPT|wxFLP_SAVE|wxFLP_USE_TEXTCTRL ); |
|||
m_filePickerIDF->SetMinSize( wxSize( 420,30 ) ); |
|||
|
|||
bSizerIDFFile->Add( m_filePickerIDF, 0, wxALL, 5 ); |
|||
|
|||
m_chkThou = new wxCheckBox( this, wxID_ANY, wxT("unit: THOU"), wxDefaultPosition, wxDefaultSize, 0 ); |
|||
bSizerIDFFile->Add( m_chkThou, 0, wxALL, 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(); |
|||
|
|||
bSizerIDFFile->Add( m_sdbSizer1, 1, wxEXPAND, 5 ); |
|||
|
|||
|
|||
this->SetSizer( bSizerIDFFile ); |
|||
this->Layout(); |
|||
|
|||
this->Centre( wxBOTH ); |
|||
} |
|||
|
|||
DIALOG_EXPORT_IDF3_BASE::~DIALOG_EXPORT_IDF3_BASE() |
|||
{ |
|||
} |
@ -0,0 +1,383 @@ |
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> |
|||
<wxFormBuilder_Project> |
|||
<FileVersion major="1" minor="11" /> |
|||
<object class="Project" expanded="1"> |
|||
<property name="class_decoration"></property> |
|||
<property name="code_generation">C++</property> |
|||
<property name="disconnect_events">1</property> |
|||
<property name="disconnect_mode">source_name</property> |
|||
<property name="disconnect_php_events">0</property> |
|||
<property name="disconnect_python_events">0</property> |
|||
<property name="embedded_files_path">res</property> |
|||
<property name="encoding">UTF-8</property> |
|||
<property name="event_generation">connect</property> |
|||
<property name="file">dialog_export_idf_base</property> |
|||
<property name="first_id">1000</property> |
|||
<property name="help_provider">none</property> |
|||
<property name="internationalize">0</property> |
|||
<property name="name">dialog_export_idf3_base</property> |
|||
<property name="namespace"></property> |
|||
<property name="path">.</property> |
|||
<property name="precompiled_header"></property> |
|||
<property name="relative_path">1</property> |
|||
<property name="skip_php_events">1</property> |
|||
<property name="skip_python_events">1</property> |
|||
<property name="use_enum">1</property> |
|||
<property name="use_microsoft_bom">0</property> |
|||
<object class="Dialog" expanded="1"> |
|||
<property name="aui_managed">0</property> |
|||
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property> |
|||
<property name="bg"></property> |
|||
<property name="center">wxBOTH</property> |
|||
<property name="context_help"></property> |
|||
<property name="context_menu">1</property> |
|||
<property name="enabled">1</property> |
|||
<property name="event_handler">impl_virtual</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_IDF3_BASE</property> |
|||
<property name="pos"></property> |
|||
<property name="size">458,177</property> |
|||
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property> |
|||
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property> |
|||
<property name="title">Export IDFv3</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="OnAuiFindManager"></event> |
|||
<event name="OnAuiPaneButton"></event> |
|||
<event name="OnAuiPaneClose"></event> |
|||
<event name="OnAuiPaneMaximize"></event> |
|||
<event name="OnAuiPaneRestore"></event> |
|||
<event name="OnAuiRender"></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"></property> |
|||
<property name="name">bSizerIDFFile</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="wxStaticText" 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="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">IDF Board 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_txtBrdFile</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="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">wxALL</property> |
|||
<property name="proportion">0</property> |
|||
<object class="wxFilePickerCtrl" 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="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="max_size"></property> |
|||
<property name="maximize_button">0</property> |
|||
<property name="maximum_size"></property> |
|||
<property name="message">Select a board file</property> |
|||
<property name="min_size"></property> |
|||
<property name="minimize_button">0</property> |
|||
<property name="minimum_size">420,30</property> |
|||
<property name="moveable">1</property> |
|||
<property name="name">m_filePickerIDF</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">wxFLP_OVERWRITE_PROMPT|wxFLP_SAVE|wxFLP_USE_TEXTCTRL</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="value"></property> |
|||
<property name="wildcard">*.emn</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">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">0</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">unit: THOU</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_chkThou</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">wxEXPAND</property> |
|||
<property name="proportion">1</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"></event> |
|||
<event name="OnContextHelpButtonClick"></event> |
|||
<event name="OnHelpButtonClick"></event> |
|||
<event name="OnNoButtonClick"></event> |
|||
<event name="OnOKButtonClick"></event> |
|||
<event name="OnSaveButtonClick"></event> |
|||
<event name="OnYesButtonClick"></event> |
|||
</object> |
|||
</object> |
|||
</object> |
|||
</object> |
|||
</object> |
|||
</wxFormBuilder_Project> |
@ -0,0 +1,52 @@ |
|||
/////////////////////////////////////////////////////////////////////////// |
|||
// C++ code generated with wxFormBuilder (version Oct 8 2012) |
|||
// http://www.wxformbuilder.org/ |
|||
// |
|||
// PLEASE DO "NOT" EDIT THIS FILE! |
|||
/////////////////////////////////////////////////////////////////////////// |
|||
|
|||
#ifndef __DIALOG_EXPORT_IDF_BASE_H__ |
|||
#define __DIALOG_EXPORT_IDF_BASE_H__ |
|||
|
|||
#include <wx/artprov.h> |
|||
#include <wx/xrc/xmlres.h> |
|||
class DIALOG_SHIM; |
|||
|
|||
#include "dialog_shim.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/checkbox.h> |
|||
#include <wx/sizer.h> |
|||
#include <wx/button.h> |
|||
#include <wx/dialog.h> |
|||
|
|||
/////////////////////////////////////////////////////////////////////////// |
|||
|
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
/// Class DIALOG_EXPORT_IDF3_BASE |
|||
/////////////////////////////////////////////////////////////////////////////// |
|||
class DIALOG_EXPORT_IDF3_BASE : public DIALOG_SHIM |
|||
{ |
|||
private: |
|||
|
|||
protected: |
|||
wxStaticText* m_txtBrdFile; |
|||
wxFilePickerCtrl* m_filePickerIDF; |
|||
wxCheckBox* m_chkThou; |
|||
wxStdDialogButtonSizer* m_sdbSizer1; |
|||
wxButton* m_sdbSizer1OK; |
|||
wxButton* m_sdbSizer1Cancel; |
|||
|
|||
public: |
|||
|
|||
DIALOG_EXPORT_IDF3_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("Export IDFv3"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 458,177 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); |
|||
~DIALOG_EXPORT_IDF3_BASE(); |
|||
|
|||
}; |
|||
|
|||
#endif //__DIALOG_EXPORT_IDF_BASE_H__ |
@ -0,0 +1,357 @@ |
|||
/**
|
|||
* @file export_idf.cpp |
|||
*/ |
|||
|
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2013 Cirilo Bernardo |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
|
|||
#include <list>
|
|||
#include <wxPcbStruct.h>
|
|||
#include <macros.h>
|
|||
#include <pcbnew.h>
|
|||
#include <class_board.h>
|
|||
#include <class_module.h>
|
|||
#include <class_edge_mod.h>
|
|||
#include <idf.h>
|
|||
|
|||
// assumed default graphical line thickness: 10000 IU == 0.1mm
|
|||
#define LINE_WIDTH (100000)
|
|||
|
|||
/**
|
|||
* Function idf_export_outline |
|||
* retrieves line segment information from the edge layer and compiles |
|||
* the data into a form which can be output as an IDFv3 compliant |
|||
* BOARD_OUTLINE section. |
|||
*/ |
|||
static void idf_export_outline( BOARD* aPcb, IDF_BOARD& aIDFBoard ) |
|||
{ |
|||
double scale = aIDFBoard.GetScale(); |
|||
|
|||
DRAWSEGMENT* graphic; // KiCad graphical item
|
|||
IDF_POINT sp, ep; // start and end points from KiCad item
|
|||
|
|||
std::list< IDF_SEGMENT* > lines; // IDF intermediate form of KiCad graphical item
|
|||
IDF_OUTLINE outline; // graphical items forming an outline or cutout
|
|||
|
|||
// NOTE: IMPLEMENTATION
|
|||
// If/when component cutouts are allowed, we must implement them separately. Cutouts
|
|||
// must be added to the board outline section and not to the Other Outline section.
|
|||
// The module cutouts should be handled via the idf_export_module() routine.
|
|||
|
|||
double offX, offY; |
|||
aIDFBoard.GetOffset( offX, offY ); |
|||
|
|||
// Retrieve segments and arcs from the board
|
|||
for( BOARD_ITEM* item = aPcb->m_Drawings; item; item = item->Next() ) |
|||
{ |
|||
if( item->Type() != PCB_LINE_T || item->GetLayer() != EDGE_N ) |
|||
continue; |
|||
|
|||
graphic = (DRAWSEGMENT*) item; |
|||
|
|||
switch( graphic->GetShape() ) |
|||
{ |
|||
case S_SEGMENT: |
|||
{ |
|||
sp.x = graphic->GetStart().x * scale + offX; |
|||
sp.y = -graphic->GetStart().y * scale + offY; |
|||
ep.x = graphic->GetEnd().x * scale + offX; |
|||
ep.y = -graphic->GetEnd().y * scale + offY; |
|||
IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep ); |
|||
|
|||
if( seg ) |
|||
lines.push_back( seg ); |
|||
} |
|||
break; |
|||
|
|||
case S_ARC: |
|||
{ |
|||
sp.x = graphic->GetCenter().x * scale + offX; |
|||
sp.y = -graphic->GetCenter().y * scale + offY; |
|||
ep.x = graphic->GetArcStart().x * scale + offX; |
|||
ep.y = -graphic->GetArcStart().y * scale + offY; |
|||
IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetAngle() / 10.0, true ); |
|||
|
|||
if( seg ) |
|||
lines.push_back( seg ); |
|||
} |
|||
break; |
|||
|
|||
case S_CIRCLE: |
|||
{ |
|||
sp.x = graphic->GetCenter().x * scale + offX; |
|||
sp.y = -graphic->GetCenter().y * scale + offY; |
|||
ep.x = sp.x - graphic->GetRadius() * scale; |
|||
ep.y = sp.y; |
|||
// Circles must always have an angle of +360 deg. to appease
|
|||
// quirky MCAD implementations of IDF.
|
|||
IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, 360.0, true ); |
|||
|
|||
if( seg ) |
|||
lines.push_back( seg ); |
|||
} |
|||
break; |
|||
|
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// if there is no outline then use the bounding box
|
|||
if( lines.empty() ) |
|||
{ |
|||
goto UseBoundingBox; |
|||
} |
|||
|
|||
// get the board outline and write it out
|
|||
// note: we do not use a try/catch block here since we intend
|
|||
// to simply ignore unclosed loops and continue processing
|
|||
// until we're out of segments to process
|
|||
IDF3::GetOutline( lines, outline ); |
|||
|
|||
if( outline.empty() ) |
|||
goto UseBoundingBox; |
|||
|
|||
aIDFBoard.AddOutline( outline ); |
|||
|
|||
// get all cutouts and write them out
|
|||
while( !lines.empty() ) |
|||
{ |
|||
IDF3::GetOutline( lines, outline ); |
|||
|
|||
if( outline.empty() ) |
|||
continue; |
|||
|
|||
aIDFBoard.AddOutline( outline ); |
|||
} |
|||
|
|||
return; |
|||
|
|||
UseBoundingBox: |
|||
|
|||
// clean up if necessary
|
|||
while( !lines.empty() ) |
|||
{ |
|||
delete lines.front(); |
|||
lines.pop_front(); |
|||
} |
|||
|
|||
outline.Clear(); |
|||
|
|||
// fetch a rectangular bounding box for the board;
|
|||
// there is always some uncertainty in the board dimensions
|
|||
// computed via ComputeBoundingBox() since this depends on the
|
|||
// individual module entities.
|
|||
EDA_RECT bbbox = aPcb->ComputeBoundingBox( true ); |
|||
|
|||
// convert to mm and compensate for an assumed LINE_WIDTH line thickness
|
|||
double x = ( bbbox.GetOrigin().x + LINE_WIDTH / 2 ) * scale + offX; |
|||
double y = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale + offY; |
|||
double dx = ( bbbox.GetSize().x - LINE_WIDTH ) * scale; |
|||
double dy = ( bbbox.GetSize().y - LINE_WIDTH ) * scale; |
|||
|
|||
double px[4], py[4]; |
|||
px[0] = x; |
|||
py[0] = y; |
|||
|
|||
px[1] = x; |
|||
py[1] = y + dy; |
|||
|
|||
px[2] = x + dx; |
|||
py[2] = y + dy; |
|||
|
|||
px[3] = x + dx; |
|||
py[3] = y; |
|||
|
|||
IDF_POINT p1, p2; |
|||
|
|||
p1.x = px[3]; |
|||
p1.y = py[3]; |
|||
p2.x = px[0]; |
|||
p2.y = py[0]; |
|||
|
|||
outline.push( new IDF_SEGMENT( p1, p2 ) ); |
|||
|
|||
for( int i = 1; i < 4; ++i ) |
|||
{ |
|||
p1.x = px[i - 1]; |
|||
p1.y = py[i - 1]; |
|||
p2.x = px[i]; |
|||
p2.y = py[i]; |
|||
|
|||
outline.push( new IDF_SEGMENT( p1, p2 ) ); |
|||
} |
|||
|
|||
aIDFBoard.AddOutline( outline ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Function idf_export_module |
|||
* retrieves information from all board modules, adds drill holes to |
|||
* the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, |
|||
* compiles data for the PLACEMENT section and compiles data for |
|||
* the library ELECTRICAL section. |
|||
*/ |
|||
static void idf_export_module( BOARD* aPcb, MODULE* aModule, |
|||
IDF_BOARD& aIDFBoard ) |
|||
{ |
|||
// Reference Designator
|
|||
std::string crefdes = TO_UTF8( aModule->GetReference() ); |
|||
|
|||
if( crefdes.empty() || !crefdes.compare( "~" ) ) |
|||
{ |
|||
std::string cvalue = TO_UTF8( aModule->GetValue() ); |
|||
|
|||
// if both the RefDes and Value are empty or set to '~' the board owns the part,
|
|||
// otherwise associated parts of the module must be marked NOREFDES.
|
|||
if( cvalue.empty() || !cvalue.compare( "~" ) ) |
|||
crefdes = "BOARD"; |
|||
else |
|||
crefdes = "NOREFDES"; |
|||
} |
|||
|
|||
// TODO: If module cutouts are supported we must add code here
|
|||
// for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() )
|
|||
// {
|
|||
// if( ( item->Type() != PCB_MODULE_EDGE_T )
|
|||
// || (item->GetLayer() != EDGE_N ) ) continue;
|
|||
// code to export cutouts
|
|||
// }
|
|||
|
|||
// Export pads
|
|||
double drill, x, y; |
|||
double scale = aIDFBoard.GetScale(); |
|||
IDF3::KEY_PLATING kplate; |
|||
std::string pintype; |
|||
std::string tstr; |
|||
|
|||
double dx, dy; |
|||
|
|||
aIDFBoard.GetOffset( dx, dy ); |
|||
|
|||
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() ) |
|||
{ |
|||
drill = (double) pad->GetDrillSize().x * scale; |
|||
x = pad->GetPosition().x * scale + dx; |
|||
y = -pad->GetPosition().y * scale + dy; |
|||
|
|||
// Export the hole on the edge layer
|
|||
if( drill > 0.0 ) |
|||
{ |
|||
// plating
|
|||
if( pad->GetAttribute() == PAD_HOLE_NOT_PLATED ) |
|||
kplate = IDF3::NPTH; |
|||
else |
|||
kplate = IDF3::PTH; |
|||
|
|||
// hole type
|
|||
tstr = TO_UTF8( pad->GetPadName() ); |
|||
|
|||
if( tstr.empty() || !tstr.compare( "0" ) || !tstr.compare( "~" ) |
|||
|| ( kplate == IDF3::NPTH ) || ( pad->GetDrillShape() == PAD_OVAL ) ) |
|||
pintype = "MTG"; |
|||
else |
|||
pintype = "PIN"; |
|||
|
|||
// fields:
|
|||
// 1. hole dia. : float
|
|||
// 2. X coord : float
|
|||
// 3. Y coord : float
|
|||
// 4. plating : PTH | NPTH
|
|||
// 5. Assoc. part : BOARD | NOREFDES | PANEL | {"refdes"}
|
|||
// 6. type : PIN | VIA | MTG | TOOL | { "other" }
|
|||
// 7. owner : MCAD | ECAD | UNOWNED
|
|||
if( ( pad->GetDrillShape() == PAD_OVAL ) |
|||
&& ( pad->GetDrillSize().x != pad->GetDrillSize().y ) ) |
|||
{ |
|||
// NOTE: IDF does not have direct support for slots;
|
|||
// slots are implemented as a board cutout and we
|
|||
// cannot represent plating or reference designators
|
|||
|
|||
double dlength = pad->GetDrillSize().y * scale; |
|||
|
|||
// NOTE: The orientation of modules and pads have
|
|||
// the opposite sense due to KiCad drawing on a
|
|||
// screen with a LH coordinate system
|
|||
double angle = pad->GetOrientation() / 10.0; |
|||
|
|||
if( dlength < drill ) |
|||
{ |
|||
std::swap( drill, dlength ); |
|||
angle += M_PI2; |
|||
} |
|||
|
|||
// NOTE: KiCad measures a slot's length from end to end
|
|||
// rather than between the centers of the arcs
|
|||
dlength -= drill; |
|||
|
|||
aIDFBoard.AddSlot( drill, dlength, angle, x, y ); |
|||
} |
|||
else |
|||
{ |
|||
aIDFBoard.AddDrill( drill, x, y, kplate, crefdes, pintype, IDF3::ECAD ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// TODO
|
|||
// add to the library item list
|
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Function Export_IDF3 |
|||
* generates IDFv3 compliant board (*.emn) and library (*.emp) |
|||
* files representing the user's PCB design. |
|||
*/ |
|||
bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, double aUseThou ) |
|||
{ |
|||
IDF_BOARD idfBoard; |
|||
|
|||
SetLocaleTo_C_standard(); |
|||
|
|||
// NOTE:
|
|||
// XXX We may enclose all this in a TRY .. CATCH block
|
|||
idfBoard.Setup( aPcb->GetFileName(), aFullFileName, aUseThou, |
|||
aPcb->GetDesignSettings().GetBoardThickness() ); |
|||
|
|||
// set up the global offsets
|
|||
EDA_RECT bbox = aPcb->ComputeBoundingBox( true ); |
|||
idfBoard.SetOffset( bbox.Centre().x * idfBoard.GetScale(), |
|||
bbox.Centre().y * idfBoard.GetScale() ); |
|||
|
|||
// Export the board outline
|
|||
idf_export_outline( aPcb, idfBoard ); |
|||
|
|||
// Output the drill holes and module (library) data.
|
|||
for( MODULE* module = aPcb->m_Modules; module != 0; module = module->Next() ) |
|||
idf_export_module( aPcb, module, idfBoard ); |
|||
|
|||
idfBoard.Finish(); |
|||
|
|||
SetLocaleTo_Default(); |
|||
|
|||
return true; |
|||
} |
@ -0,0 +1,968 @@ |
|||
/**
|
|||
* file: idf.cpp |
|||
* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2013 Cirilo Bernardo |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
// TODO: Consider using different precision formats for THOU vs MM output
|
|||
// Keep in mind that THOU cannot represent MM very well although MM can
|
|||
// represent 1 THOU with 4 decimal places. For modern manufacturing we
|
|||
// are interested in a resolution of about 0.1 THOU.
|
|||
|
|||
#include <list>
|
|||
#include <string>
|
|||
#include <algorithm>
|
|||
#include <cstdio>
|
|||
#include <cmath>
|
|||
#include <ctime>
|
|||
#include <wx/filename.h>
|
|||
#include <macros.h>
|
|||
#include <idf.h>
|
|||
#include <build_version.h>
|
|||
|
|||
// differences in angle smaller than MIN_ANG are considered equal
|
|||
#define MIN_ANG (0.01)
|
|||
// minimum drill diameter (nanometers) - 10000 is a 0.01mm drill
|
|||
#define IDF_MIN_DIA ( 10000.0 )
|
|||
|
|||
// minimum board thickness; this is about 0.012mm (0.5 mils)
|
|||
// which is about the thickness of a single kapton layer typically
|
|||
// used in a flexible design.
|
|||
#define IDF_MIN_BRD_THICKNESS (12000)
|
|||
|
|||
bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius ) |
|||
{ |
|||
double dx = x - aPoint.x; |
|||
double dy = y - aPoint.y; |
|||
|
|||
double d2 = dx * dx + dy * dy; |
|||
|
|||
if( d2 <= aRadius * aRadius ) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const |
|||
{ |
|||
double dx = aPoint.x - x; |
|||
double dy = aPoint.y - y; |
|||
double dist = sqrt( dx * dx + dy * dy ); |
|||
|
|||
return dist; |
|||
} |
|||
|
|||
|
|||
double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) |
|||
{ |
|||
return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x ); |
|||
} |
|||
|
|||
|
|||
double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) |
|||
{ |
|||
double ang = CalcAngleRad( aStartPoint, aEndPoint ); |
|||
|
|||
// round to thousandths of a degree
|
|||
int iang = int (ang / M_PI * 1800000.0); |
|||
|
|||
ang = iang / 10000.0; |
|||
|
|||
return ang; |
|||
} |
|||
|
|||
|
|||
IDF_SEGMENT::IDF_SEGMENT() |
|||
{ |
|||
angle = 0.0; |
|||
offsetAngle = 0.0; |
|||
radius = 0.0; |
|||
} |
|||
|
|||
|
|||
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ) |
|||
{ |
|||
angle = 0.0; |
|||
offsetAngle = 0.0; |
|||
radius = 0.0; |
|||
startPoint = aStartPoint; |
|||
endPoint = aEndPoint; |
|||
} |
|||
|
|||
|
|||
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, |
|||
const IDF_POINT& aEndPoint, |
|||
double aAngle, |
|||
bool aFromKicad ) |
|||
{ |
|||
double diff = abs( aAngle ) - 360.0; |
|||
|
|||
if( ( diff < MIN_ANG |
|||
&& diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) ) |
|||
{ |
|||
angle = 0.0; |
|||
startPoint = aStartPoint; |
|||
endPoint = aEndPoint; |
|||
|
|||
if( diff < MIN_ANG && diff > -MIN_ANG ) |
|||
{ |
|||
angle = 360.0; |
|||
center = aStartPoint; |
|||
offsetAngle = 0.0; |
|||
radius = aStartPoint.CalcDistance( aEndPoint ); |
|||
} |
|||
else if( aAngle < MIN_ANG && aAngle > -MIN_ANG ) |
|||
{ |
|||
CalcCenterAndRadius(); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// we need to convert from the KiCad arc convention
|
|||
angle = aAngle; |
|||
|
|||
center = aStartPoint; |
|||
|
|||
offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint ); |
|||
|
|||
radius = aStartPoint.CalcDistance( aEndPoint ); |
|||
|
|||
startPoint = aEndPoint; |
|||
|
|||
double ang = offsetAngle + aAngle; |
|||
ang = (ang / 180.0) * M_PI; |
|||
|
|||
endPoint.x = ( radius * cos( ang ) ) + center.x; |
|||
endPoint.y = ( radius * sin( ang ) ) + center.y; |
|||
} |
|||
|
|||
|
|||
bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius ) |
|||
{ |
|||
return startPoint.Matches( aPoint, aRadius ); |
|||
} |
|||
|
|||
|
|||
bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius ) |
|||
{ |
|||
return endPoint.Matches( aPoint, aRadius ); |
|||
} |
|||
|
|||
|
|||
void IDF_SEGMENT::CalcCenterAndRadius( void ) |
|||
{ |
|||
// NOTE: this routine does not check if the points are the same
|
|||
// or too close to be sensible in a production setting.
|
|||
|
|||
double offAng = IDF3::CalcAngleRad( startPoint, endPoint ); |
|||
double d = startPoint.CalcDistance( endPoint ) / 2.0; |
|||
double xm = ( startPoint.x + endPoint.x ) * 0.5; |
|||
double ym = ( startPoint.y + endPoint.y ) * 0.5; |
|||
|
|||
radius = d / sin( angle * M_PI / 180.0 ); |
|||
|
|||
if( radius < 0.0 ) |
|||
{ |
|||
radius = -radius; |
|||
} |
|||
|
|||
// calculate the height of the triangle with base d and hypotenuse r
|
|||
double dh2 = radius * radius - d * d; |
|||
|
|||
if( dh2 < 0 ) |
|||
{ |
|||
// this should only ever happen due to rounding errors when r == d
|
|||
dh2 = 0; |
|||
} |
|||
|
|||
double h = sqrt( dh2 ); |
|||
|
|||
if( angle > 0.0 ) |
|||
offAng += M_PI2; |
|||
else |
|||
offAng -= M_PI2; |
|||
|
|||
if( ( angle > M_PI ) || ( angle < -M_PI ) ) |
|||
offAng += M_PI; |
|||
|
|||
center.x = h * cos( offAng ) + xm; |
|||
center.y = h * sin( offAng ) + ym; |
|||
|
|||
offsetAngle = IDF3::CalcAngleDeg( center, startPoint ); |
|||
} |
|||
|
|||
|
|||
bool IDF_SEGMENT::IsCircle( void ) |
|||
{ |
|||
double diff = abs( angle ) - 360.0; |
|||
|
|||
if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) ) |
|||
return true; |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
double IDF_SEGMENT::GetMinX( void ) |
|||
{ |
|||
if( angle == 0.0 ) |
|||
return std::min( startPoint.x, endPoint.x ); |
|||
|
|||
// Calculate the leftmost point of the circle or arc
|
|||
|
|||
if( IsCircle() ) |
|||
{ |
|||
// if only everything were this easy
|
|||
return center.x - radius; |
|||
} |
|||
|
|||
// cases:
|
|||
// 1. CCW arc: if offset + included angle >= 180 deg then
|
|||
// MinX = center.x - radius, otherwise MinX is the
|
|||
// same as for the case of a line.
|
|||
// 2. CW arc: if offset + included angle <= -180 deg then
|
|||
// MinX = center.x - radius, otherwise MinX is the
|
|||
// same as for the case of a line.
|
|||
|
|||
if( angle > 0 ) |
|||
{ |
|||
// CCW case
|
|||
if( ( offsetAngle + angle ) >= 180.0 ) |
|||
{ |
|||
return center.x - radius; |
|||
} |
|||
else |
|||
{ |
|||
return std::min( startPoint.x, endPoint.x ); |
|||
} |
|||
} |
|||
|
|||
// CW case
|
|||
if( ( offsetAngle + angle ) <= -180.0 ) |
|||
{ |
|||
return center.x - radius; |
|||
} |
|||
|
|||
return std::min( startPoint.x, endPoint.x ); |
|||
} |
|||
|
|||
|
|||
void IDF_SEGMENT::SwapEnds( void ) |
|||
{ |
|||
if( IsCircle() ) |
|||
{ |
|||
// reverse the direction
|
|||
angle = -angle; |
|||
return; |
|||
} |
|||
|
|||
IDF_POINT tmp = startPoint; |
|||
startPoint = endPoint; |
|||
endPoint = tmp; |
|||
|
|||
if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) ) |
|||
return; // nothing more to do
|
|||
|
|||
// change the direction of the arc
|
|||
angle = -angle; |
|||
// calculate the new offset angle
|
|||
offsetAngle = IDF3::CalcAngleDeg( center, startPoint ); |
|||
} |
|||
|
|||
|
|||
IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY, |
|||
IDF3::KEY_PLATING aPlating, |
|||
const std::string aRefDes, |
|||
const std::string aHoleType, |
|||
IDF3::KEY_OWNER aOwner ) |
|||
{ |
|||
if( aDrillDia < 0.3 ) |
|||
dia = 0.3; |
|||
else |
|||
dia = aDrillDia; |
|||
|
|||
x = aPosX; |
|||
y = aPosY; |
|||
plating = aPlating; |
|||
|
|||
if( !aRefDes.compare( "BOARD" ) ) |
|||
{ |
|||
kref = IDF3::BOARD; |
|||
} |
|||
else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) ) |
|||
{ |
|||
kref = IDF3::NOREFDES; |
|||
} |
|||
else if( !aRefDes.compare( "PANEL" ) ) |
|||
{ |
|||
kref = IDF3::PANEL; |
|||
} |
|||
else |
|||
{ |
|||
kref = IDF3::REFDES; |
|||
refdes = aRefDes; |
|||
} |
|||
|
|||
if( !aHoleType.compare( "PIN" ) ) |
|||
{ |
|||
khole = IDF3::PIN; |
|||
} |
|||
else if( !aHoleType.compare( "VIA" ) ) |
|||
{ |
|||
khole = IDF3::VIA; |
|||
} |
|||
else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) ) |
|||
{ |
|||
khole = IDF3::MTG; |
|||
} |
|||
else if( !aHoleType.compare( "TOOL" ) ) |
|||
{ |
|||
khole = IDF3::TOOL; |
|||
} |
|||
else |
|||
{ |
|||
khole = IDF3::OTHER; |
|||
holetype = aHoleType; |
|||
} |
|||
|
|||
owner = aOwner; |
|||
} // IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
|
|||
|
|||
|
|||
bool IDF_DRILL_DATA::Write( FILE* aLayoutFile ) |
|||
{ |
|||
// TODO: check stream integrity and return 'false' as appropriate
|
|||
|
|||
if( !aLayoutFile ) |
|||
return false; |
|||
|
|||
std::string holestr; |
|||
std::string refstr; |
|||
std::string ownstr; |
|||
std::string pltstr; |
|||
|
|||
switch( khole ) |
|||
{ |
|||
case IDF3::PIN: |
|||
holestr = "PIN"; |
|||
break; |
|||
|
|||
case IDF3::VIA: |
|||
holestr = "VIA"; |
|||
break; |
|||
|
|||
case IDF3::TOOL: |
|||
holestr = "TOOL"; |
|||
break; |
|||
|
|||
case IDF3::OTHER: |
|||
holestr = "\"" + holetype + "\""; |
|||
break; |
|||
|
|||
default: |
|||
holestr = "MTG"; |
|||
break; |
|||
} |
|||
|
|||
switch( kref ) |
|||
{ |
|||
case IDF3::BOARD: |
|||
refstr = "BOARD"; |
|||
break; |
|||
|
|||
case IDF3::PANEL: |
|||
refstr = "PANEL"; |
|||
break; |
|||
|
|||
case IDF3::REFDES: |
|||
refstr = "\"" + refdes + "\""; |
|||
break; |
|||
|
|||
default: |
|||
refstr = "NOREFDES"; |
|||
break; |
|||
} |
|||
|
|||
if( plating == IDF3::PTH ) |
|||
pltstr = "PTH"; |
|||
else |
|||
pltstr = "NPTH"; |
|||
|
|||
switch( owner ) |
|||
{ |
|||
case IDF3::MCAD: |
|||
ownstr = "MCAD"; |
|||
break; |
|||
|
|||
case IDF3::ECAD: |
|||
ownstr = "ECAD"; |
|||
break; |
|||
|
|||
default: |
|||
ownstr = "UNOWNED"; |
|||
} |
|||
|
|||
fprintf( aLayoutFile, "%.3f %.5f %.5f %s %s %s %s\n", |
|||
dia, x, y, pltstr.c_str(), refstr.c_str(), holestr.c_str(), ownstr.c_str() ); |
|||
|
|||
return true; |
|||
} // IDF_DRILL_DATA::Write( aLayoutFile )
|
|||
|
|||
|
|||
IDF_BOARD::IDF_BOARD() |
|||
{ |
|||
outlineIndex = 0; |
|||
scale = 1e-6; |
|||
boardThickness = 1.6; // default to 1.6mm thick boards
|
|||
|
|||
useThou = false; // by default we want mm output
|
|||
hasBrdOutlineHdr = false; |
|||
|
|||
layoutFile = NULL; |
|||
libFile = NULL; |
|||
} |
|||
|
|||
|
|||
IDF_BOARD::~IDF_BOARD() |
|||
{ |
|||
Finish(); |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::Setup( wxString aBoardName, |
|||
wxString aFullFileName, |
|||
bool aUseThou, |
|||
int aBoardThickness ) |
|||
{ |
|||
if( aBoardThickness < IDF_MIN_BRD_THICKNESS ) |
|||
return false; |
|||
|
|||
if( aUseThou ) |
|||
{ |
|||
useThou = true; |
|||
scale = 1e-3 / 25.4; |
|||
} |
|||
else |
|||
{ |
|||
useThou = false; |
|||
scale = 1e-6; |
|||
} |
|||
|
|||
boardThickness = aBoardThickness * scale; |
|||
|
|||
wxFileName brdname( aBoardName ); |
|||
wxFileName idfname( aFullFileName ); |
|||
|
|||
// open the layout file
|
|||
idfname.SetExt( wxT( "emn" ) ); |
|||
layoutFile = wxFopen( aFullFileName, wxT( "wt" ) ); |
|||
|
|||
if( layoutFile == NULL ) |
|||
return false; |
|||
|
|||
// open the library file
|
|||
idfname.SetExt( wxT( "emp" ) ); |
|||
libFile = wxFopen( idfname.GetFullPath(), wxT( "wt" ) ); |
|||
|
|||
if( libFile == NULL ) |
|||
{ |
|||
fclose( layoutFile ); |
|||
layoutFile = NULL; |
|||
return false; |
|||
} |
|||
|
|||
|
|||
time_t date; |
|||
time( &date ); |
|||
struct tm tdate; |
|||
|
|||
time( &date ); |
|||
localtime_r( &date, &tdate ); |
|||
|
|||
fprintf( layoutFile, ".HEADER\n" |
|||
"BOARD_FILE 3.0 \"Created by KiCad %s\"" |
|||
" %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" |
|||
"\"%s\" %s\n" |
|||
".END_HEADER\n\n", |
|||
TO_UTF8( GetBuildVersion() ), |
|||
tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, |
|||
tdate.tm_hour, tdate.tm_min, tdate.tm_sec, |
|||
TO_UTF8( brdname.GetFullName() ), useThou ? "THOU" : "MM" ); |
|||
|
|||
fprintf( libFile, ".HEADER\n" |
|||
"BOARD_FILE 3.0 \"Created by KiCad %s\" %.4d/%.2d/%.2d.%.2d:%.2d:%.2d 1\n" |
|||
".END_HEADER\n\n", |
|||
TO_UTF8( GetBuildVersion() ), |
|||
tdate.tm_year + 1900, tdate.tm_mon + 1, tdate.tm_mday, |
|||
tdate.tm_hour, tdate.tm_min, tdate.tm_sec ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::Finish( void ) |
|||
{ |
|||
// Steps to finalize the board and library files:
|
|||
// 1. (emp) finalize the library file
|
|||
// 2. (emn) close the BOARD_OUTLINE section
|
|||
// 3. (emn) write out the DRILLED_HOLES section
|
|||
// 4. (emn) write out the COMPONENT_PLACEMENT section
|
|||
|
|||
// TODO:
|
|||
// idfLib.Finish();
|
|||
if( libFile != NULL ) |
|||
{ |
|||
fclose( libFile ); |
|||
libFile = NULL; |
|||
} |
|||
|
|||
if( layoutFile == NULL ) |
|||
return false; |
|||
|
|||
// Finalize the board outline section
|
|||
fprintf( layoutFile, ".END_BOARD_OUTLINE\n\n" ); |
|||
|
|||
// Write out the drill section
|
|||
if( WriteDrills() ) |
|||
{ |
|||
fclose( layoutFile ); |
|||
layoutFile = NULL; |
|||
return false; |
|||
} |
|||
|
|||
// TODO: Write out the component placement section
|
|||
// IDF3::export_placement();
|
|||
|
|||
fclose( layoutFile ); |
|||
layoutFile = NULL; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::AddOutline( IDF_OUTLINE& aOutline ) |
|||
{ |
|||
if( !layoutFile ) |
|||
return false; |
|||
|
|||
// TODO: check the stream integrity
|
|||
|
|||
std::list<IDF_SEGMENT*>::iterator bo; |
|||
std::list<IDF_SEGMENT*>::iterator eo; |
|||
|
|||
if( !hasBrdOutlineHdr ) |
|||
{ |
|||
fprintf( layoutFile, ".BOARD_OUTLINE ECAD\n%.5f\n", boardThickness ); |
|||
hasBrdOutlineHdr = true; |
|||
} |
|||
|
|||
if( aOutline.size() == 1 ) |
|||
{ |
|||
if( !aOutline.front()->IsCircle() ) |
|||
return false; // this is a bad outline
|
|||
|
|||
// NOTE: a circle always has an angle of 360, never -360,
|
|||
// otherwise SolidWorks chokes on the file.
|
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
aOutline.front()->startPoint.x, aOutline.front()->startPoint.y ); |
|||
fprintf( layoutFile, "%d %.5f %.5f 360\n", outlineIndex, |
|||
aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); |
|||
|
|||
++outlineIndex; |
|||
return true; |
|||
} |
|||
|
|||
// ensure that the very last point is the same as the very first point
|
|||
aOutline.back()-> endPoint = aOutline.front()->startPoint; |
|||
|
|||
// check if we must reverse things
|
|||
if( ( aOutline.IsCCW() && ( outlineIndex > 0 ) ) |
|||
|| ( ( !aOutline.IsCCW() ) && ( outlineIndex == 0 ) ) ) |
|||
{ |
|||
eo = aOutline.begin(); |
|||
bo = aOutline.end(); |
|||
--bo; |
|||
|
|||
// for the first item we write out both points
|
|||
if( aOutline.front()->angle < MIN_ANG && aOutline.front()->angle > -MIN_ANG ) |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
aOutline.front()->startPoint.x, aOutline.front()->startPoint.y ); |
|||
} |
|||
else |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
aOutline.front()->endPoint.x, aOutline.front()->endPoint.y ); |
|||
fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, |
|||
aOutline.front()->startPoint.x, aOutline.front()->startPoint.y, |
|||
-aOutline.front()->angle ); |
|||
} |
|||
|
|||
// for all other segments we only write out the start point
|
|||
while( bo != eo ) |
|||
{ |
|||
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
(*bo)->startPoint.x, (*bo)->startPoint.y ); |
|||
} |
|||
else |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, |
|||
(*bo)->startPoint.x, (*bo)->startPoint.y, -(*bo)->angle ); |
|||
} |
|||
|
|||
--bo; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
bo = aOutline.begin(); |
|||
eo = aOutline.end(); |
|||
|
|||
// for the first item we write out both points
|
|||
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
(*bo)->startPoint.x, (*bo)->startPoint.y ); |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
(*bo)->endPoint.x, (*bo)->endPoint.y ); |
|||
} |
|||
else |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
(*bo)->startPoint.x, (*bo)->startPoint.y ); |
|||
fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, |
|||
(*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle ); |
|||
} |
|||
|
|||
++bo; |
|||
|
|||
// for all other segments we only write out the last point
|
|||
while( bo != eo ) |
|||
{ |
|||
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG ) |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f 0\n", outlineIndex, |
|||
(*bo)->endPoint.x, (*bo)->endPoint.y ); |
|||
} |
|||
else |
|||
{ |
|||
fprintf( layoutFile, "%d %.5f %.5f %.5f\n", outlineIndex, |
|||
(*bo)->endPoint.x, (*bo)->endPoint.y, (*bo)->angle ); |
|||
} |
|||
|
|||
++bo; |
|||
} |
|||
} |
|||
|
|||
++outlineIndex; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::AddDrill( double dia, double x, double y, |
|||
IDF3::KEY_PLATING plating, |
|||
const std::string refdes, |
|||
const std::string holeType, |
|||
IDF3::KEY_OWNER owner ) |
|||
{ |
|||
if( dia < IDF_MIN_DIA * scale ) |
|||
return false; |
|||
|
|||
IDF_DRILL_DATA* dp = new IDF_DRILL_DATA( dia, x, y, plating, refdes, holeType, owner ); |
|||
drills.push_back( dp ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::AddSlot( double aWidth, double aLength, double aOrientation, |
|||
double aX, double aY ) |
|||
{ |
|||
if( aWidth < IDF_MIN_DIA * scale ) |
|||
return false; |
|||
|
|||
if( aLength < IDF_MIN_DIA * scale ) |
|||
return false; |
|||
|
|||
IDF_POINT c[2]; // centers
|
|||
IDF_POINT pt[4]; |
|||
|
|||
double a1 = aOrientation / 180.0 * M_PI; |
|||
double a2 = a1 + M_PI2; |
|||
double d1 = aLength / 2.0; |
|||
double d2 = aWidth / 2.0; |
|||
double sa1 = sin( a1 ); |
|||
double ca1 = cos( a1 ); |
|||
double dsa2 = d2 * sin( a2 ); |
|||
double dca2 = d2 * cos( a2 ); |
|||
|
|||
c[0].x = aX + d1 * ca1; |
|||
c[0].y = aY + d1 * sa1; |
|||
|
|||
c[1].x = aX - d1 * ca1; |
|||
c[1].y = aY - d1 * sa1; |
|||
|
|||
pt[0].x = c[0].x - dca2; |
|||
pt[0].y = c[0].y - dsa2; |
|||
|
|||
pt[1].x = c[1].x - dca2; |
|||
pt[1].y = c[1].y - dsa2; |
|||
|
|||
pt[2].x = c[1].x + dca2; |
|||
pt[2].y = c[1].y + dsa2; |
|||
|
|||
pt[3].x = c[0].x + dca2; |
|||
pt[3].y = c[0].y + dsa2; |
|||
|
|||
IDF_OUTLINE outline; |
|||
|
|||
// first straight run
|
|||
IDF_SEGMENT* seg = new IDF_SEGMENT( pt[0], pt[1] ); |
|||
outline.push( seg ); |
|||
// first 180 degree cap
|
|||
seg = new IDF_SEGMENT( c[1], pt[1], -180.0, true ); |
|||
outline.push( seg ); |
|||
// final straight run
|
|||
seg = new IDF_SEGMENT( pt[2], pt[3] ); |
|||
outline.push( seg ); |
|||
// final 180 degree cap
|
|||
seg = new IDF_SEGMENT( c[0], pt[3], -180.0, true ); |
|||
outline.push( seg ); |
|||
|
|||
return AddOutline( outline ); |
|||
} |
|||
|
|||
|
|||
bool IDF_BOARD::WriteDrills( void ) |
|||
{ |
|||
if( !layoutFile ) |
|||
return false; |
|||
|
|||
// TODO: check the stream integrity and return false as appropriate
|
|||
if( drills.empty() ) |
|||
return true; |
|||
|
|||
fprintf( layoutFile, ".DRILLED_HOLES\n" ); |
|||
|
|||
std::list<struct IDF_DRILL_DATA*>::iterator ds = drills.begin(); |
|||
std::list<struct IDF_DRILL_DATA*>::iterator de = drills.end(); |
|||
|
|||
while( ds != de ) |
|||
{ |
|||
if( !(*ds)->Write( layoutFile ) ) |
|||
return false; |
|||
|
|||
++ds; |
|||
} |
|||
|
|||
fprintf( layoutFile, ".END_DRILLED_HOLES\n" ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
double IDF_BOARD::GetScale( void ) |
|||
{ |
|||
return scale; |
|||
} |
|||
|
|||
|
|||
void IDF_BOARD::SetOffset( double x, double y ) |
|||
{ |
|||
offsetX = x; |
|||
offsetY = y; |
|||
} |
|||
|
|||
|
|||
void IDF_BOARD::GetOffset( double& x, double& y ) |
|||
{ |
|||
x = offsetX; |
|||
y = offsetY; |
|||
} |
|||
|
|||
|
|||
void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines, |
|||
IDF_OUTLINE& aOutline ) |
|||
{ |
|||
aOutline.Clear(); |
|||
|
|||
// NOTE: To tell if the point order is CCW or CW,
|
|||
// sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
|
|||
// If the result is >0, the direction is CW, otherwise
|
|||
// it is CCW. Note that the result cannot be 0 unless
|
|||
// we have a bounded area of 0.
|
|||
|
|||
// First we find the segment with the leftmost point
|
|||
std::list<IDF_SEGMENT*>::iterator bl = aLines.begin(); |
|||
std::list<IDF_SEGMENT*>::iterator el = aLines.end(); |
|||
std::list<IDF_SEGMENT*>::iterator idx = bl++; // iterator for the object with minX
|
|||
|
|||
double minx = (*idx)->GetMinX(); |
|||
double curx; |
|||
|
|||
while( bl != el ) |
|||
{ |
|||
curx = (*bl)->GetMinX(); |
|||
|
|||
if( curx < minx ) |
|||
{ |
|||
minx = curx; |
|||
idx = bl; |
|||
} |
|||
|
|||
++bl; |
|||
} |
|||
|
|||
aOutline.push( *idx ); |
|||
aLines.erase( idx ); |
|||
|
|||
// If the item is a circle then we're done
|
|||
if( aOutline.front()->IsCircle() ) |
|||
return; |
|||
|
|||
// Assemble the loop
|
|||
bool complete = false; // set if loop is complete
|
|||
bool matched; // set if a segment's end point was matched
|
|||
|
|||
while( !complete ) |
|||
{ |
|||
matched = false; |
|||
bl = aLines.begin(); |
|||
el = aLines.end(); |
|||
|
|||
while( bl != el && !matched ) |
|||
{ |
|||
if( (*bl)->MatchesStart( aOutline.back()->endPoint ) ) |
|||
{ |
|||
if( (*bl)->IsCircle() ) |
|||
{ |
|||
// a circle on the perimeter is pathological but we just ignore it
|
|||
++bl; |
|||
} |
|||
else |
|||
{ |
|||
matched = true; |
|||
aOutline.push( *bl ); |
|||
aLines.erase( bl ); |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
++bl; |
|||
} |
|||
|
|||
if( !matched ) |
|||
{ |
|||
// attempt to match the end points
|
|||
bl = aLines.begin(); |
|||
el = aLines.end(); |
|||
|
|||
while( bl != el && !matched ) |
|||
{ |
|||
if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) ) |
|||
{ |
|||
if( (*bl)->IsCircle() ) |
|||
{ |
|||
// a circle on the perimeter is pathological but we just ignore it
|
|||
++bl; |
|||
} |
|||
else |
|||
{ |
|||
matched = true; |
|||
(*bl)->SwapEnds(); |
|||
aOutline.push( *bl ); |
|||
aLines.erase( bl ); |
|||
} |
|||
|
|||
continue; |
|||
} |
|||
|
|||
++bl; |
|||
} |
|||
} |
|||
|
|||
if( !matched ) |
|||
{ |
|||
// still no match - attempt to close the loop
|
|||
if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG ) |
|||
|| ( aOutline.front()->angle > MIN_ANG ) ) |
|||
{ |
|||
// close the loop
|
|||
IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint, |
|||
aOutline.front()->startPoint ); |
|||
|
|||
if( seg ) |
|||
{ |
|||
complete = true; |
|||
aOutline.push( seg ); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
// the outline is bad; drop the segments
|
|||
aOutline.Clear(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// check if the loop is complete
|
|||
if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) ) |
|||
{ |
|||
complete = true; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
bool IDF_LIB::WriteLib( FILE* aLibFile ) |
|||
{ |
|||
if( !aLibFile ) |
|||
return false; |
|||
|
|||
// TODO: check stream integrity and return false as appropriate
|
|||
|
|||
// TODO: export models
|
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool IDF_LIB::WriteBrd( FILE* aLayoutFile ) |
|||
{ |
|||
if( !aLayoutFile ) |
|||
return false; |
|||
|
|||
// TODO: check stream integrity and return false as appropriate
|
|||
|
|||
// TODO: write out the board placement information
|
|||
|
|||
return true; |
|||
} |
@ -0,0 +1,454 @@ |
|||
/** |
|||
* @file idf.h |
|||
*/ |
|||
|
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2013 Cirilo Bernardo |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
|||
* or you may search the http://www.gnu.org website for the version 2 license, |
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#ifndef IDF_H |
|||
#define IDF_H |
|||
|
|||
#include <wx/string.h> |
|||
|
|||
#ifndef M_PI |
|||
#define M_PI 3.1415926535897932384626433832795028841 |
|||
#endif |
|||
|
|||
#ifndef M_PI2 |
|||
#define M_PI2 ( M_PI / 2.0 ) |
|||
#endif |
|||
|
|||
#ifndef M_PI4 |
|||
#define M_PI4 ( M_PI / 4.0 ) |
|||
#endif |
|||
|
|||
class IDF_POINT; |
|||
class IDF_SEGMENT; |
|||
class IDF_DRILL_DATA; |
|||
class IDF_OUTLINE; |
|||
|
|||
namespace IDF3 { |
|||
enum KEY_OWNER |
|||
{ |
|||
UNOWNED = 0, // < either MCAD or ECAD may modify a feature |
|||
MCAD, // < only MCAD may modify a feature |
|||
ECAD // < only ECAD may modify a feature |
|||
}; |
|||
|
|||
enum KEY_HOLETYPE |
|||
{ |
|||
PIN = 0, // < drill hole is for a pin |
|||
VIA, // < drill hole is for a via |
|||
MTG, // < drill hole is for mounting |
|||
TOOL, // < drill hole is for tooling |
|||
OTHER // < user has specified a custom type |
|||
}; |
|||
|
|||
enum KEY_PLATING |
|||
{ |
|||
PTH = 0, // < Plate-Through Hole |
|||
NPTH // < Non-Plate-Through Hole |
|||
}; |
|||
|
|||
enum KEY_REFDES |
|||
{ |
|||
BOARD = 0, // < feature is associated with the board |
|||
NOREFDES, // < feature is associated with a component with no RefDes |
|||
PANEL, // < feature is associated with an IDF panel |
|||
REFDES // < reference designator as assigned by the CAD software |
|||
}; |
|||
|
|||
// calculate the angle between the horizon and the segment aStartPoint to aEndPoint |
|||
double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); |
|||
double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); |
|||
|
|||
// take contiguous elements from 'lines' and stuff them into 'outline' |
|||
void GetOutline( std::list<IDF_SEGMENT*>& aLines, |
|||
IDF_OUTLINE& aOutline ); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_POINT |
|||
* represents a point |
|||
*/ |
|||
class IDF_POINT |
|||
{ |
|||
public: |
|||
double x; // < X coordinate |
|||
double y; // < Y coordinate |
|||
|
|||
IDF_POINT() |
|||
{ |
|||
x = 0.0; |
|||
y = 0.0; |
|||
} |
|||
|
|||
/** |
|||
* Function Matches() |
|||
* returns true if the given coordinate point is within the given radius |
|||
* of the point. |
|||
* @param aPoint : coordinates of the point being compared |
|||
* @param aRadius : radius within which the points are considered the same |
|||
*/ |
|||
bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 ); |
|||
double CalcDistance( const IDF_POINT& aPoint ) const; |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_SEGMENT |
|||
* represents a geometry segment as used in IDFv3 outlines |
|||
*/ |
|||
class IDF_SEGMENT |
|||
{ |
|||
private: |
|||
/** |
|||
* Function CalcCenterAndRadius() |
|||
* Calculates the center, radius, and angle between center and start point given the |
|||
* IDF compliant points and included angle. |
|||
* @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3 |
|||
*/ |
|||
void CalcCenterAndRadius( void ); |
|||
|
|||
public: |
|||
IDF_POINT startPoint; // starting point in IDF coordinates |
|||
IDF_POINT endPoint; // end point in IDF coordinates |
|||
IDF_POINT center; // center of an arc or circle; used primarily for calculating min X |
|||
double angle; // included angle (degrees) according to IDFv3 specification |
|||
double offsetAngle; // angle between center and start of arc; used to speed up some calcs. |
|||
double radius; // radius of the arc or circle; used to speed up some calcs. |
|||
|
|||
/** |
|||
* Function IDF_SEGMENT() |
|||
* initializes the internal variables |
|||
*/ |
|||
IDF_SEGMENT(); |
|||
|
|||
/** |
|||
* Function IDF_SEGMENT( start, end ) |
|||
* creates a straight segment |
|||
*/ |
|||
IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint ); |
|||
|
|||
/** |
|||
* Function IDF_SEGMENT( start, end ) |
|||
* creates a straight segment, arc, or circle depending on the angle |
|||
* @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention) |
|||
* @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention) |
|||
* @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention |
|||
* @param fromKicad : set true if we need to convert from KiCad to IDF convention |
|||
*/ |
|||
IDF_SEGMENT( const IDF_POINT& aStartPoint, |
|||
const IDF_POINT& aEndPoint, |
|||
double aAngle, |
|||
bool aFromKicad ); |
|||
|
|||
/** |
|||
* Function MatchesStart() |
|||
* returns true if the given coordinate is within a radius 'rad' |
|||
* of the start point. |
|||
* @param aPoint : coordinates of the point being compared |
|||
* @param aRadius : radius within which the points are considered the same |
|||
*/ |
|||
bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 ); |
|||
|
|||
/** |
|||
* Function MatchesEnd() |
|||
* returns true if the given coordinate is within a radius 'rad' |
|||
* of the end point. |
|||
* @param aPoint : coordinates of the point being compared |
|||
* @param aRadius : radius within which the points are considered the same |
|||
*/ |
|||
bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 ); |
|||
|
|||
/** |
|||
* Function IsCircle() |
|||
* returns true if this segment is a circle |
|||
*/ |
|||
bool IsCircle( void ); |
|||
|
|||
/** |
|||
* Function GetMinX() |
|||
* returns the minimum X coordinate of this segment |
|||
*/ |
|||
double GetMinX( void ); |
|||
|
|||
/** |
|||
* Function SwapEnds() |
|||
* Swaps the start and end points and alters internal |
|||
* variables as necessary for arcs |
|||
*/ |
|||
void SwapEnds( void ); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_OUTLINE |
|||
* contains segment and winding information for an IDF outline |
|||
*/ |
|||
class IDF_OUTLINE |
|||
{ |
|||
private: |
|||
double dir; |
|||
std::list<IDF_SEGMENT*> outline; |
|||
|
|||
public: |
|||
IDF_OUTLINE() { dir = 0.0; } |
|||
~IDF_OUTLINE() { Clear(); } |
|||
|
|||
// returns true if the current list of points represents a counterclockwise winding |
|||
bool IsCCW( void ) |
|||
{ |
|||
if( dir > 0.0 ) |
|||
return false; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// clears the internal list of outline segments |
|||
void Clear( void ) |
|||
{ |
|||
dir = 0.0; |
|||
|
|||
while( !outline.empty() ) |
|||
{ |
|||
delete outline.front(); |
|||
outline.pop_front(); |
|||
} |
|||
} |
|||
|
|||
// returns the size of the internal segment list |
|||
size_t size( void ) |
|||
{ |
|||
return outline.size(); |
|||
} |
|||
|
|||
// returns true if the internal segment list is empty |
|||
bool empty( void ) |
|||
{ |
|||
return outline.empty(); |
|||
} |
|||
|
|||
// return the front() of the internal segment list |
|||
IDF_SEGMENT*& front( void ) |
|||
{ |
|||
return outline.front(); |
|||
} |
|||
|
|||
// return the back() of the internal segment list |
|||
IDF_SEGMENT*& back( void ) |
|||
{ |
|||
return outline.back(); |
|||
} |
|||
|
|||
// return the begin() iterator of the internal segment list |
|||
std::list<IDF_SEGMENT*>::iterator begin( void ) |
|||
{ |
|||
return outline.begin(); |
|||
} |
|||
|
|||
// return the end() iterator of the internal segment list |
|||
std::list<IDF_SEGMENT*>::iterator end( void ) |
|||
{ |
|||
return outline.end(); |
|||
} |
|||
|
|||
// push a segment onto the internal list |
|||
void push( IDF_SEGMENT* item ) |
|||
{ |
|||
// XXX - check that startPoint[N] == endPoint[N -1], otherwise THROW |
|||
// XXX - a Circle must stand alone; if we add to a circle or add a |
|||
// circle to an existing list, we should throw an exception. |
|||
outline.push_back( item ); |
|||
dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x ) |
|||
* ( outline.back()->endPoint.y + outline.back()->startPoint.y ); |
|||
} |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_BOARD |
|||
* contains objects necessary for the maintenance of the IDF board and library files. |
|||
*/ |
|||
class IDF_BOARD |
|||
{ |
|||
private: |
|||
std::list<IDF_DRILL_DATA*> drills; ///< IDF drill data |
|||
int outlineIndex; ///< next outline index to use |
|||
bool useThou; ///< true if output is THOU |
|||
double scale; ///< scale from KiCad IU to IDF output units |
|||
double boardThickness; ///< total thickness of the PCB |
|||
bool hasBrdOutlineHdr; ///< true when a board outline header has been written |
|||
|
|||
double offsetX; ///< offset to roughly center the board on the world origin |
|||
double offsetY; |
|||
|
|||
FILE* layoutFile; ///< IDF board file (*.emn) |
|||
FILE* libFile; ///< IDF library file (*.emp) |
|||
|
|||
/** |
|||
* Function Write |
|||
* outputs a .DRILLED_HOLES section compliant with the |
|||
* IDFv3 specification. |
|||
* @param aLayoutFile : open file (*.emn) for output |
|||
*/ |
|||
bool WriteDrills( void ); |
|||
|
|||
public: |
|||
IDF_BOARD(); |
|||
|
|||
~IDF_BOARD(); |
|||
|
|||
// Set up the output files and scale factor; |
|||
// return TRUE if everything is OK |
|||
bool Setup( wxString aBoardName, wxString aFullFileName, bool aUseThou, int aBoardThickness ); |
|||
|
|||
// Finish a board |
|||
// Write out all current data and close files. |
|||
// Return true for success |
|||
bool Finish( void ); |
|||
|
|||
/** |
|||
* Function GetScale |
|||
* returns the output scaling factor |
|||
*/ |
|||
double GetScale( void ); |
|||
|
|||
/** |
|||
* Function SetOffset |
|||
* sets the global coordinate offsets |
|||
*/ |
|||
void SetOffset( double x, double y ); |
|||
|
|||
/** |
|||
* Function GetOffset |
|||
* returns the global coordinate offsets |
|||
*/ |
|||
void GetOffset( double& x, double& y ); |
|||
|
|||
// Add an outline; the very first outline is the board perimeter; |
|||
// all additional outlines are cutouts. |
|||
bool AddOutline( IDF_OUTLINE& aOutline ); |
|||
|
|||
/** |
|||
* Function AddDrill |
|||
* creates a drill entry and adds it to the list of PCB holes |
|||
* @param dia : drill diameter |
|||
* @param x : X coordinate of the drill center |
|||
* @param y : Y coordinate of the drill center |
|||
* @param plating : flag, PTH or NPTH |
|||
* @param refdes : component Reference Designator |
|||
* @param holetype : purpose of hole |
|||
* @param owner : one of MCAD, ECAD, UNOWNED |
|||
*/ |
|||
bool AddDrill( double dia, double x, double y, |
|||
IDF3::KEY_PLATING plating, |
|||
const std::string refdes, |
|||
const std::string holeType, |
|||
IDF3::KEY_OWNER owner ); |
|||
|
|||
/** |
|||
* Function AddSlot |
|||
* creates a slot cutout within the IDF BOARD section; this is a deficient representation |
|||
* of a KiCad 'oval' drill; IDF is unable to represent a plated slot and unable to |
|||
* represent the Reference Designator association with a slot. |
|||
*/ |
|||
bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY ); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_DRILL_DATA |
|||
* contains information describing a drilled hole and is responsible for |
|||
* writing this information to a file in compliance with the IDFv3 specification. |
|||
*/ |
|||
class IDF_DRILL_DATA |
|||
{ |
|||
private: |
|||
double dia; |
|||
double x; |
|||
double y; |
|||
IDF3::KEY_PLATING plating; |
|||
IDF3::KEY_REFDES kref; |
|||
IDF3::KEY_HOLETYPE khole; |
|||
std::string refdes; |
|||
std::string holetype; |
|||
IDF3::KEY_OWNER owner; |
|||
|
|||
public: |
|||
/** |
|||
* Constructor IDF_DRILL_DATA |
|||
* creates a drill entry with information compliant with the |
|||
* IDFv3 specifications. |
|||
* @param aDrillDia : drill diameter |
|||
* @param aPosX : X coordinate of the drill center |
|||
* @param aPosY : Y coordinate of the drill center |
|||
* @param aPlating : flag, PTH or NPTH |
|||
* @param aRefDes : component Reference Designator |
|||
* @param aHoleType : purpose of hole |
|||
* @param aOwner : one of MCAD, ECAD, UNOWNED |
|||
*/ |
|||
IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY, |
|||
IDF3::KEY_PLATING aPlating, |
|||
const std::string aRefDes, |
|||
const std::string aHoleType, |
|||
IDF3::KEY_OWNER aOwner ); |
|||
|
|||
/** |
|||
* Function Write |
|||
* writes a single line representing the hole within a .DRILLED_HOLES section |
|||
*/ |
|||
bool Write( FILE* aLayoutFile ); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* @Class IDF_LIB |
|||
* stores information on IDF models ( also has an inbuilt NOMODEL model ) |
|||
* and is responsible for writing the ELECTRICAL sections of the library file |
|||
* (*.emp) and the PLACEMENT section of the board file. |
|||
*/ |
|||
class IDF_LIB |
|||
{ |
|||
// TODO: IMPLEMENT |
|||
|
|||
public: |
|||
/** |
|||
* Function WriteLib |
|||
* writes all current library information to the output file |
|||
*/ |
|||
bool WriteLib( FILE* aLibFile ); |
|||
|
|||
// write placement information to the board file |
|||
bool WriteBrd( FILE* aLayoutFile ); |
|||
|
|||
// bool Finish( void ) |
|||
// { |
|||
// TODO: Write out the library (*.emp) file |
|||
// idf_lib.Write( lib_file ); |
|||
// TODO: fclose( lib_file ); |
|||
// } |
|||
}; |
|||
|
|||
#endif // IDF_H |
Write
Preview
Loading…
Cancel
Save
Reference in new issue