7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-20 00:21:31 +00:00

Implement ODB++ export

ADDED: Add support in Pcbnew for exporting ODB++ files under Fabrication
       Outputs, base on ODB++Design Format Specification (Release v8.1
       Update 3 February 2021).

Note: There is still a lot of work to do if we will make the feature as
      complete as the ODB++ spec.  However, the current functionality's
      completeness is already sufficient to cover general production
      scenarios. I have compared the output results with Gerber files by
      DFM tool and the accuracy at the graphic level should be able to
      cover most usage scenarios.  Additionally, I am very grateful to
      the great open-source project Horizon EDA for giving me a lot of
      inspiration in terms of ideas.

The feature can be enabled by adding "EnableODB=1" to the kicad_advanced
configuration file.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2019
This commit is contained in:
Eric 2024-09-14 15:34:51 +00:00 committed by Wayne Stambaugh
parent d2cb868829
commit 1506beecbc
52 changed files with 7698 additions and 3 deletions

View File

@ -119,6 +119,7 @@ static const wxChar MinorSchematicGraphSize[] = wxT( "MinorSchematicGraphSize" )
static const wxChar ResolveTextRecursionDepth[] = wxT( "ResolveTextRecursionDepth" );
static const wxChar EnableExtensionSnaps[] = wxT( "EnableExtensionSnaps" );
static const wxChar EnableSnapAnchorsDebug[] = wxT( "EnableSnapAnchorsDebug" );
static const wxChar EnableODB[] = wxT( "EnableODB" );
} // namespace KEYS
@ -527,6 +528,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::ResolveTextRecursionDepth,
&m_ResolveTextRecursionDepth,
m_ResolveTextRecursionDepth, 0, 10 ) );
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableODB,
&m_EnableODB, m_EnableODB ) );
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableExtensionSnaps,
&m_EnableExtensionSnaps,

View File

@ -647,6 +647,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_24.png" ), 24, wxT( "light" ) );
@ -1063,6 +1064,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_dark_24.png" ), 24, wxT( "dark" ) );
@ -1479,6 +1481,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_16.png" ), 16, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_16.png" ), 16, wxT( "light" ) );
@ -1895,6 +1898,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_dark_16.png" ), 16, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_dark_16.png" ), 16, wxT( "dark" ) );
@ -2311,6 +2315,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_32.png" ), 32, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_32.png" ), 32, wxT( "light" ) );
@ -2727,6 +2732,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_dark_32.png" ), 32, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_dark_32.png" ), 32, wxT( "dark" ) );
@ -3143,6 +3149,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_48.png" ), 48, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_48.png" ), 48, wxT( "light" ) );
@ -3559,6 +3566,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_dark_48.png" ), 48, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_dark_48.png" ), 48, wxT( "dark" ) );
@ -3975,6 +3983,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_64.png" ), 64, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_64.png" ), 64, wxT( "light" ) );
@ -4391,6 +4400,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::post_gerber].emplace_back( BITMAPS::post_gerber, wxT( "post_gerber_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_rpt].emplace_back( BITMAPS::post_rpt, wxT( "post_rpt_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_xml].emplace_back( BITMAPS::post_xml, wxT( "post_xml_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::post_odb].emplace_back( BITMAPS::post_odb, wxT( "post_odb_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::preference].emplace_back( BITMAPS::preference, wxT( "preference_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::print_button].emplace_back( BITMAPS::print_button, wxT( "print_button_dark_64.png" ), 64, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::project].emplace_back( BITMAPS::project, wxT( "project_dark_64.png" ), 64, wxT( "dark" ) );

View File

@ -71,8 +71,8 @@ public:
*/
static const ADVANCED_CFG& GetCfg();
///@{
/// \ingroup advanced_config
///@{
/// \ingroup advanced_config
/**
* Distance from an arc end point and the estimated end point, when rotating from the
@ -660,6 +660,11 @@ public:
*/
bool m_EnableSnapAnchorsDebug;
/**
* When true, enable output to odb++
*/
bool m_EnableODB;
///@}
private:

View File

@ -464,6 +464,7 @@ enum class BITMAPS : unsigned int
post_module,
post_rpt,
post_xml,
post_odb,
preference,
primitives_to_custom_pad,
print_button,

View File

@ -57,6 +57,7 @@ enum LAST_PATH_TYPE : unsigned int
LAST_PATH_SVG,
LAST_PATH_PLOT,
LAST_PATH_2581,
LAST_PATH_ODBPP,
LAST_PATH_SIZE
};

View File

@ -68,6 +68,8 @@ set( PCBNEW_DIALOGS
dialogs/dialog_export_idf.cpp
dialogs/dialog_export_idf_base.cpp
dialogs/dialog_export_step.cpp
dialogs/dialog_export_odbpp.cpp
dialogs/dialog_export_odbpp_base.cpp
dialogs/dialog_export_step_base.cpp
dialogs/dialog_export_step_process.cpp
dialogs/dialog_export_step_process_base.cpp
@ -633,8 +635,9 @@ add_subdirectory( pcb_io/easyeda )
add_subdirectory( pcb_io/easyedapro )
add_subdirectory( pcb_io/fabmaster )
add_subdirectory( pcb_io/ipc2581 )
add_subdirectory( pcb_io/odbpp )
set( PCBNEW_IO_LIBRARIES pcad2kicadpcb altium2pcbnew cadstar2pcbnew easyeda easyedapro fabmaster ipc2581 CACHE INTERNAL "")
set( PCBNEW_IO_LIBRARIES pcad2kicadpcb altium2pcbnew cadstar2pcbnew easyeda easyedapro fabmaster ipc2581 odbpp CACHE INTERNAL "")
# a very small program launcher for pcbnew_kiface
add_executable( pcbnew WIN32 MACOSX_BUNDLE

View File

@ -0,0 +1,112 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <dialogs/dialog_export_odbpp.h>
#include <board.h>
#include <footprint.h>
#include <kiway_holder.h>
#include <pcb_edit_frame.h>
#include <pcbnew_settings.h>
#include <pgm_base.h>
#include <project.h>
#include <project/board_project_settings.h>
#include <project/project_file.h>
#include <settings/settings_manager.h>
#include <widgets/std_bitmap_button.h>
#include <set>
#include <vector>
#include <wx/dirdlg.h>
static wxString s_oemColumn = wxEmptyString;
DIALOG_EXPORT_ODBPP::DIALOG_EXPORT_ODBPP( PCB_EDIT_FRAME* aParent ) :
DIALOG_EXPORT_ODBPP_BASE( aParent ), m_parent( aParent )
{
m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
SetupStandardButtons( { { wxID_OK, _( "Export" ) }, { wxID_CANCEL, _( "Close" ) } } );
wxString path = m_parent->GetLastPath( LAST_PATH_ODBPP );
if( path.IsEmpty() )
{
wxFileName brdFile( m_parent->GetBoard()->GetFileName() );
path = brdFile.GetPath();
}
m_outputFileName->SetValue( path );
// Fill wxChoice (and others) items with data before calling finishDialogSettings()
// to calculate suitable widgets sizes
Init();
// Now all widgets have the size fixed, call FinishDialogSettings
finishDialogSettings();
}
void DIALOG_EXPORT_ODBPP::onBrowseClicked( wxCommandEvent& event )
{
// Build the absolute path of current output directory to preselect it in the file browser.
wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
wxFileName fn( Prj().AbsolutePath( path ) );
wxDirDialog dlg( this, _( "Export ODB++ File" ), fn.GetPath() );
if( dlg.ShowModal() == wxID_CANCEL )
return;
m_outputFileName->SetValue( dlg.GetPath() );
}
void DIALOG_EXPORT_ODBPP::onOKClick( wxCommandEvent& event )
{
m_parent->SetLastPath( LAST_PATH_ODBPP, m_outputFileName->GetValue() );
event.Skip();
}
bool DIALOG_EXPORT_ODBPP::Init()
{
PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
BOARD* board = m_parent->GetBoard();
m_choiceUnits->SetSelection( cfg->m_ExportODBPP.units );
m_precision->SetValue( cfg->m_ExportODBPP.precision );
m_cbCompress->SetValue( cfg->m_ExportODBPP.compress );
return true;
}
bool DIALOG_EXPORT_ODBPP::TransferDataFromWindow()
{
PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
cfg->m_ExportODBPP.units = m_choiceUnits->GetSelection();
cfg->m_ExportODBPP.precision = m_precision->GetValue();
cfg->m_ExportODBPP.compress = m_cbCompress->GetValue();
return true;
}

View File

@ -0,0 +1,671 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="17"/>
<object class="Project" expanded="true">
<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_odbpp_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property>
<property name="internationalize">1</property>
<property name="name">DIALOG_EXPORT_ODBPP_BASE</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_lua_events">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="true">
<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="drag_accept_files">0</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_ODBPP_BASE</property>
<property name="pos"></property>
<property name="size">380,300</property>
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
<property name="title">Export ODB++</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bMainSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">15</property>
<property name="flag">wxBOTTOM|wxEXPAND|wxTOP</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bSizerTop</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">protected</property>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="false">
<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="drag_accept_files">0</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">File:</property>
<property name="markup">0</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_lblBrdFile</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>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">1</property>
<object class="wxTextCtrl" expanded="false">
<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="drag_accept_files">0</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="maxlength">0</property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">350,-1</property>
<property name="moveable">1</property>
<property name="name">m_outputFileName</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">Enter a filename if you do not want to use default file names&#x0A;Can be used only when printing the current sheet</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="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="false">
<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="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></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="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyButton</property>
<property name="margins"></property>
<property name="markup">0</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">-1,-1</property>
<property name="moveable">1</property>
<property name="name">m_browseButton</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="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size">-1,-1</property>
<property name="style"></property>
<property name="subclass">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</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="OnButtonClick">onBrowseClicked</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bSizer3</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">10</property>
<property name="flag">wxEXPAND|wxLEFT|wxRIGHT|wxTOP</property>
<property name="proportion">1</property>
<object class="wxStaticBoxSizer" expanded="true">
<property name="id">wxID_ANY</property>
<property name="label">File Format</property>
<property name="minimum_size"></property>
<property name="name">sbSizer1</property>
<property name="orient">wxVERTICAL</property>
<property name="parent">1</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">3</property>
<object class="wxFlexGridSizer" expanded="false">
<property name="cols">2</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols">1</property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizer</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="false">
<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="drag_accept_files">0</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">Units:</property>
<property name="markup">0</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_lblUnits</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>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_RIGHT|wxALL</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;Millimeters&quot; &quot;Inches&quot;</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="drag_accept_files">0</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="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_choiceUnits</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size">130,30</property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</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>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="false">
<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="drag_accept_files">0</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">Precision:</property>
<property name="markup">0</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_lblPrecision</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">The number of values following the decimal separator</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_RIGHT|wxALL</property>
<property name="proportion">0</property>
<object class="wxSpinCtrl" expanded="false">
<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="drag_accept_files">0</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="initial">7</property>
<property name="max">16</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min">2</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_precision</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">130,30</property>
<property name="style">wxSP_ARROW_KEYS</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">The number of values following the decimal separator</property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="false">
<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="drag_accept_files">0</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">Compress output</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_cbCompress</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">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Compress output into &apos;zip&apos; file</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="OnCheckBox">onCompressCheck</event>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="false">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxStdDialogButtonSizer" expanded="false">
<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_stdButtons</property>
<property name="permission">protected</property>
<event name="OnOKButtonClick">onOKClick</event>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,56 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef ODBPP_EXPORT_DIALOG_H
#define ODBPP_EXPORT_DIALOG_H
#include "dialog_export_odbpp_base.h"
class PCB_EDIT_FRAME;
class DIALOG_EXPORT_ODBPP : public DIALOG_EXPORT_ODBPP_BASE
{
public:
DIALOG_EXPORT_ODBPP( PCB_EDIT_FRAME* aParent );
wxString GetOutputPath() const { return m_outputFileName->GetValue(); }
wxString GetUnitsString() const
{
if( m_choiceUnits->GetSelection() == 0 )
return wxT( "mm" );
else
return wxT( "inch" );
}
wxString GetPrecision() const { return wxString::Format( "%d", m_precision->GetValue() ); }
bool GetCompress() const { return m_cbCompress->GetValue(); }
private:
void onBrowseClicked( wxCommandEvent& event ) override;
void onOKClick( wxCommandEvent& event ) override;
bool Init();
bool TransferDataFromWindow() override;
PCB_EDIT_FRAME* m_parent;
};
#endif // ODBPP_EXPORT_DIALOG_H

View File

@ -0,0 +1,117 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/std_bitmap_button.h"
#include "dialog_export_odbpp_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_EXPORT_ODBPP_BASE::DIALOG_EXPORT_ODBPP_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* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL );
bSizerTop = new wxBoxSizer( wxHORIZONTAL );
m_lblBrdFile = new wxStaticText( this, wxID_ANY, _("File:"), wxDefaultPosition, wxDefaultSize, 0 );
m_lblBrdFile->Wrap( -1 );
bSizerTop->Add( m_lblBrdFile, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_outputFileName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_outputFileName->SetToolTip( _("Enter a filename if you do not want to use default file names\nCan be used only when printing the current sheet") );
m_outputFileName->SetMinSize( wxSize( 350,-1 ) );
bSizerTop->Add( m_outputFileName, 1, wxALIGN_CENTER_VERTICAL, 5 );
m_browseButton = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
bSizerTop->Add( m_browseButton, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
bMainSizer->Add( bSizerTop, 0, wxBOTTOM|wxEXPAND|wxTOP, 15 );
wxBoxSizer* bSizer3;
bSizer3 = new wxBoxSizer( wxHORIZONTAL );
wxStaticBoxSizer* sbSizer1;
sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("File Format") ), wxVERTICAL );
wxFlexGridSizer* fgSizer;
fgSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
fgSizer->AddGrowableCol( 1 );
fgSizer->SetFlexibleDirection( wxBOTH );
fgSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_lblUnits = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Units:"), wxDefaultPosition, wxDefaultSize, 0 );
m_lblUnits->Wrap( -1 );
fgSizer->Add( m_lblUnits, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
wxString m_choiceUnitsChoices[] = { _("Millimeters"), _("Inches") };
int m_choiceUnitsNChoices = sizeof( m_choiceUnitsChoices ) / sizeof( wxString );
m_choiceUnits = new wxChoice( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxSize( 130,30 ), m_choiceUnitsNChoices, m_choiceUnitsChoices, 0 );
m_choiceUnits->SetSelection( 0 );
fgSizer->Add( m_choiceUnits, 0, wxALIGN_RIGHT|wxALL, 5 );
m_lblPrecision = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Precision:"), wxDefaultPosition, wxDefaultSize, 0 );
m_lblPrecision->Wrap( -1 );
m_lblPrecision->SetToolTip( _("The number of values following the decimal separator") );
fgSizer->Add( m_lblPrecision, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_precision = new wxSpinCtrl( sbSizer1->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 130,30 ), wxSP_ARROW_KEYS, 2, 16, 3 );
m_precision->SetToolTip( _("The number of values following the decimal separator") );
fgSizer->Add( m_precision, 0, wxALIGN_RIGHT|wxALL, 5 );
m_cbCompress = new wxCheckBox( sbSizer1->GetStaticBox(), wxID_ANY, _("Compress output"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbCompress->SetToolTip( _("Compress output into 'zip' file") );
fgSizer->Add( m_cbCompress, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
sbSizer1->Add( fgSizer, 3, wxEXPAND|wxALL, 5 );
bSizer3->Add( sbSizer1, 1, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 10 );
bMainSizer->Add( bSizer3, 0, wxEXPAND, 5 );
bMainSizer->Add( 0, 0, 1, wxEXPAND, 5 );
m_stdButtons = new wxStdDialogButtonSizer();
m_stdButtonsOK = new wxButton( this, wxID_OK );
m_stdButtons->AddButton( m_stdButtonsOK );
m_stdButtonsCancel = new wxButton( this, wxID_CANCEL );
m_stdButtons->AddButton( m_stdButtonsCancel );
m_stdButtons->Realize();
bMainSizer->Add( m_stdButtons, 0, wxALL|wxEXPAND, 5 );
this->SetSizer( bMainSizer );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onBrowseClicked ), NULL, this );
m_cbCompress->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onCompressCheck ), NULL, this );
m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onOKClick ), NULL, this );
}
DIALOG_EXPORT_ODBPP_BASE::~DIALOG_EXPORT_ODBPP_BASE()
{
// Disconnect Events
m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onBrowseClicked ), NULL, this );
m_cbCompress->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onCompressCheck ), NULL, this );
m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_EXPORT_ODBPP_BASE::onOKClick ), NULL, this );
}

