← Back to team overview

kicad-developers team mailing list archive

patches for basic IDFv3 support

 

Hi folks,

 Here are initial patches for consideration to introduce IDFv3 export support. At the moment an IDF board (.emn) and library file (.emp) are produced and the user has the option of producing the files in units of MM (default) or THOU as per IDFv3 spec. The board file uses the board outline if a valid one has been defined, otherwise the bounding box.

 The library file is currently not populated but I plan to develop that incrementally - at first I will implement a simple solution where all components are 5mm extrusions of the component bounding boxes. Any further development of the IDF library file would require much more work since it will be more instrusive but I can set aside some time to work on that if people really want a more refined IDF export.

 The export function is in the same sub-menu as the GenCad, Specctra, and VRML exports.

 I have tested the export using a few crazy outlines which I made up and also using the projects in the KiCad demo directory; all boards including the non-rectangular 'video' demo board were rendered correctly. Outlines with arcs (including hand-crafted arcs which are not 90 degrees) also render fine.

 The patches are against version 4555.

Regards,
Cirilo
=== modified file 'include/wxPcbStruct.h'
--- include/wxPcbStruct.h	2013-12-05 12:24:27 +0000
+++ include/wxPcbStruct.h	2013-12-14 11:00:12 +0000
@@ -956,6 +956,12 @@
                           bool aExport3DFiles, const wxString & a3D_Subdir );
 
     /**
+     * Function ExportToIDF3
+     * will export the current BOARD to a IDFv3 board and lib files.
+     */
+    void ExportToIDF3( wxCommandEvent& event );
+
+    /**
      * Function ExporttoSPECCTRA
      * will export the current BOARD to a specctra dsn file.  See
      * See http://www.autotraxeda.com/docs/SPECCTRA/SPECCTRA.pdf for the

=== modified file 'pcbnew/menubar_pcbframe.cpp'
--- pcbnew/menubar_pcbframe.cpp	2013-12-12 16:01:03 +0000
+++ pcbnew/menubar_pcbframe.cpp	2013-12-15 09:07:04 +0000
@@ -204,6 +204,11 @@
                  _( "Export a VRML board representation" ),
                  KiBitmap( three_d_xpm ) );
 
+    // IDF3
+    AddMenuItem( submenuexport, ID_GEN_EXPORT_FILE_IDF3,
+                 _( "I&DFv3" ), _( "Export IDFv3 format" ),
+                 KiBitmap( export_xpm ) );
+
     AddMenuItem( filesMenu, submenuexport,
                  ID_GEN_EXPORT_FILE, _( "E&xport" ),
                  _( "Export board" ), KiBitmap( export_xpm ) );

=== modified file 'pcbnew/pcbframe.cpp'
--- pcbnew/pcbframe.cpp	2013-12-12 16:01:03 +0000
+++ pcbnew/pcbframe.cpp	2013-12-15 09:07:04 +0000
@@ -108,6 +108,7 @@
     EVT_MENU( ID_GEN_EXPORT_FILE_GENCADFORMAT, PCB_EDIT_FRAME::ExportToGenCAD )
     EVT_MENU( ID_GEN_EXPORT_FILE_MODULE_REPORT, PCB_EDIT_FRAME::GenFootprintsReport )
     EVT_MENU( ID_GEN_EXPORT_FILE_VRML, PCB_EDIT_FRAME::OnExportVRML )
+    EVT_MENU( ID_GEN_EXPORT_FILE_IDF3, PCB_EDIT_FRAME::ExportToIDF3 )
 
     EVT_MENU( ID_GEN_IMPORT_SPECCTRA_SESSION,PCB_EDIT_FRAME::ImportSpecctraSession )
     EVT_MENU( ID_GEN_IMPORT_SPECCTRA_DESIGN, PCB_EDIT_FRAME::ImportSpecctraDesign )

=== modified file 'pcbnew/pcbnew_id.h'
--- pcbnew/pcbnew_id.h	2013-12-05 12:24:27 +0000
+++ pcbnew/pcbnew_id.h	2013-12-14 21:04:57 +0000
@@ -246,6 +246,7 @@
     ID_MENU_PCB_SWAP_LAYERS,
     ID_MENU_PCB_RESET_TEXTMODULE_FIELDS_SIZES,
 
+    ID_GEN_EXPORT_FILE_IDF3,
     ID_GEN_EXPORT_FILE_VRML,
     ID_GEN_EXPORT_SPECCTRA,
     ID_GEN_EXPORT_FILE_GENCADFORMAT,

=== modified file 'pcbnew/CMakeLists.txt'
--- pcbnew/CMakeLists.txt	2013-12-10 13:43:04 +0000
+++ pcbnew/CMakeLists.txt	2013-12-15 09:07:04 +0000
@@ -55,6 +55,8 @@
     dialogs/dialog_edit_module_text.cpp
     dialogs/dialog_edit_module_text_base.cpp
     dialogs/dialog_exchange_modules_base.cpp
+    dialogs/dialog_export_idf.cpp
+    dialogs/dialog_export_idf_base.cpp
     dialogs/dialog_export_vrml_base.cpp
     dialogs/dialog_export_vrml.cpp
     dialogs/dialog_find_base.cpp
@@ -173,6 +175,7 @@
     event_handlers_tracks_vias_sizes.cpp
     export_d356.cpp
     export_gencad.cpp
+    export_idf.cpp
     export_vrml.cpp
     files.cpp
     gen_drill_report_files.cpp
@@ -183,6 +186,7 @@
     hotkeys.cpp
     hotkeys_board_editor.cpp
     hotkeys_module_editor.cpp
+    idf.cpp
     initpcb.cpp
     layer_widget.cpp
     librairi.cpp

=== added file 'pcbnew/dialogs/dialog_export_idf.cpp'
--- pcbnew/dialogs/dialog_export_idf.cpp	1970-01-01 00:00:00 +0000
+++ pcbnew/dialogs/dialog_export_idf.cpp	2013-12-15 09:19:33 +0000
@@ -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;
+    }
+}

=== added file 'pcbnew/dialogs/dialog_export_idf_base.fbp'
--- pcbnew/dialogs/dialog_export_idf_base.fbp	1970-01-01 00:00:00 +0000
+++ pcbnew/dialogs/dialog_export_idf_base.fbp	2013-12-14 03:40:08 +0000
@@ -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>

=== added file 'pcbnew/dialogs/dialog_export_idf_base.cpp'
--- pcbnew/dialogs/dialog_export_idf_base.cpp	1970-01-01 00:00:00 +0000
+++ pcbnew/dialogs/dialog_export_idf_base.cpp	2013-12-14 03:40:08 +0000
@@ -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()
+{
+}

=== added file 'pcbnew/dialogs/dialog_export_idf_base.h'
--- pcbnew/dialogs/dialog_export_idf_base.h	1970-01-01 00:00:00 +0000
+++ pcbnew/dialogs/dialog_export_idf_base.h	2013-12-14 03:40:08 +0000
@@ -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__

=== added file 'pcbnew/export_idf.cpp'
--- pcbnew/export_idf.cpp	1970-01-01 00:00:00 +0000
+++ pcbnew/export_idf.cpp	2013-12-15 09:33:54 +0000
@@ -0,0 +1,332 @@
+/**
+ * @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.
+
+    // 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;
+                sp.y    = graphic->GetStart().y * scale;
+                ep.x    = graphic->GetEnd().x * scale;
+                ep.y    = graphic->GetEnd().y * scale;
+                IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep );
+
+                if( seg )
+                    lines.push_back( seg );
+            }
+            break;
+
+        case S_ARC:
+            {
+                sp.x = graphic->GetCenter().x * scale;
+                sp.y = graphic->GetCenter().y * scale;
+                ep.x = graphic->GetArcStart().x * scale;
+                ep.y = graphic->GetArcStart().y * scale;
+                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;
+                sp.y = graphic->GetCenter().y * scale;
+                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;
+    double  y   = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale;
+    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;
+
+    for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
+    {
+        drill = (double) pad->GetDrillSize().x * scale;
+        x     = pad->GetPosition().x * scale;
+        y     = pad->GetPosition().y * scale;
+
+        // 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 ) )
+                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 )
+            {
+                // 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;
+
+                if( dlength < drill )
+                    std::swap( drill, dlength );
+
+                aIDFBoard.AddSlot( drill, dlength, pad->GetOrientation() / 10.0, 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() );
+
+    // 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;
+}

=== added file 'pcbnew/idf.cpp'
--- pcbnew/idf.cpp	1970-01-01 00:00:00 +0000
+++ pcbnew/idf.cpp	2013-12-15 09:37:00 +0000
@@ -0,0 +1,989 @@
+/**
+ * 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 )
+{
+    // calculate the angle between the start point (center) and end point
+    double ang;
+    double dx = aEndPoint.x - aStartPoint.x;
+    double dy = aEndPoint.y - aStartPoint.y;
+
+    if( ( dx * dx ) < ( 0.1 * dy * dy ) )
+    {
+        // we have a steep dY/dX; use an alternative calculation of the angle
+        ang = M_PI2 - atan( dx / dy );
+
+        if( dy < 0.0 )
+            ang += M_PI;
+    }
+    else
+    {
+        ang = atan( dy / dx );
+
+        if( dx < 0.0 )
+        {
+            if( dy < 0.0 )
+                ang -= M_PI;
+            else
+                ang += M_PI;
+        }
+    }
+
+    return ang;
+}
+
+
+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;
+
+    // this is a round hole and should be treated differently
+    // XXX - in this case we should create a MTG drill hole owned by the board
+    // TO IMPLEMENT
+    if( aWidth == aLength )
+        return false;
+
+    if( aWidth > aLength )
+        std::swap( aWidth, aLength );
+
+    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( pt[1], pt[2], 180.0, false );
+    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( pt[3], pt[0], 180.0, false );
+    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 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;
+}

=== added file 'pcbnew/idf.h'
--- pcbnew/idf.h	1970-01-01 00:00:00 +0000
+++ pcbnew/idf.h	2013-12-15 09:42:28 +0000
@@ -0,0 +1,434 @@
+/**
+ * @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
+ */
+
+#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
+
+    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 );
+
+    // 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 );
+    // }
+};


Follow ups