View File

@ -0,0 +1,72 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class STD_BITMAP_BUTTON;
#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/textctrl.h>
#include <wx/bmpbuttn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/choice.h>
#include <wx/spinctrl.h>
#include <wx/checkbox.h>
#include <wx/statbox.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_EXPORT_ODBPP_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_EXPORT_ODBPP_BASE : public DIALOG_SHIM
{
private:
protected:
wxBoxSizer* bSizerTop;
wxStaticText* m_lblBrdFile;
wxTextCtrl* m_outputFileName;
STD_BITMAP_BUTTON* m_browseButton;
wxStaticText* m_lblUnits;
wxChoice* m_choiceUnits;
wxStaticText* m_lblPrecision;
wxSpinCtrl* m_precision;
wxCheckBox* m_cbCompress;
wxStdDialogButtonSizer* m_stdButtons;
wxButton* m_stdButtonsOK;
wxButton* m_stdButtonsCancel;
// Virtual event handlers, override them in your derived class
virtual void onBrowseClicked( wxCommandEvent& event ) { event.Skip(); }
virtual void onCompressCheck( wxCommandEvent& event ) { event.Skip(); }
virtual void onOKClick( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_EXPORT_ODBPP_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Export ODB++"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 380,300 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_EXPORT_ODBPP_BASE();
};

View File

@ -63,6 +63,7 @@
#include <pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.h>
#include <dialogs/dialog_export_2581.h>
#include <dialogs/dialog_map_layers.h>
#include <dialogs/dialog_export_odbpp.h>
#include <dialogs/dialog_import_choose_project.h>
#include <tools/pcb_actions.h>
#include "footprint_info_impl.h"
@ -80,6 +81,7 @@
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <wx/dir.h>
#include "widgets/filedlg_hook_save_project.h"
@ -1404,3 +1406,144 @@ void PCB_EDIT_FRAME::GenIPC2581File( wxCommandEvent& event )
GetScreen()->SetContentModified( false );
}
void PCB_EDIT_FRAME::GenODBPPFiles( wxCommandEvent& event )
{
DIALOG_EXPORT_ODBPP dlg( this );
if( dlg.ShowModal() != wxID_OK )
return;
wxFileName pcbFileName = dlg.GetOutputPath();
// Write through symlinks, don't replace them
WX_FILENAME::ResolvePossibleSymlinks( pcbFileName );
if( !IsWritable( pcbFileName ) )
{
wxString msg = wxString::Format( _( "Insufficient permissions to write file '%s'." ),
pcbFileName.GetFullPath() );
DisplayErrorMessage( this, msg );
return;
}
if( !wxFileName::DirExists( pcbFileName.GetFullPath() ) )
{
// Make every directory provided when the provided path doesn't exist
if( !wxFileName::Mkdir( pcbFileName.GetFullPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
{
wxString msg;
msg.Printf( _( "Cannot create output directory '%s'." ), pcbFileName.GetFullPath() );
DisplayErrorMessage( this, msg );
return;
}
}
wxFileName tempFile( pcbFileName.GetFullPath(), "" );
tempFile.AppendDir( "odb" );
wxString upperTxt;
wxString lowerTxt;
std::map<std::string, UTF8> props;
props["units"] = dlg.GetUnitsString();
props["sigfig"] = dlg.GetPrecision();
WX_PROGRESS_REPORTER reporter( this, _( "Generating ODB++ output files" ), 5 );
auto saveFile = [&]() -> bool
{
try
{
IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::ODBPP ) );
pi->SetProgressReporter( &reporter );
pi->SaveBoard( pcbFileName.GetFullPath(), GetBoard(), &props );
return true;
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, wxString::Format( _( "Error generating ODBPP files '%s'.\n%s" ),
tempFile.GetFullPath(), ioe.What() ) );
lowerTxt.Printf( _( "Failed to create directory '%s'." ), tempFile.GetFullPath() );
SetMsgPanel( upperTxt, lowerTxt );
// In case we started a file but didn't fully write it, clean up
wxFileName::Rmdir( tempFile.GetFullPath() );
return false;
}
};
thread_pool& tp = GetKiCadThreadPool();
auto ret = tp.submit( saveFile );
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
while( status != std::future_status::ready )
{
reporter.KeepRefreshing();
status = ret.wait_for( std::chrono::milliseconds( 250 ) );
}
try
{
if( !ret.get() )
return;
}
catch( const std::exception& e )
{
wxLogError( "Exception in ODB++ generation: %s", e.what() );
GetScreen()->SetContentModified( false );
return;
}
if( dlg.GetCompress() )
{
wxFileName zipFileName( pcbFileName.GetFullPath(), "odb.zip" );
wxFFileOutputStream fnout( zipFileName.GetFullPath() );
wxZipOutputStream zipStream( fnout );
std::function<void( const wxString&, const wxString& )> addDirToZip =
[&]( const wxString& dirPath, const wxString& parentPath )
{
wxDir dir( dirPath );
wxString fileName;
bool cont = dir.GetFirst( &fileName, wxEmptyString, wxDIR_DEFAULT );
while( cont )
{
wxFileName fileInZip( dirPath, fileName );
wxString relativePath =
parentPath.IsEmpty()
? fileName
: parentPath + wxString( wxFileName::GetPathSeparator() )
+ fileName;
if( wxFileName::DirExists( fileInZip.GetFullPath() ) )
{
zipStream.PutNextDirEntry( relativePath );
addDirToZip( fileInZip.GetFullPath(), relativePath );
}
else
{
wxFFileInputStream fileStream( fileInZip.GetFullPath() );
zipStream.PutNextEntry( relativePath );
fileStream.Read( zipStream );
}
cont = dir.GetNext( &fileName );
}
};
addDirToZip( tempFile.GetFullPath(), wxEmptyString );
zipStream.Close();
fnout.Close();
}
GetScreen()->SetContentModified( false );
}

View File

@ -168,6 +168,10 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
submenuFabOutputs->Add( PCB_ACTIONS::generateGerbers );
submenuFabOutputs->Add( PCB_ACTIONS::generateDrillFiles );
submenuFabOutputs->Add( PCB_ACTIONS::generateIPC2581File );
if( ADVANCED_CFG::GetCfg().m_EnableODB )
submenuFabOutputs->Add( PCB_ACTIONS::generateODBPPFile );
submenuFabOutputs->Add( PCB_ACTIONS::generatePosFile );
submenuFabOutputs->Add( PCB_ACTIONS::generateReportFile );
submenuFabOutputs->Add( PCB_ACTIONS::generateD356File );

View File

@ -346,6 +346,11 @@ public:
*/
void GenIPC2581File( wxCommandEvent& event );
/**
* Create and Generate ODB++ output files
*/
void GenODBPPFiles( wxCommandEvent& event );
/**
* Create an ASCII footprint report file giving some infos on footprints and board outlines.
*

View File

@ -0,0 +1,38 @@
# This program source code file is part of KiCad, a free EDA CAD application.
#
# Copyright (C) 2024 KiCad Developers, see AUTHORS.TXT for contributors.
# Author: SYSUEric <jzzhuang666@gmail.com>.
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
# Sources for the pcbnew pcb_io called ODB++
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
set( ODBPP_SRCS
odb_util.cpp
odb_attribute.cpp
odb_feature.cpp
odb_component.cpp
odb_netlist.cpp
odb_eda_data.cpp
odb_fonts.cpp
odb_entity.cpp
pcb_io_odbpp.cpp
)
add_library( odbpp STATIC ${ODBPP_SRCS} )
target_link_libraries( odbpp pcbcommon )

View File

@ -0,0 +1,101 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "odb_attribute.h"
#include <sstream>
#include <iomanip>
size_t ATTR_MANAGER::GetTextIndex( std::unordered_map<std::string, size_t>& aMap,
std::vector<std::pair<size_t, std::string>>& aVec,
const std::string& aText )
{
if( aMap.count( aText ) )
{
return aMap.at( aText );
}
else
{
auto index = aMap.size();
aMap.emplace( aText, index );
aVec.emplace_back( index, aText );
return index;
}
}
size_t ATTR_MANAGER::GetAttrNameNumber( const wxString& aName )
{
return GetTextIndex( m_attrNames, m_attrNameVec, aName.Lower().ToStdString() );
}
size_t ATTR_MANAGER::GetAttrTextNumber( const wxString& aText )
{
return GetTextIndex( m_attrTexts, m_attrTextVec, aText.Upper().ToStdString() );
}
void ATTR_RECORD_WRITER::WriteAttributes( std::ostream& ost ) const
{
ODB::CHECK_ONCE once;
for( const auto& attr : attributes )
{
if( once() )
ost << ";";
else
ost << ",";
ost << attr.first;
if( attr.second.size() )
ost << "=" << attr.second;
}
ost << ";";
}
void ATTR_MANAGER::WriteAttributesName( std::ostream& ost, const std::string& prefix ) const
{
for( const auto& [n, name] : m_attrNameVec )
{
ost << prefix << "@" << n << " " << name << std::endl;
}
}
void ATTR_MANAGER::WriteAttributesText( std::ostream& ost, const std::string& prefix ) const
{
for( const auto& [n, name] : m_attrTextVec )
{
ost << prefix << "&" << n << " " << name << std::endl;
}
}
void ATTR_MANAGER::WriteAttributes( std::ostream& ost, const std::string& prefix ) const
{
ost << std::endl << "#\n#Feature attribute names\n#" << std::endl;
WriteAttributesName( ost );
ost << std::endl << "#\n#Feature attribute text strings\n#" << std::endl;
WriteAttributesText( ost );
}

View File

@ -0,0 +1,352 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ATTRIBUTE_PROVIDER_H_
#define _ATTRIBUTE_PROVIDER_H_
#include "odb_util.h"
#include "stroke_params.h"
#include <wx/string.h>
#include <string>
#include <type_traits>
namespace ODB_ATTR
{
enum class TYPE
{
FLOAT,
BOOLEAN,
TEXT,
OPTION,
INTEGER
};
// Base class template for attributes
template <typename T, TYPE AttrType>
struct AttributeBase
{
using ValueType = T;
static constexpr TYPE type = AttrType;
constexpr AttributeBase( T v ) : value( v ) {}
T value;
};
// Specialized attribute types
template <typename T, unsigned int N>
struct FloatAttribute : AttributeBase<double, TYPE::FLOAT>
{
static constexpr unsigned int digits = N;
using AttributeBase<double, TYPE::FLOAT>::AttributeBase;
};
template <typename T>
struct BooleanAttribute : AttributeBase<bool, TYPE::BOOLEAN>
{
using AttributeBase<bool, TYPE::BOOLEAN>::AttributeBase;
};
template <typename T>
struct TextAttribute : AttributeBase<std::string, TYPE::TEXT>
{
constexpr TextAttribute( const std::string& t ) :
AttributeBase<std::string, TYPE::TEXT>( ODB::GenLegalEntityName( t ).ToStdString() )
{
}
};
template <typename T>
struct OPTION_Attribute : AttributeBase<int, TYPE::OPTION>
{
using AttributeBase<int, TYPE::OPTION>::AttributeBase;
};
template <typename T>
struct AttributeName
{
};
// Attribute name and type definitions
template <typename Tag, template <typename, unsigned int> class Attr, TYPE AttrType, unsigned int N>
struct Attribute
{
using TYPE = Attr<Tag, N>;
};
template <typename Tag, template <typename> class Attr, TYPE AttrType>
struct AttributeSimple
{
using TYPE = Attr<Tag>;
};
// TYPE traits for attributes
template <typename T>
struct IsFeature : std::false_type
{
};
template <typename T>
struct IsNet : std::false_type
{
};
template <typename T>
struct IsPkg : std::false_type
{
};
template <typename T>
struct IsLayer : std::false_type
{
};
template <typename T>
struct IsStep : std::false_type
{
};
template <typename T>
struct IsComp : std::false_type
{
};
template <typename T>
struct IsProductModel : std::false_type
{
};
template <typename T>
struct IsSymbol : std::false_type
{
};
#define DEFINE_ATTR( Tag, Attr, AttrType, AttrName, ... ) \
struct Tag##_t \
{ \
}; \
constexpr const char Tag##_name[] = AttrName; \
using Tag = Attribute<Tag##_t, Attr, AttrType, __VA_ARGS__>::TYPE; \
template <> \
struct AttributeName<Tag> \
{ \
static constexpr const char* name = Tag##_name; \
};
#define DEFINE_ATTR_SIMPLE( Tag, Attr, AttrType, AttrName ) \
struct Tag##_t \
{ \
}; \
constexpr const char Tag##_name[] = AttrName; \
using Tag = AttributeSimple<Tag##_t, Attr, AttrType>::TYPE; \
template <> \
struct AttributeName<Tag> \
{ \
static constexpr const char* name = Tag##_name; \
};
#define DEFINE_FLOAT_ATTR( NAME, N ) DEFINE_ATTR( NAME, FloatAttribute, TYPE::FLOAT, #NAME, N )
#define DEFINE_BOOLEAN_ATTR( NAME ) \
DEFINE_ATTR_SIMPLE( NAME, BooleanAttribute, TYPE::BOOLEAN, #NAME )
#define DEFINE_TEXT_ATTR( NAME ) DEFINE_ATTR_SIMPLE( NAME, TextAttribute, TYPE::TEXT, #NAME )
#define DEFINE_OPTION_ATTR( NAME ) \
struct NAME##_t \
{ \
}; \
template <> \
struct AttributeSimple<NAME##_t, OPTION_Attribute, TYPE::OPTION>; \
template <> \
struct AttributeName<NAME> \
{ \
static constexpr const char* name = #NAME; \
};
// used by which entity
#define USED_BY_FEATURE_ENTITY( NAME ) \
template <> \
struct IsFeature<NAME> : std::true_type \
{ \
};
#define USED_BY_NET_ENTITY( NAME ) \
template <> \
struct IsNet<NAME> : std::true_type \
{ \
};
#define USED_BY_PKG_ENTITY( NAME ) \
template <> \
struct IsPkg<NAME> : std::true_type \
{ \
};
// Attribute definitions
// BOOLEAN ATTRIBUTES
DEFINE_BOOLEAN_ATTR( SMD )
USED_BY_FEATURE_ENTITY( SMD )
DEFINE_BOOLEAN_ATTR( NET_POINT )
USED_BY_FEATURE_ENTITY( NET_POINT )
DEFINE_BOOLEAN_ATTR( ROUT_PLATED )
USED_BY_FEATURE_ENTITY( ROUT_PLATED )
DEFINE_BOOLEAN_ATTR( MECHANICAL )
DEFINE_BOOLEAN_ATTR( MOUNT_HOLE )
USED_BY_FEATURE_ENTITY( MOUNT_HOLE )
DEFINE_BOOLEAN_ATTR( TEAR_DROP )
USED_BY_FEATURE_ENTITY( TEAR_DROP )
DEFINE_BOOLEAN_ATTR( TEST_POINT )
USED_BY_FEATURE_ENTITY( TEST_POINT )
// TEXT ATTRIBUTES
DEFINE_TEXT_ATTR( STRING )
USED_BY_FEATURE_ENTITY( STRING )
DEFINE_TEXT_ATTR( GEOMETRY )
USED_BY_FEATURE_ENTITY( GEOMETRY )
DEFINE_TEXT_ATTR( NET_NAME )
USED_BY_FEATURE_ENTITY( NET_NAME )
// FLOAT ATTRIBUTES
DEFINE_FLOAT_ATTR( BOARD_THICKNESS, 1 ) // 0.0~10.0
DEFINE_FLOAT_ATTR( STRING_ANGLE, 1 ) // 0.0~360.0
USED_BY_FEATURE_ENTITY( STRING_ANGLE )
// OPTION ATTRIBUTES
enum class DRILL
{
PLATED,
NON_PLATED,
VIA
};
DEFINE_OPTION_ATTR( DRILL )
USED_BY_FEATURE_ENTITY( DRILL )
enum class PAD_USAGE
{
TOEPRINT,
VIA,
G_FIDUCIAL,
L_FIDUCIAL,
TOOLING_HOLE,
BOND_FINGER
};
DEFINE_OPTION_ATTR( PAD_USAGE )
USED_BY_FEATURE_ENTITY( PAD_USAGE )
enum class PLATED_TYPE
{
STANDARD,
PRESS_FIT
};
DEFINE_OPTION_ATTR( PLATED_TYPE )
USED_BY_FEATURE_ENTITY( PLATED_TYPE )
enum class VIA_TYPE
{
DRILLED,
LASER,
PHOTO
};
DEFINE_OPTION_ATTR( VIA_TYPE )
USED_BY_FEATURE_ENTITY( VIA_TYPE )
} // namespace ODB_ATTR
class ATTR_MANAGER
{
public:
ATTR_MANAGER() = default;
virtual ~ATTR_MANAGER() = default;
template <typename Tr, typename Ta>
void AddFeatureAttribute( Tr& r, Ta v )
{
const auto id = GetAttrNameNumber( ODB_ATTR::AttributeName<Ta>::name );
if constexpr( std::is_enum_v<Ta> )
r.attributes.emplace( id, std::to_string( static_cast<int>( v ) ) );
else
r.attributes.emplace( id, AttrValue2String( v ) );
}
protected:
size_t GetAttrNameNumber( const wxString& name );
void WriteAttributes( std::ostream& ost, const std::string& prefix = "" ) const;
void WriteAttributesName( std::ostream& ost, const std::string& prefix = "" ) const;
void WriteAttributesText( std::ostream& ost, const std::string& prefix = "" ) const;
private:
size_t GetAttrTextNumber( const wxString& aName );
size_t GetTextIndex( std::unordered_map<std::string, size_t>& aMap,
std::vector<std::pair<size_t, std::string>>& aVec,
const std::string& aText );
template <typename T, unsigned int n>
std::string AttrValue2String( ODB_ATTR::FloatAttribute<T, n> a )
{
return ODB::Double2String( a.value, a.digits );
}
template <typename T>
std::string AttrValue2String( ODB_ATTR::BooleanAttribute<T> a )
{
return "";
}
template <typename T>
std::string AttrValue2String( ODB_ATTR::TextAttribute<T> a )
{
return std::to_string( GetAttrTextNumber( a.value ) );
}
std::unordered_map<std::string, size_t> m_attrNames;
std::vector<std::pair<size_t, std::string>> m_attrNameVec;
std::unordered_map<std::string, size_t> m_attrTexts;
std::vector<std::pair<size_t, std::string>> m_attrTextVec;
};
class ATTR_RECORD_WRITER
{
public:
ATTR_RECORD_WRITER() = default;
virtual ~ATTR_RECORD_WRITER() = default;
void WriteAttributes( std::ostream& ost ) const;
public:
std::map<unsigned int, std::string> attributes;
};
#endif // ATTRIBUTE_PROVIDER_H_

View File

@ -0,0 +1,94 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "odb_component.h"
#include "odb_util.h"
#include "hash_eda.h"
#include "pcb_io_odbpp.h"
ODB_COMPONENT& COMPONENTS_MANAGER::AddComponent( const FOOTPRINT* aFp,
const EDA_DATA::PACKAGE& aPkg )
{
auto& comp = m_compList.emplace_back( m_compList.size(), aPkg.m_index );
comp.m_center = ODB::AddXY( aFp->GetPosition() );
if( aFp->GetOrientation() != ANGLE_0 )
{
// odb Rotation is expressed in degrees and is always clockwise.
// while kicad EDA_ANGLE is anticlockwise.
comp.m_rot =
ODB::Double2String( ( ANGLE_360 - aFp->GetOrientation() ).Normalize().AsDegrees() );
}
if( aFp->GetLayer() != F_Cu )
{
comp.m_mirror = wxT( "M" );
}
comp.m_comp_name = aFp->GetReference().ToAscii();
comp.m_part_name =
wxString::Format( "%s_%s_%s", aFp->GetFPID().GetFullLibraryName(),
aFp->GetFPID().GetLibItemName().wx_str(), aFp->GetValue() );
return comp;
}
void COMPONENTS_MANAGER::Write( std::ostream& ost ) const
{
ost << "UNITS=" << PCB_IO_ODBPP::m_unitsStr << std::endl;
WriteAttributes( ost );
for( const auto& comp : m_compList )
{
comp.Write( ost );
}
}
void ODB_COMPONENT::Write( std::ostream& ost ) const
{
ost << "# CMP " << m_index << std::endl;
ost << "CMP " << m_pkg_ref << " " << m_center.first << " " << m_center.second << " " << m_rot
<< " " << m_mirror << " " << m_comp_name << " " << m_part_name;
WriteAttributes( ost );
ost << std::endl;
for( const auto& toep : m_toeprints )
{
toep.Write( ost );
}
ost << "#" << std::endl;
}
void ODB_COMPONENT::TOEPRINT::Write( std::ostream& ost ) const
{
ost << "TOP " << m_pin_num << " " << m_center.first << " " << m_center.second << " " << m_rot
<< " " << m_mirror << " " << m_net_num << " " << m_subnet_num << " " << m_toeprint_name
<< std::endl;
}

View File

@ -0,0 +1,99 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ODB_COMPONENT_H_
#define _ODB_COMPONENT_H_
#include "odb_util.h"
#include <list>
#include <wx/string.h>
#include "odb_attribute.h"
#include "odb_eda_data.h"
class ODB_COMPONENT;
class COMPONENTS_MANAGER : public ATTR_MANAGER
{
public:
COMPONENTS_MANAGER() = default;
virtual ~COMPONENTS_MANAGER() { m_compList.clear(); }
ODB_COMPONENT& AddComponent( const FOOTPRINT* aFp, const EDA_DATA::PACKAGE& aPkg );
void Write( std::ostream& ost ) const;
private:
std::list<ODB_COMPONENT> m_compList;
};
class ODB_COMPONENT : public ATTR_RECORD_WRITER
{
public:
ODB_COMPONENT( size_t aIndex, size_t r ) : m_index( aIndex ), m_pkg_ref( r ) {}
const size_t m_index; ///<! CMP index number on board to be used in SNT(TOP), 0~n-1
size_t m_pkg_ref; ///<! package ref number from PKG in eda/data file, 0~n-1
std::pair<wxString, wxString> m_center;
wxString m_rot = wxT( "0" );
wxString m_mirror = wxT( "N" );
wxString m_comp_name; ///<! Unique reference designator (component name)
wxString
m_part_name; ///<! Part identification is a single string of ASCII characters without spaces
std::vector<std::pair<wxString, wxString>> m_prp; // !< Component Property Record
struct TOEPRINT
{
public:
TOEPRINT( const EDA_DATA::PIN& pin ) :
m_pin_num( pin.m_index ), m_toeprint_name( pin.m_name )
{
}
const size_t m_pin_num; ///<! index of PIN record in the eda/data file, 0~n-1.
std::pair<wxString, wxString> m_center; ///<! Board location of the pin.
wxString m_rot; ///<! Rotation, clockwise, it equals to the actual PAD rotation,
///<! not CMP m_rot.
wxString m_mirror; ///<! equal to CMP m_mirror.
size_t m_net_num = 0; ///<! Number of NET record in the eda/data file.
size_t m_subnet_num = 0; ///<! Number of subnet (SNT record TOP) in the referenced net
wxString m_toeprint_name; ///<! Name of the pad in PIN record
void Write( std::ostream& ost ) const;
};
std::list<TOEPRINT> m_toeprints;
void Write( std::ostream& ost ) const;
};
#endif // _ODB_COMPONENT_H_

View File

@ -0,0 +1,34 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ODB_DEFINES_H_
#define _ODB_DEFINES_H_
#include <string>
#define ODB_JOB_NAME "JOB_NAME"
#define ODB_UNITS "UNITS"
#define ODB_DIM_X "x"
#define ODB_DIM_R "r"
#define ODB_DIM_C "c"
#define ODB_NONE "NONE"
#endif // _ODB_DEFINES_H_

View File

@ -0,0 +1,420 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "odb_eda_data.h"
#include "hash_eda.h"
#include "netinfo.h"
#include "odb_feature.h"
#include "base_units.h"
#include "pcb_io_odbpp.h"
EDA_DATA::EDA_DATA()
{
auto& x = nets_map.emplace( std::piecewise_construct, std::forward_as_tuple( 0 ),
std::forward_as_tuple( nets.size(), "$NONE$" ) )
.first->second;
nets.push_back( &x );
}
void EDA_DATA::NET::Write( std::ostream& ost ) const
{
ost << "NET " << m_name;
WriteAttributes( ost );
ost << std::endl;
for( const auto& subnet : subnets )
{
subnet->Write( ost );
}
}
void EDA_DATA::AddNET( const NETINFO_ITEM* aNet )
{
if( nets_map.end() == nets_map.find( aNet->GetNetCode() ) )
{
auto& net = nets_map.emplace( std::piecewise_construct,
std::forward_as_tuple( aNet->GetNetCode() ),
std::forward_as_tuple( nets.size(), aNet->GetNetname() ) )
.first->second;
nets.push_back( &net );
//TODO: netname check
}
}
void EDA_DATA::SUB_NET::Write( std::ostream& ost ) const
{
ost << "SNT ";
WriteSubnet( ost );
ost << std::endl;
for( const auto& fid : feature_ids )
{
fid.Write( ost );
}
}
void EDA_DATA::FEATURE_ID::Write( std::ostream& ost ) const
{
static const std::map<TYPE, std::string> type_map = {
{ TYPE::COPPER, "C" },
{ TYPE::HOLE, "H" },
};
ost << "FID " << type_map.at( type ) << " " << layer << " " << feature_id << std::endl;
}
void EDA_DATA::SUB_NET_VIA::WriteSubnet( std::ostream& ost ) const
{
ost << "VIA";
}
void EDA_DATA::SUB_NET_TRACE::WriteSubnet( std::ostream& ost ) const
{
ost << "TRC";
}
void EDA_DATA::SUB_NET_PLANE::WriteSubnet( std::ostream& ost ) const
{
static const std::map<FILL_TYPE, std::string> fill_type_map = { { FILL_TYPE::SOLID, "S" },
{ FILL_TYPE::OUTLINE, "O" } };
static const std::map<CUTOUT_TYPE, std::string> cutout_type_map = {
{ CUTOUT_TYPE::CIRCLE, "C" },
{ CUTOUT_TYPE::RECT, "R" },
{ CUTOUT_TYPE::OCTAGON, "O" },
{ CUTOUT_TYPE::EXACT, "E" }
};
ost << "PLN " << fill_type_map.at( fill_type ) << " " << cutout_type_map.at( cutout_type )
<< " " << fill_size;
}
void EDA_DATA::SUB_NET_TOEPRINT::WriteSubnet( std::ostream& ost ) const
{
static const std::map<SIDE, std::string> side_map = {
{ SIDE::BOTTOM, "B" },
{ SIDE::TOP, "T" },
};
ost << "TOP " << side_map.at( side ) << " " << comp_num << " " << toep_num;
}
void EDA_DATA::SUB_NET::AddFeatureID( FEATURE_ID::TYPE type, const wxString& layer,
size_t feature_id )
{
feature_ids.emplace_back( type, m_edadata->GetLyrIdx( layer ), feature_id );
}
size_t EDA_DATA::GetLyrIdx( const wxString& aLayer )
{
if( layers_map.count( aLayer ) )
{
return layers_map.at( aLayer );
}
else
{
auto idx = layers_map.size();
layers_map.emplace( aLayer, idx );
layers.push_back( aLayer );
return idx;
}
}
void OUTLINE_SQUARE::Write( std::ostream& ost ) const
{
ost << "SQ " << ODB::Data2String( m_center.x ) << " " << ODB::Data2String( m_center.y ) << " "
<< ODB::Data2String( m_halfSide ) << std::endl;
}
void OUTLINE_CIRCLE::Write( std::ostream& ost ) const
{
ost << "CR " << ODB::Data2String( m_center.x ) << " " << ODB::Data2String( m_center.y ) << " "
<< ODB::Data2String( m_radius ) << std::endl;
}
void OUTLINE_RECT::Write( std::ostream& ost ) const
{
ost << "RC " << ODB::Data2String( m_lower_left.x ) << " " << ODB::Data2String( m_lower_left.y )
<< " " << ODB::Data2String( m_width ) << " " << ODB::Data2String( m_height ) << std::endl;
}
void OUTLINE_CONTOUR::Write( std::ostream& ost ) const
{
ost << "CT" << std::endl;
m_surfaces->WriteData( ost );
ost << "CE" << std::endl;
}
void EDA_DATA::AddPackage( const FOOTPRINT* aFp )
{
// ODBPP only need unique PACKAGE in PKG record in eda/data file.
// the PKG index can repeat to be ref in CMP record in component file.
std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( aFp->Clone() ) );
m_eda_footprints.emplace_back( fp );
fp->SetParentGroup( nullptr );
fp->SetPosition( { 0, 0 } );
if( fp->GetLayer() != F_Cu )
fp->Flip( fp->GetPosition(), false );
fp->SetOrientation( ANGLE_0 );
size_t hash = hash_fp_item( fp.get(), HASH_POS | REL_COORD );
size_t pkg_index = packages_map.size();
wxString fp_name = fp->GetFPID().GetLibItemName().wx_str();
auto [iter, success] = packages_map.emplace( hash, PACKAGE( pkg_index, fp_name ) );
if( !success )
{
return;
}
PACKAGE* pkg = &( iter->second );
packages.push_back( pkg );
BOX2I bbox = fp->GetBoundingBox();
pkg->m_xmin = bbox.GetPosition().x;
pkg->m_ymin = bbox.GetPosition().y;
pkg->m_xmax = bbox.GetEnd().x;
pkg->m_ymax = bbox.GetEnd().y;
pkg->m_pitch = UINT64_MAX;
if( fp->Pads().size() < 2 )
pkg->m_pitch = pcbIUScale.mmToIU( 1.0 ); // placeholder value
for( size_t i = 0; i < fp->Pads().size(); ++i )
{
const PAD* pad1 = fp->Pads()[i];
for( size_t j = i + 1; j < fp->Pads().size(); ++j )
{
const PAD* pad2 = fp->Pads()[j];
const uint64_t pin_dist = ( pad1->GetCenter() - pad2->GetCenter() ).EuclideanNorm();
pkg->m_pitch = std::min( pkg->m_pitch, pin_dist );
}
}
const SHAPE_POLY_SET& courtyard = fp->GetCourtyard( F_CrtYd );
const SHAPE_POLY_SET& courtyard_back = fp->GetCourtyard( B_CrtYd );
SHAPE_POLY_SET pkg_outline;
if( courtyard.OutlineCount() > 0 )
pkg_outline = courtyard;
if( courtyard_back.OutlineCount() > 0 )
{
pkg_outline = courtyard_back;
}
if( !courtyard.OutlineCount() && !courtyard_back.OutlineCount() )
{
pkg_outline = fp->GetBoundingHull();
}
// TODO: Here we put rect, square, and circle, all as polygon
if( pkg_outline.OutlineCount() > 0 )
{
for( int ii = 0; ii < pkg_outline.OutlineCount(); ++ii )
{
pkg->m_pkgOutlines.push_back(
std::make_unique<OUTLINE_CONTOUR>( pkg_outline.Polygon( ii ) ) );
}
}
for( size_t i = 0; i < fp->Pads().size(); ++i )
{
const PAD* pad = fp->Pads()[i];
pkg->AddPin( pad, i );
}
return;
}
void EDA_DATA::PACKAGE::AddPin( const PAD* aPad, size_t aPinNum )
{
wxString name = aPad->GetNumber();
// Pins are required to have names, so if our pad doesn't have a name, we need to
// generate one that is unique
if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
name = wxString::Format( "NPTH%zu", aPinNum );
else if( name.empty() )
name = wxString::Format( "PAD%zu", aPinNum );
// // for SNT record, pad, net, pin
std::shared_ptr<PIN> pin = std::make_shared<PIN>( m_pinsVec.size(), name );
m_pinsVec.push_back( pin );
VECTOR2D relpos = aPad->GetFPRelativePosition();
// TODO: is odb pkg pin center means center of pad hole or center of pad shape?
if( aPad->GetOffset().x != 0 || aPad->GetOffset().y != 0 )
relpos += aPad->GetOffset();
pin->m_center = ODB::AddXY( relpos );
if( aPad->HasHole() )
{
pin->type = PIN::TYPE::THROUGH_HOLE;
}
else
{
pin->type = PIN::TYPE::SURFACE;
}
if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
pin->etype = PIN::ELECTRICAL_TYPE::MECHANICAL;
else if( aPad->IsOnCopperLayer() )
pin->etype = PIN::ELECTRICAL_TYPE::ELECTRICAL;
else
pin->etype = PIN::ELECTRICAL_TYPE::UNDEFINED;
if( ( aPad->HasHole() && aPad->IsOnCopperLayer() ) || aPad->GetAttribute() == PAD_ATTRIB::PTH )
{
pin->mtype = PIN::MOUNT_TYPE::THROUGH_HOLE;
}
else if( aPad->HasHole() && aPad->GetAttribute() == PAD_ATTRIB::NPTH )
{
pin->mtype = PIN::MOUNT_TYPE::HOLE;
}
else if( aPad->GetAttribute() == PAD_ATTRIB::SMD )
{
pin->mtype = PIN::MOUNT_TYPE::SMT;
}
else
{
pin->mtype = PIN::MOUNT_TYPE::UNDEFINED;
}
const std::shared_ptr<SHAPE_POLY_SET>& polygons = aPad->GetEffectivePolygon( ERROR_INSIDE );
// TODO: Here we put all pad shapes as polygonl, we should switch by pad shape
// Note:pad only use polygons->Polygon(0),
if( polygons->OutlineCount() > 0 )
{
pin->m_pinOutlines.push_back( std::make_unique<OUTLINE_CONTOUR>( polygons->Polygon( 0 ) ) );
}
}
void EDA_DATA::PIN::Write( std::ostream& ost ) const
{
static const std::map<TYPE, std::string> type_map = { { TYPE::SURFACE, "S" },
{ TYPE::THROUGH_HOLE, "T" },
{ TYPE::BLIND, "B" } };
static const std::map<ELECTRICAL_TYPE, std::string> etype_map = {
{ ELECTRICAL_TYPE::ELECTRICAL, "E" },
{ ELECTRICAL_TYPE::MECHANICAL, "M" },
{ ELECTRICAL_TYPE::UNDEFINED, "U" }
};
static const std::map<MOUNT_TYPE, std::string> mtype_map = { { MOUNT_TYPE::THROUGH_HOLE, "T" },
{ MOUNT_TYPE::HOLE, "H" },
{ MOUNT_TYPE::SMT, "S" },
{ MOUNT_TYPE::UNDEFINED, "U" } };
ost << "PIN " << m_name << " " << type_map.at( type ) << " " << m_center.first << " "
<< m_center.second << " 0 " << etype_map.at( etype ) << " " << mtype_map.at( mtype )
<< std::endl;
for( const auto& outline : m_pinOutlines )
{
outline->Write( ost );
}
}
void EDA_DATA::PACKAGE::Write( std::ostream& ost ) const
{
ost << "PKG " << m_name << " " << ODB::Data2String( m_pitch ) << " "
<< ODB::Data2String( m_xmin ) << " " << ODB::Data2String( m_ymin ) << " "
<< ODB::Data2String( m_xmax ) << " " << ODB::Data2String( m_ymax ) << std::endl;
for( const auto& outline : m_pkgOutlines )
{
outline->Write( ost );
}
for( const auto& pin : m_pinsVec )
{
pin->Write( ost );
}
}
void EDA_DATA::Write( std::ostream& ost ) const
{
ost << "# " << wxDateTime::Now().FormatISOCombined() << std::endl;
ost << "UNITS=" << PCB_IO_ODBPP::m_unitsStr << std::endl;
ost << "LYR";
for( const auto& layer : layers )
{
ost << " " << layer;
}
ost << std::endl;
WriteAttributes( ost, "#" );
for( const auto& net : nets )
{
ost << "#NET " << net->m_index << std::endl;
net->Write( ost );
}
size_t i = 0;
for( const auto* pkg : packages )
{
ost << "# PKG " << i << std::endl;
i++;
pkg->Write( ost );
ost << "#" << std::endl;
}
}

View File

@ -0,0 +1,340 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ODB_EDA_DATA_H_
#define _ODB_EDA_DATA_H_
#include <list>
#include <memory>
#include "odb_attribute.h"
#include "odb_feature.h"
class PKG_OUTLINE;
class EDA_DATA : public ATTR_MANAGER
{
public:
EDA_DATA();
void Write( std::ostream& ost ) const;
size_t GetLyrIdx( const wxString& aLayerName );
std::vector<std::shared_ptr<FOOTPRINT>> GetEdaFootprints() const { return m_eda_footprints; }
class FEATURE_ID
{
friend EDA_DATA;
public:
enum class TYPE
{
COPPER,
LAMINATE,
HOLE
};
FEATURE_ID( TYPE t, size_t l, size_t fid ) : type( t ), layer( l ), feature_id( fid ) {}
TYPE type;
size_t layer;
size_t feature_id;
void Write( std::ostream& ost ) const;
};
class SUB_NET
{
public:
SUB_NET( size_t aIndex, EDA_DATA* aEda ) : m_index( aIndex ), m_edadata( aEda ) {}
const size_t m_index;
void Write( std::ostream& ost ) const;
std::list<FEATURE_ID> feature_ids;
void AddFeatureID( FEATURE_ID::TYPE type, const wxString& layer, size_t feature_id );
virtual ~SUB_NET() {}
protected:
virtual void WriteSubnet( std::ostream& ost ) const = 0;
EDA_DATA* m_edadata;
};
class SUB_NET_VIA : public SUB_NET
{
public:
SUB_NET_VIA( size_t aIndex, EDA_DATA* aEda ) : SUB_NET( aIndex, aEda ) {}
void WriteSubnet( std::ostream& ost ) const override;
};
class SUB_NET_TRACE : public SUB_NET
{
public:
SUB_NET_TRACE( size_t aIndex, EDA_DATA* aEda ) : SUB_NET( aIndex, aEda ) {}
void WriteSubnet( std::ostream& ost ) const override;
};
class SUB_NET_PLANE : public SUB_NET
{
public:
enum class FILL_TYPE
{
SOLID,
OUTLINE
};
enum class CUTOUT_TYPE
{
CIRCLE,
RECT,
OCTAGON,
EXACT
};
SUB_NET_PLANE( size_t aIndex, EDA_DATA* aEda, FILL_TYPE aFill, CUTOUT_TYPE aCutout,
size_t aFillSize ) :
SUB_NET( aIndex, aEda ), fill_type( aFill ), cutout_type( aCutout ),
fill_size( aFillSize )
{
}
FILL_TYPE fill_type;
CUTOUT_TYPE cutout_type;
size_t fill_size;
void WriteSubnet( std::ostream& ost ) const override;
};
class SUB_NET_TOEPRINT : public SUB_NET
{
public:
enum class SIDE
{
TOP,
BOTTOM
};
SUB_NET_TOEPRINT( size_t aIndex, EDA_DATA* aEda, SIDE aSide, size_t aCompNum,
size_t aToepNum ) :
SUB_NET( aIndex, aEda ), side( aSide ), comp_num( aCompNum ), toep_num( aToepNum )
{
}
~SUB_NET_TOEPRINT() {}
SIDE side;
size_t comp_num;
size_t toep_num;
void WriteSubnet( std::ostream& ost ) const override;
};
class NET : public ATTR_RECORD_WRITER
{
public:
NET( size_t aIndex, const wxString& aName ) : m_index( aIndex ), m_name( aName ) {}
const size_t m_index;
wxString m_name;
std::list<std::unique_ptr<SUB_NET>> subnets;
template <typename T, typename... Args>
T& AddSubnet( Args&&... args )
{
auto f = std::make_unique<T>( subnets.size(), std::forward<Args>( args )... );
auto& r = *f;
subnets.push_back( std::move( f ) );
return r;
}
void Write( std::ostream& ost ) const;
};
void AddNET( const NETINFO_ITEM* aNet );
NET& GetNet( size_t aNetcode ) { return nets_map.at( aNetcode ); }
class PIN
{
public:
PIN( const size_t aIndex, const wxString& aName ) : m_index( aIndex ), m_name( aName ) {}
const size_t m_index;
wxString m_name;
std::pair<wxString, wxString> m_center;
enum class TYPE
{
THROUGH_HOLE,
BLIND,
SURFACE
};
TYPE type = TYPE::SURFACE;
enum class ELECTRICAL_TYPE
{
ELECTRICAL,
MECHANICAL,
UNDEFINED
};
ELECTRICAL_TYPE etype = ELECTRICAL_TYPE::UNDEFINED;
enum class MOUNT_TYPE
{
SMT,
SMT_RECOMMENDED,
THROUGH_HOLE,
THROUGH_RECOMMENDED,
PRESSFIT,
NON_BOARD,
HOLE,
UNDEFINED
};
MOUNT_TYPE mtype = MOUNT_TYPE::UNDEFINED;
std::list<std::unique_ptr<PKG_OUTLINE>> m_pinOutlines;
void Write( std::ostream& ost ) const;
};
class PACKAGE : public ATTR_RECORD_WRITER
{
public:
PACKAGE( const size_t aIndex, const wxString& afpName ) :
m_index( aIndex ), m_name( afpName )
{
}
const size_t m_index; /// <! Reference number of the package to be used in CMP.
wxString m_name;
size_t m_pitch;
int64_t m_xmin, m_ymin, m_xmax, m_ymax; // Box points: leftlow, rightup
std::list<std::unique_ptr<PKG_OUTLINE>> m_pkgOutlines;
void AddPin( const PAD* aPad, size_t aPinNum );
const std::shared_ptr<PIN> GetEdaPkgPin( size_t aPadIndex ) const
{
return m_pinsVec.at( aPadIndex );
}
void Write( std::ostream& ost ) const;
private:
std::vector<std::shared_ptr<PIN>> m_pinsVec;
};
void AddPackage( const FOOTPRINT* aFp );
const PACKAGE& GetPackage( size_t aHash ) const { return packages_map.at( aHash ); }
private:
std::map<size_t, NET> nets_map;
std::list<const NET*> nets;
std::map<size_t, PACKAGE> packages_map; //hash value, package
std::list<const PACKAGE*> packages;
std::map<wxString, size_t> layers_map;
std::vector<wxString> layers;
std::vector<std::shared_ptr<FOOTPRINT>> m_eda_footprints;
};
class PKG_OUTLINE
{
public:
virtual void Write( std::ostream& ost ) const = 0;
virtual ~PKG_OUTLINE() = default;
};
class OUTLINE_RECT : public PKG_OUTLINE
{
public:
OUTLINE_RECT( const VECTOR2I& aLowerLeft, size_t aWidth, size_t aHeight ) :
m_lower_left( aLowerLeft ), m_width( aWidth ), m_height( aHeight )
{
}
OUTLINE_RECT( const BOX2I& aBox ) :
OUTLINE_RECT( aBox.GetPosition(), aBox.GetWidth(), aBox.GetHeight() )
{
}
VECTOR2I m_lower_left;
size_t m_width;
size_t m_height;
void Write( std::ostream& ost ) const override;
};
class ODB_SURFACE_DATA;
class OUTLINE_CONTOUR : public PKG_OUTLINE
{
public:
OUTLINE_CONTOUR( const SHAPE_POLY_SET::POLYGON& aPolygon,
FILL_T aFillType = FILL_T::FILLED_SHAPE )
{
if( !aPolygon.empty() && aPolygon[0].PointCount() >= 3 )
{
m_surfaces = std::make_unique<ODB_SURFACE_DATA>( aPolygon );
if( aFillType != FILL_T::NO_FILL )
{
m_surfaces->AddPolygonHoles( aPolygon );
}
}
}
std::unique_ptr<ODB_SURFACE_DATA> m_surfaces;
void Write( std::ostream& ost ) const override;
};
class OUTLINE_SQUARE : public PKG_OUTLINE
{
public:
OUTLINE_SQUARE( const VECTOR2I& aCenter, size_t aHalfSide ) :
m_center( aCenter ), m_halfSide( aHalfSide )
{
}
VECTOR2I m_center;
size_t m_halfSide;
void Write( std::ostream& ost ) const override;
};
class OUTLINE_CIRCLE : public PKG_OUTLINE
{
public:
OUTLINE_CIRCLE( const VECTOR2I& aCenter, size_t aRadius ) :
m_center( aCenter ), m_radius( aRadius )
{
}
VECTOR2I m_center;
size_t m_radius;
void Write( std::ostream& ost ) const override;
};
#endif // _ODB_EDA_DATA_H_

View File

@ -0,0 +1,975 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <base_units.h>
#include <board_stackup_manager/stackup_predefined_prms.h>
#include <build_version.h>
#include <callback_gal.h>
#include <connectivity/connectivity_data.h>
#include <connectivity/connectivity_algo.h>
#include <convert_basic_shapes_to_polygon.h>
#include <font/font.h>
#include <footprint.h>
#include <hash_eda.h>
#include <pad.h>
#include <pcb_dimension.h>
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_track.h>
#include <pcbnew_settings.h>
#include <board_design_settings.h>
#include <pgm_base.h>
#include <progress_reporter.h>
#include <settings/settings_manager.h>
#include <wx_fstream_progress.h>
#include <geometry/shape_circle.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_segment.h>
#include <wx/log.h>
#include <wx/numformatter.h>
#include <wx/mstream.h>
#include "odb_attribute.h"
#include "odb_entity.h"
#include "odb_defines.h"
#include "odb_feature.h"
#include "odb_util.h"
#include "pcb_io_odbpp.h"
bool ODB_ENTITY_BASE::CreateDirectiryTree( ODB_TREE_WRITER& writer )
{
try
{
writer.CreateEntityDirectory( writer.GetRootPath(), GetEntityName() );
return true;
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
return false;
}
}
ODB_MISC_ENTITY::ODB_MISC_ENTITY()
{
m_info = { { wxS( ODB_JOB_NAME ), wxS( "job" ) },
{ wxS( ODB_UNITS ), PCB_IO_ODBPP::m_unitsStr },
{ wxS( "ODB_VERSION_MAJOR" ), wxS( "8" ) },
{ wxS( "ODB_VERSION_MINOR" ), wxS( "1" ) },
{ wxS( "ODB_SOURCE" ), wxS( "KiCad EDA" + GetMajorMinorPatchVersion() ) },
{ wxS( "CREATION_DATE" ), wxDateTime::Now().FormatISOCombined() },
{ wxS( "SAVE_DATE" ), wxDateTime::Now().FormatISOCombined() },
{ wxS( "SAVE_APP" ), wxS( "Pcbnew" ) },
{ wxS( "SAVE_USER" ), wxS( "" ) },
{ wxS( "MAX_UID" ), wxS( "" ) } };
}
void ODB_MISC_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "info" );
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
for( auto& info : m_info )
{
twriter.WriteEquationLine( info.first, info.second );
}
}
void ODB_MATRIX_ENTITY::AddStep( const wxString& aStepName )
{
m_matrixSteps.emplace( aStepName.Upper(), m_col++ );
}
void ODB_MATRIX_ENTITY::InitEntityData()
{
AddStep( "PCB" );
InitMatrixLayerData();
}
void ODB_MATRIX_ENTITY::InitMatrixLayerData()
{
BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
stackup.SynchronizeWithBoard( &dsnSettings );
std::vector<BOARD_STACKUP_ITEM*> layers = stackup.GetList();
std::set<PCB_LAYER_ID> added_layers;
for( int i = 0; i < stackup.GetCount(); i++ )
{
BOARD_STACKUP_ITEM* stackup_item = layers.at( i );
for( int sublayer_id = 0; sublayer_id < stackup_item->GetSublayersCount(); sublayer_id++ )
{
wxString ly_name = stackup_item->GetLayerName();
if( ly_name.IsEmpty() )
{
if( IsValidLayer( stackup_item->GetBrdLayerId() ) )
ly_name = m_board->GetLayerName( stackup_item->GetBrdLayerId() );
if( ly_name.IsEmpty() && stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
ly_name = wxString::Format( "DIELECTRIC_%d",
stackup_item->GetDielectricLayerId() );
}
MATRIX_LAYER matrix( m_row++, ly_name );
if( stackup_item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
if( stackup_item->GetTypeName() == KEY_CORE )
matrix.m_diType.emplace( ODB_DIELECTRIC_TYPE::CORE );
else
matrix.m_diType.emplace( ODB_DIELECTRIC_TYPE::PREPREG );
matrix.m_type = ODB_TYPE::DIELECTRIC;
matrix.m_context = ODB_CONTEXT::BOARD;
matrix.m_polarity = ODB_POLARITY::POSITIVE;
m_matrixLayers.push_back( matrix );
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
continue;
}
else
{
added_layers.insert( stackup_item->GetBrdLayerId() );
AddMatrixLayerField( matrix, stackup_item->GetBrdLayerId() );
}
}
}
LSEQ layer_seq = m_board->GetEnabledLayers().Seq();
for( PCB_LAYER_ID layer : layer_seq )
{
if( added_layers.find( layer ) != added_layers.end() )
continue;
MATRIX_LAYER matrix( m_row++, m_board->GetLayerName( layer ) );
added_layers.insert( layer );
AddMatrixLayerField( matrix, layer );
}
AddDrillMatrixLayer();
AddCOMPMatrixLayer();
}
void ODB_MATRIX_ENTITY::AddMatrixLayerField( MATRIX_LAYER& aMLayer, PCB_LAYER_ID aLayer )
{
aMLayer.m_polarity = ODB_POLARITY::POSITIVE;
aMLayer.m_context = ODB_CONTEXT::BOARD;
switch( aLayer )
{
case F_Paste:
case B_Paste: aMLayer.m_type = ODB_TYPE::SOLDER_PASTE; break;
case F_SilkS:
case B_SilkS: aMLayer.m_type = ODB_TYPE::SILK_SCREEN; break;
case F_Mask:
case B_Mask: aMLayer.m_type = ODB_TYPE::SOLDER_MASK; break;
case B_CrtYd:
case F_CrtYd:
case Edge_Cuts:
case B_Fab:
case F_Fab:
case F_Adhes:
case B_Adhes:
case Dwgs_User:
case Cmts_User:
case Eco1_User:
case Eco2_User:
case Margin:
case User_1:
case User_2:
case User_3:
case User_4:
case User_5:
case User_6:
case User_7:
case User_8:
case User_9:
aMLayer.m_context = ODB_CONTEXT::MISC;
aMLayer.m_type = ODB_TYPE::DOCUMENT;
break;
default:
if( IsCopperLayer( aLayer ) )
{
aMLayer.m_type = ODB_TYPE::SIGNAL;
}
else
{
// Do not handle other layers :
aMLayer.m_type = ODB_TYPE::UNDEFINED;
m_row--;
}
break;
}
if( aMLayer.m_type != ODB_TYPE::UNDEFINED )
{
m_matrixLayers.push_back( aMLayer );
m_plugin->GetLayerNameList().emplace_back( std::make_pair( aLayer, aMLayer.m_layerName ) );
}
}
void ODB_MATRIX_ENTITY::AddDrillMatrixLayer()
{
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
m_plugin->GetSlotHolesMap();
bool has_pth_layer = false;
bool has_npth_layer = false;
for( BOARD_ITEM* item : m_board->Tracks() )
{
if( item->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
}
}
for( FOOTPRINT* fp : m_board->Footprints() )
{
// std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( it_fp->Clone() ) );
if( fp->IsFlipped() )
{
m_hasBotComp = true;
}
for( PAD* pad : fp->Pads() )
{
if( !has_pth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
has_pth_layer = true;
if( !has_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH )
has_npth_layer = true;
if( pad->HasHole() && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
else if( pad->HasHole() )
drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
}
// m_plugin->GetLoadedFootprintList().push_back( std::move( fp ) );
}
auto InitDrillMatrix =
[&]( const wxString& aHasPlated, std::pair<PCB_LAYER_ID, PCB_LAYER_ID> aLayerPair )
{
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", aHasPlated,
m_board->GetLayerName( aLayerPair.first ),
m_board->GetLayerName( aLayerPair.second ) );
MATRIX_LAYER matrix( m_row++, dLayerName );
matrix.m_type = ODB_TYPE::DRILL;
matrix.m_context = ODB_CONTEXT::BOARD;
matrix.m_polarity = ODB_POLARITY::POSITIVE;
matrix.m_span.emplace( std::make_pair(
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.first ) ),
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.second ) ) ) );
m_matrixLayers.push_back( matrix );
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
};
if( drill_layers.find( std::make_pair( F_Cu, B_Cu ) ) != drill_layers.end()
|| !slot_holes.empty() )
{
// for pad has hole
if( has_pth_layer )
InitDrillMatrix( "plated", std::make_pair( F_Cu, B_Cu ) );
if( has_npth_layer )
InitDrillMatrix( "non-plated", std::make_pair( F_Cu, B_Cu ) );
}
for( const auto& [layer_pair, vec] : drill_layers )
{
if( layer_pair != std::make_pair( F_Cu, B_Cu ) ) // pad has initialized above
InitDrillMatrix( "plated", layer_pair ); // for via
}
}
void ODB_MATRIX_ENTITY::AddCOMPMatrixLayer()
{
MATRIX_LAYER matrix( m_row++, "COMP_+_TOP" );
matrix.m_type = ODB_TYPE::COMPONENT;
matrix.m_context = ODB_CONTEXT::BOARD;
m_matrixLayers.push_back( matrix );
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
if( m_hasBotComp )
{
matrix.m_layerName = ODB::GenLegalEntityName( "COMP_+_BOT" );
matrix.m_rowNumber = m_row++;
m_matrixLayers.push_back( matrix );
m_plugin->GetLayerNameList().emplace_back(
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
}
}
void ODB_MATRIX_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "matrix" );
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
for( const auto& [step_name, column] : m_matrixSteps )
{
const auto array_proxy = twriter.MakeArrayProxy( "STEP" );
twriter.WriteEquationLine( "COL", column );
twriter.WriteEquationLine( "NAME", step_name );
}
for( const auto& layer : m_matrixLayers )
{
const auto array_proxy = twriter.MakeArrayProxy( "LAYER" );
twriter.WriteEquationLine( "ROW", layer.m_rowNumber );
twriter.write_line_enum( "CONTEXT", layer.m_context );
twriter.write_line_enum( "TYPE", layer.m_type );
if( layer.m_addType.has_value() )
{
twriter.write_line_enum( "ADD_TYPE", layer.m_addType.value() );
}
twriter.WriteEquationLine( "NAME", layer.m_layerName.Upper() );
twriter.WriteEquationLine( "OLD_NAME", wxEmptyString );
twriter.write_line_enum( "POLARITY", layer.m_polarity );
if( layer.m_diType.has_value() )
{
twriter.write_line_enum( "DIELECTRIC_TYPE", layer.m_diType.value() );
}
twriter.WriteEquationLine( "DIELECTRIC_NAME", wxEmptyString );
twriter.WriteEquationLine( "CU_TOP", wxEmptyString );
twriter.WriteEquationLine( "CU_BOTTOM", wxEmptyString );
twriter.WriteEquationLine( "REF", wxEmptyString );
if( layer.m_span.has_value() )
{
twriter.WriteEquationLine( "START_NAME", layer.m_span->first.Upper() );
twriter.WriteEquationLine( "END_NAME", layer.m_span->second.Upper() );
}
else
{
twriter.WriteEquationLine( "START_NAME", wxEmptyString );
twriter.WriteEquationLine( "END_NAME", wxEmptyString );
}
twriter.WriteEquationLine( "COLOR", wxEmptyString );
}
}
ODB_LAYER_ENTITY::ODB_LAYER_ENTITY( BOARD* aBoard, PCB_IO_ODBPP* aPlugin,
std::map<int, std::vector<BOARD_ITEM*>>& aMap,
const PCB_LAYER_ID& aLayerID, const wxString& aLayerName ) :
ODB_ENTITY_BASE( aBoard, aPlugin ), m_layerItems( aMap ), m_layerID( aLayerID ),
m_matrixLayerName( aLayerName )
{
m_featuresMgr = std::make_unique<FEATURES_MANAGER>( aBoard, aPlugin, aLayerName );
}
void ODB_LAYER_ENTITY::InitEntityData()
{
if( m_matrixLayerName.Contains( "drill" ) )
{
InitDrillData();
InitFeatureData();
return;
}
if( m_layerID != PCB_LAYER_ID::UNDEFINED_LAYER )
{
InitFeatureData();
}
}
void ODB_LAYER_ENTITY::InitFeatureData()
{
if( m_layerItems.empty() )
return;
const NETINFO_LIST& nets = m_board->GetNetInfo();
for( const NETINFO_ITEM* net : nets )
{
std::vector<BOARD_ITEM*>& vec = m_layerItems[net->GetNetCode()];
std::stable_sort( vec.begin(), vec.end(),
[]( BOARD_ITEM* a, BOARD_ITEM* b )
{
if( a->GetParentFootprint() == b->GetParentFootprint() )
return a->Type() < b->Type();
return a->GetParentFootprint() < b->GetParentFootprint();
} );
if( vec.empty() )
continue;
m_featuresMgr->InitFeatureList( m_layerID, vec );
}
}
ODB_COMPONENT& ODB_LAYER_ENTITY::InitComponentData( const FOOTPRINT* aFp,
const EDA_DATA::PACKAGE& aPkg )
{
if( m_matrixLayerName == "COMP_+_BOT" )
{
if( !m_compBot.has_value() )
{
m_compBot.emplace();
}
return m_compBot.value().AddComponent( aFp, aPkg );
}
else
{
if( !m_compTop.has_value() )
{
m_compTop.emplace();
}
return m_compTop.value().AddComponent( aFp, aPkg );
}
}
void ODB_LAYER_ENTITY::InitDrillData()
{
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
m_plugin->GetDrillLayerItemsMap();
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
m_plugin->GetSlotHolesMap();
if( !m_layerItems.empty() )
{
m_layerItems.clear();
}
m_tools.emplace( PCB_IO_ODBPP::m_unitsStr );
bool is_npth_layer = false;
wxString plated_name = "plated";
if( m_matrixLayerName.Contains( "non-plated" ) )
{
is_npth_layer = true;
plated_name = "non-plated";
}
for( const auto& [layer_pair, vec] : slot_holes )
{
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
m_board->GetLayerName( layer_pair.first ),
m_board->GetLayerName( layer_pair.second ) );
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
{
for( BOARD_ITEM* item : vec )
{
if( item->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( item );
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
{
continue;
}
m_tools.value().AddDrillTools(
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
ODB::SymDouble2String(
std::min( pad->GetDrillSizeX(), pad->GetDrillSizeY() ) ) );
// for drill features
m_layerItems[pad->GetNetCode()].push_back( item );
}
}
break;
}
}
for( const auto& [layer_pair, vec] : drill_layers )
{
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
m_board->GetLayerName( layer_pair.first ),
m_board->GetLayerName( layer_pair.second ) );
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
{
for( BOARD_ITEM* item : vec )
{
if( item->Type() == PCB_VIA_T && !is_npth_layer )
{
PCB_VIA* via = static_cast<PCB_VIA*>( item );
m_tools.value().AddDrillTools( "VIA",
ODB::SymDouble2String( via->GetDrillValue() ) );
// for drill features
m_layerItems[via->GetNetCode()].push_back( item );
}
else if( item->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( item );
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
{
continue;
}
m_tools.value().AddDrillTools(
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
ODB::SymDouble2String( pad->GetDrillSizeX() ) );
// for drill features
m_layerItems[pad->GetNetCode()].push_back( item );
}
}
break;
}
}
}
void ODB_STEP_ENTITY::InitEntityData()
{
MakeLayerEntity();
InitEdaData();
// Init Layer Entity Data
for( const auto& [layerName, layer_entity_ptr] : m_layerEntityMap )
{
layer_entity_ptr->InitEntityData();
}
}
void ODB_LAYER_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
GenAttrList( writer );
GenFeatures( writer );
if( m_compTop.has_value() || m_compBot.has_value() )
{
GenComponents( writer );
}
if( m_tools.has_value() )
{
GenTools( writer );
}
}
void ODB_LAYER_ENTITY::GenComponents( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "components" );
if( m_compTop.has_value() )
{
m_compTop->Write( fileproxy.GetStream() );
}
else if( m_compBot.has_value() )
{
m_compBot->Write( fileproxy.GetStream() );
}
}
void ODB_LAYER_ENTITY::GenFeatures( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "features" );
m_featuresMgr->GenerateFeatureFile( fileproxy.GetStream() );
}
void ODB_LAYER_ENTITY::GenAttrList( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "attrlist" );
}
void ODB_LAYER_ENTITY::GenTools( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "tools" );
m_tools.value().GenerateFile( fileproxy.GetStream() );
}
void ODB_STEP_ENTITY::InitEdaData()
{
//InitPackage
for( const FOOTPRINT* fp : m_board->Footprints() )
{
m_edaData.AddPackage( fp );
}
// for NET
const NETINFO_LIST& nets = m_board->GetNetInfo();
for( const NETINFO_ITEM* net : nets )
{
m_edaData.AddNET( net );
}
// for CMP
size_t j = 0;
for( const FOOTPRINT* fp : m_board->Footprints() )
{
wxString compName = ODB::GenLegalEntityName( "COMP_+_TOP" );
if( fp->IsFlipped() )
compName = ODB::GenLegalEntityName( "COMP_+_BOT" );
auto iter = m_layerEntityMap.find( compName );
if( iter == m_layerEntityMap.end() )
{
wxLogError( _( "Failed to add component data" ) );
return;
}
// ODBPP only need unique PACKAGE in PKG record in eda/data file.
// the PKG index can repeat to be ref in CMP record in component file.
std::shared_ptr<FOOTPRINT> fp_pkg = m_edaData.GetEdaFootprints().at( j );
++j;
const EDA_DATA::PACKAGE& eda_pkg =
m_edaData.GetPackage( hash_fp_item( fp_pkg.get(), HASH_POS | REL_COORD ) );
ODB_COMPONENT& comp = iter->second->InitComponentData( fp, eda_pkg );
for( int i = 0; i < fp->Pads().size(); ++i )
{
PAD* pad = fp->Pads()[i];
auto& eda_net = m_edaData.GetNet( pad->GetNetCode() );
auto& subnet = eda_net.AddSubnet<EDA_DATA::SUB_NET_TOEPRINT>(
&m_edaData,
fp->IsFlipped() ? EDA_DATA::SUB_NET_TOEPRINT::SIDE::BOTTOM
: EDA_DATA::SUB_NET_TOEPRINT::SIDE::TOP,
comp.m_index, comp.m_toeprints.size() );
m_plugin->GetPadSubnetMap().emplace( pad, &subnet );
const std::shared_ptr<EDA_DATA::PIN> pin = eda_pkg.GetEdaPkgPin( i );
const EDA_DATA::PIN& pin_ref = *pin;
auto& toep = comp.m_toeprints.emplace_back( pin_ref );
toep.m_net_num = eda_net.m_index;
toep.m_subnet_num = subnet.m_index;
toep.m_center = ODB::AddXY( pad->GetPosition() );
toep.m_rot = ODB::Double2String(
( ANGLE_360 - pad->GetOrientation() ).Normalize().AsDegrees() );
if( pad->IsFlipped() )
toep.m_mirror = wxT( "M" );
else
toep.m_mirror = wxT( "N" );
}
}
for( PCB_TRACK* track : m_board->Tracks() )
{
auto& eda_net = m_edaData.GetNet( track->GetNetCode() );
EDA_DATA::SUB_NET* subnet = nullptr;
if( track->Type() == PCB_VIA_T )
subnet = &( eda_net.AddSubnet<EDA_DATA::SUB_NET_VIA>( &m_edaData ) );
else
subnet = &( eda_net.AddSubnet<EDA_DATA::SUB_NET_TRACE>( &m_edaData ) );
m_plugin->GetViaTraceSubnetMap().emplace( track, subnet );
}
for( ZONE* zone : m_board->Zones() )
{
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
auto& eda_net = m_edaData.GetNet( zone->GetNetCode() );
auto& subnet = eda_net.AddSubnet<EDA_DATA::SUB_NET_PLANE>(
&m_edaData, EDA_DATA::SUB_NET_PLANE::FILL_TYPE::SOLID,
EDA_DATA::SUB_NET_PLANE::CUTOUT_TYPE::EXACT, 0 );
m_plugin->GetPlaneSubnetMap().emplace( std::piecewise_construct,
std::forward_as_tuple( layer, zone ),
std::forward_as_tuple( &subnet ) );
}
}
}
void ODB_STEP_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
wxString step_root = writer.GetCurrentPath();
writer.CreateEntityDirectory( step_root, "layers" );
GenerateLayerFiles( writer );
writer.CreateEntityDirectory( step_root, "eda" );
GenerateEdaFiles( writer );
writer.CreateEntityDirectory( step_root, "netlists/cadnet" );
GenerateNetlistsFiles( writer );
writer.SetCurrentPath( step_root );
GenerateProfileFile( writer );
GenerateStepHeaderFile( writer );
//TODO: system attributes
// GenerateAttrListFile( writer );
}
void ODB_STEP_ENTITY::GenerateProfileFile( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "profile" );
m_profile = std::make_unique<FEATURES_MANAGER>( m_board, m_plugin, wxEmptyString );
SHAPE_POLY_SET board_outline;
if( !m_board->GetBoardPolygonOutlines( board_outline ) )
{
wxLogError( "Failed to get board outline" );
}
if( !m_profile->AddContour( board_outline, 0 ) )
{
wxLogError( "Failed to add polygon to profile" );
}
m_profile->GenerateProfileFeatures( fileproxy.GetStream() );
}
void ODB_STEP_ENTITY::GenerateStepHeaderFile( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "stephdr" );
m_stephdr = {
{ ODB_UNITS, PCB_IO_ODBPP::m_unitsStr },
{ "X_DATUM", "0" },
{ "Y_DATUM", "0" },
{ "X_ORIGIN", "0" },
{ "Y_ORIGIN", "0" },
{ "TOP_ACTIVE", "0" },
{ "BOTTOM_ACTIVE", "0" },
{ "RIGHT_ACTIVE", "0" },
{ "LEFT_ACTIVE", "0" },
{ "AFFECTING_BOM", "" },
{ "AFFECTING_BOM_CHANGED", "0" },
};
ODB_TEXT_WRITER twriter( fileproxy.GetStream() );
for( const auto& [key, value] : m_stephdr )
{
twriter.WriteEquationLine( key, value );
}
}
void ODB_STEP_ENTITY::GenerateLayerFiles( ODB_TREE_WRITER& writer )
{
wxString layers_root = writer.GetCurrentPath();
for( auto& [layerName, layerEntity] : m_layerEntityMap )
{
writer.CreateEntityDirectory( layers_root, layerName );
layerEntity->GenerateFiles( writer );
}
}
void ODB_STEP_ENTITY::GenerateEdaFiles( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "data" );
m_edaData.Write( fileproxy.GetStream() );
}
void ODB_STEP_ENTITY::GenerateNetlistsFiles( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "netlist" );
m_netlist.Write( fileproxy.GetStream() );
}
bool ODB_STEP_ENTITY::CreateDirectiryTree( ODB_TREE_WRITER& writer )
{
try
{
writer.CreateEntityDirectory( writer.GetRootPath(), "steps" );
writer.CreateEntityDirectory( writer.GetCurrentPath(), GetEntityName() );
return true;
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
return false;
}
}
void ODB_STEP_ENTITY::MakeLayerEntity()
{
LSEQ layers = m_board->GetEnabledLayers().Seq();
const NETINFO_LIST& nets = m_board->GetNetInfo();
// To avoid the overhead of repeatedly cycling through the layers and nets,
// we pre-sort the board items into a map of layer -> net -> items
std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>>& elements =
m_plugin->GetLayerElementsMap();
std::for_each( m_board->Tracks().begin(), m_board->Tracks().end(),
[&layers, &elements]( PCB_TRACK* aTrack )
{
if( aTrack->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aTrack );
for( PCB_LAYER_ID layer : layers )
{
if( via->FlashLayer( layer ) )
elements[layer][via->GetNetCode()].push_back( via );
}
}
else
{
elements[aTrack->GetLayer()][aTrack->GetNetCode()].push_back( aTrack );
}
} );
std::for_each( m_board->Zones().begin(), m_board->Zones().end(),
[&elements]( ZONE* zone )
{
LSEQ zone_layers = zone->GetLayerSet().Seq();
for( PCB_LAYER_ID layer : zone_layers )
{
elements[layer][zone->GetNetCode()].push_back( zone );
}
} );
for( BOARD_ITEM* item : m_board->Drawings() )
{
if( BOARD_CONNECTED_ITEM* conn_it = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
elements[conn_it->GetLayer()][conn_it->GetNetCode()].push_back( conn_it );
else
elements[item->GetLayer()][0].push_back( item );
}
for( FOOTPRINT* fp : m_board->Footprints() )
{
for( PCB_FIELD* field : fp->GetFields() )
elements[field->GetLayer()][0].push_back( field );
for( BOARD_ITEM* item : fp->GraphicalItems() )
elements[item->GetLayer()][0].push_back( item );
for( PAD* pad : fp->Pads() )
{
LSEQ pad_layers = pad->GetLayerSet().Seq();
VECTOR2I margin;
for( PCB_LAYER_ID layer : pad_layers )
{
bool onCopperLayer = ( LSET::AllCuMask() & LSET( { layer } ) ).any();
bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & LSET( { layer } ) ).any();
bool onSolderPasteLayer =
( LSET( { F_Paste, B_Paste } ) & LSET( { layer } ) ).any();
if( onSolderMaskLayer )
margin.x = margin.y = pad->GetSolderMaskExpansion();
if( onSolderPasteLayer )
margin = pad->GetSolderPasteMargin();
VECTOR2I padPlotsSize = pad->GetSize() + margin * 2;
if( onCopperLayer && !pad->IsOnCopperLayer() )
continue;
if( onCopperLayer && !pad->FlashLayer( layer ) )
continue;
if( pad->GetShape() != PAD_SHAPE::CUSTOM
&& ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
continue;
elements[layer][pad->GetNetCode()].push_back( pad );
}
}
}
for( const auto& [layerID, layerName] : m_plugin->GetLayerNameList() )
{
std::shared_ptr<ODB_LAYER_ENTITY> layer_entity_ptr = std::make_shared<ODB_LAYER_ENTITY>(
m_board, m_plugin, elements[layerID], layerID, layerName );
m_layerEntityMap.emplace( layerName, layer_entity_ptr );
}
}

View File

@ -0,0 +1,273 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
* Author: SYSUEric <jzzhuang666@gmail.com>.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ODB_ENTITY_H_
#define _ODB_ENTITY_H_
#include <optional>
#include <vector>
#include <map>
#include <wx/string.h>
#include <iostream>
#include <functional>
#include "odb_feature.h"
#include "odb_eda_data.h"
#include "odb_netlist.h"
#include "odb_component.h"
class BOARD;
class ODB_TREE_WRITER;
class BOARD_ITEM;
class PCB_IO_ODBPP;
class ODB_ENTITY_BASE
{
public:
ODB_ENTITY_BASE( BOARD* aBoard, PCB_IO_ODBPP* aPlugin ) : m_board( aBoard ), m_plugin( aPlugin )
{
}
ODB_ENTITY_BASE() : m_board( nullptr ), m_plugin( nullptr ) {}
virtual ~ODB_ENTITY_BASE() = default;
virtual void GenerateFiles( ODB_TREE_WRITER& writer ) {}
virtual bool CreateDirectiryTree( ODB_TREE_WRITER& writer );
virtual std::string GetEntityName() = 0;
virtual void InitEntityData() {}
protected:
BOARD* m_board;
std::vector<std::string> m_fileName;
PCB_IO_ODBPP* m_plugin;
};
enum class ODB_SUBTYPE;
enum class ODB_POLARITY;
enum class ODB_CONTEXT;
enum class ODB_TYPE;
class ODB_MATRIX_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_MATRIX_ENTITY( BOARD* aBoard, PCB_IO_ODBPP* aPlugin ) : ODB_ENTITY_BASE( aBoard, aPlugin )
{
}
virtual ~ODB_MATRIX_ENTITY() = default;
inline virtual std::string GetEntityName() { return "matrix"; }
struct MATRIX_LAYER
{
std::optional<std::pair<wxString, wxString>> m_span; // !< start, end
std::optional<ODB_SUBTYPE> m_addType;
std::optional<ODB_DIELECTRIC_TYPE> m_diType;
uint32_t m_rowNumber;
wxString m_layerName;
ODB_CONTEXT m_context;
ODB_TYPE m_type;
ODB_POLARITY m_polarity = ODB_POLARITY::POSITIVE;
MATRIX_LAYER( uint32_t aRow, const wxString& aLayerName ) :
m_rowNumber( aRow ), m_layerName( ODB::GenLegalEntityName( aLayerName ) )
{
}
};
virtual void GenerateFiles( ODB_TREE_WRITER& writer );
virtual void InitEntityData();
void InitMatrixLayerData();
void AddStep( const wxString& aStepName );
void AddMatrixLayerField( MATRIX_LAYER& aMLayer, PCB_LAYER_ID aLayer );
void AddDrillMatrixLayer();
void AddCOMPMatrixLayer();
private:
std::map<wxString, unsigned int> m_matrixSteps;
std::vector<MATRIX_LAYER> m_matrixLayers;
unsigned int m_row = 1;
unsigned int m_col = 1;
bool m_hasBotComp = false;
};
class ODB_MISC_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_MISC_ENTITY();
virtual ~ODB_MISC_ENTITY() = default;
inline virtual std::string GetEntityName() { return "misc"; }
//TODO
// bool AddAttrList();
// bool AddSysAttrFiles();
virtual void GenerateFiles( ODB_TREE_WRITER& writer );
private:
std::map<wxString, wxString> m_info;
// ODB_ATTRLIST m_attrlist;
};
class FEATURES_MANAGER;
class ODB_LAYER_ENTITY;
class ODB_STEP_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_STEP_ENTITY( BOARD* aBoard, PCB_IO_ODBPP* aPlugin ) :
ODB_ENTITY_BASE( aBoard, aPlugin ), m_profile( nullptr ), m_netlist( aBoard )
{
}
virtual ~ODB_STEP_ENTITY() = default;
inline virtual std::string GetEntityName() { return "pcb"; }
void InitEdaData();
void InitPackage();
void InitNetListData();
void MakeLayerEntity();
bool AddNetList();
bool AddProfile();
bool AddStepHeader();
virtual bool CreateDirectiryTree( ODB_TREE_WRITER& writer );
virtual void InitEntityData();
void GenerateLayerFiles( ODB_TREE_WRITER& writer );
void GenerateEdaFiles( ODB_TREE_WRITER& writer );
void GenerateNetlistsFiles( ODB_TREE_WRITER& writer );
void GenerateProfileFile( ODB_TREE_WRITER& writer );
void GenerateStepHeaderFile( ODB_TREE_WRITER& writer );
virtual void GenerateFiles( ODB_TREE_WRITER& writer );
private:
// ODB_ATTRLIST m_attrList;
std::map<wxString, std::shared_ptr<ODB_LAYER_ENTITY>> m_layerEntityMap;
std::unique_ptr<FEATURES_MANAGER> m_profile;
EDA_DATA m_edaData;
std::unordered_map<wxString, wxString> m_stephdr;
ODB_NET_LIST m_netlist;
};
class ODB_LAYER_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_LAYER_ENTITY( BOARD* aBoard, PCB_IO_ODBPP* aPlugin,
std::map<int, std::vector<BOARD_ITEM*>>& aMap, const PCB_LAYER_ID& aLayerID,
const wxString& aLayerName );
virtual ~ODB_LAYER_ENTITY() = default;
inline virtual std::string GetEntityName() { return "layers"; }
virtual void InitEntityData();
void InitFeatureData();
ODB_COMPONENT& InitComponentData( const FOOTPRINT* aFp, const EDA_DATA::PACKAGE& aPkg );
void InitDrillData();
void AddLayerFeatures();
void GenAttrList( ODB_TREE_WRITER& writer );
void GenComponents( ODB_TREE_WRITER& writer );
void GenTools( ODB_TREE_WRITER& writer );
void GenFeatures( ODB_TREE_WRITER& writer );
virtual void GenerateFiles( ODB_TREE_WRITER& writer );
private:
std::map<int, std::vector<BOARD_ITEM*>> m_layerItems;
PCB_LAYER_ID m_layerID;
wxString m_matrixLayerName;
// ODB_ATTRLIST m_attrList;
std::optional<ODB_DRILL_TOOLS> m_tools;
std::optional<COMPONENTS_MANAGER> m_compTop;
std::optional<COMPONENTS_MANAGER> m_compBot;
std::unique_ptr<FEATURES_MANAGER> m_featuresMgr;
};
class ODB_SYMBOLS_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_SYMBOLS_ENTITY() = default;
virtual ~ODB_SYMBOLS_ENTITY() = default;
inline virtual std::string GetEntityName() { return "symbols"; }
//TODO
// virtual void GenerateFiles( ODB_TREE_WRITER& writer );
};
class ODB_FONTS_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_FONTS_ENTITY() = default;
virtual ~ODB_FONTS_ENTITY() = default;
inline virtual std::string GetEntityName() { return "fonts"; }
virtual void GenerateFiles( ODB_TREE_WRITER& writer );
};
class ODB_WHEELS_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_WHEELS_ENTITY() = default;
virtual ~ODB_WHEELS_ENTITY() = default;
inline virtual std::string GetEntityName() { return "wheels"; }
// TODO
// virtual void GenerateFiles( ODB_TREE_WRITER& writer );
};
class ODB_INPUT_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_INPUT_ENTITY() = default;
virtual ~ODB_INPUT_ENTITY() = default;
inline virtual std::string GetEntityName() { return "input"; }
// TODO
// virtual void GenerateFiles( ODB_TREE_WRITER &writer );
};
class ODB_USER_ENTITY : public ODB_ENTITY_BASE
{
public:
ODB_USER_ENTITY() = default;
virtual ~ODB_USER_ENTITY() = default;
inline virtual std::string GetEntityName() { return "user"; }
// TODO
// virtual void GenerateFiles( ODB_TREE_WRITER &writer );
};
#endif // _ODB_ENTITY_H_

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More