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
@ -528,6 +529,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
&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,
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

View File

@ -0,0 +1,349 @@
/*
* 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_FEATURE_H_
#define _ODB_FEATURE_H_
#include "odb_attribute.h"
#include "pad.h"
#include "convert_basic_shapes_to_polygon.h"
#include "footprint.h"
#include <list>
#include "math/vector2d.h"
#include "odb_defines.h"
enum class ODB_DIRECTION
{
CW,
CCW
};
class ODB_LINE;
class ODB_ARC;
class ODB_PAD;
class ODB_SURFACE;
class ODB_FEATURE;
class PCB_IO_ODBPP;
class PCB_VIA;
class FEATURES_MANAGER : public ATTR_MANAGER
{
public:
FEATURES_MANAGER( BOARD* aBoard, PCB_IO_ODBPP* aPlugin, const wxString& aLayerName ) :
m_board( aBoard ), m_plugin( aPlugin ), m_layerName( aLayerName )
{
}
virtual ~FEATURES_MANAGER() { m_featuresList.clear(); }
void InitFeatureList( PCB_LAYER_ID aLayer, std::vector<BOARD_ITEM*>& aItems );
void AddFeatureLine( const VECTOR2I& aStart, const VECTOR2I& aEnd, uint64_t aWidth );
void AddFeatureArc( const VECTOR2I& aStart, const VECTOR2I& aEnd, const VECTOR2I& aCenter,
uint64_t aWidth, ODB_DIRECTION aDirection );
void AddPadCircle( const VECTOR2I& aCenter, uint64_t aDiameter, const EDA_ANGLE& aAngle,
bool aMirror, double aResize = 1.0 );
void AddPadShape( const PAD& aPad, PCB_LAYER_ID aLayer );
void AddFeatureSurface( const SHAPE_POLY_SET::POLYGON& aPolygon,
FILL_T aFillType = FILL_T::FILLED_SHAPE );
void AddShape( const PCB_SHAPE& aShape, PCB_LAYER_ID aLayer = UNDEFINED_LAYER );
void AddVia( const PCB_VIA* aVia, PCB_LAYER_ID aLayer );
void AddViaDrillHole( const PCB_VIA* aVia, PCB_LAYER_ID aLayer );
bool AddContour( const SHAPE_POLY_SET& aPolySet, int aOutline = 0,
FILL_T aFillType = FILL_T::FILLED_SHAPE );
bool AddPolygon( const SHAPE_POLY_SET::POLYGON& aPolygon, FILL_T aFillType, int aWidth,
LINE_STYLE aDashType );
bool AddPolygonCutouts( const SHAPE_POLY_SET::POLYGON& aPolygon );
void GenerateFeatureFile( std::ostream& ost ) const;
void GenerateProfileFeatures( std::ostream& ost ) const;
private:
inline uint32_t AddCircleSymbol( const wxString& aDiameter )
{
return GetSymbolIndex( m_circleSymMap, "r" + aDiameter );
}
uint32_t AddRoundDonutSymbol( const wxString& aOuterDim, const wxString& aInnerDim )
{
wxString sym = "donut_r" + aOuterDim + ODB_DIM_X + aInnerDim;
return GetSymbolIndex( m_roundDonutSymMap, sym );
}
uint32_t AddRectSymbol( const wxString& aWidth, const wxString& aHeight )
{
wxString sym = "rect" + aWidth + ODB_DIM_X + aHeight;
return GetSymbolIndex( m_rectSymMap, sym );
}
uint32_t AddOvalSymbol( const wxString& aWidth, const wxString& aHeight )
{
wxString sym = "oval" + aWidth + ODB_DIM_X + aHeight;
return GetSymbolIndex( m_ovalSymMap, sym );
}
uint32_t AddRoundRectSymbol( const wxString& aWidth, const wxString& aHeight,
const wxString& aRadius )
{
wxString sym = "rect" + aWidth + ODB_DIM_X + aHeight + ODB_DIM_X + ODB_DIM_R + aRadius;
return GetSymbolIndex( m_roundRectSymMap, sym );
}
uint32_t AddRoundRectDonutSymbol( const wxString& aOuterWidth, const wxString& aOuterHeight,
const wxString& aLineWidth, const wxString& aRadius )
{
wxString sym = "donut_rc" + aOuterWidth + ODB_DIM_X + aOuterHeight + ODB_DIM_X + aLineWidth
+ ODB_DIM_X + ODB_DIM_R + aRadius;
return GetSymbolIndex( m_roundRectDonutSymMap, sym );
}
uint32_t AddChamferRectSymbol( const wxString& aWidth, const wxString& aHeight,
const wxString& aRadius, int aPositions )
{
wxString sym = "rect" + aWidth + ODB_DIM_X + aHeight + ODB_DIM_X + ODB_DIM_C + aRadius;
if( aPositions != RECT_CHAMFER_ALL )
{
sym += ODB_DIM_X;
if( aPositions & RECT_CHAMFER_TOP_RIGHT )
sym += "1";
if( aPositions & RECT_CHAMFER_TOP_LEFT )
sym += "2";
if( aPositions & RECT_CHAMFER_BOTTOM_LEFT )
sym += "3";
if( aPositions & RECT_CHAMFER_BOTTOM_RIGHT )
sym += "4";
}
return GetSymbolIndex( m_chamRectSymMap, sym );
}
uint32_t GetSymbolIndex( std::map<wxString, uint32_t>& aSymMap, const wxString& aKey )
{
if( aSymMap.count( aKey ) )
{
return aSymMap.at( aKey );
}
else
{
uint32_t index = m_symIndex;
m_symIndex++;
aSymMap.emplace( aKey, index );
m_allSymMap.emplace( index, aKey );
return index;
}
}
std::map<wxString, uint32_t> m_circleSymMap; // diameter -> symbol index
std::map<wxString, uint32_t> m_roundDonutSymMap;
std::map<wxString, uint32_t> m_padSymMap; // name -> symbol index
std::map<wxString, uint32_t> m_rectSymMap; // w,h -> symbol index
std::map<wxString, uint32_t> m_ovalSymMap; // w,h -> symbol index
std::map<wxString, uint32_t> m_roundRectSymMap;
std::map<wxString, uint32_t> m_roundRectDonutSymMap;
std::map<wxString, uint32_t> m_chamRectSymMap;
std::map<uint32_t, wxString> m_allSymMap;
template <typename T, typename... Args>
void AddFeature( Args&&... args )
{
auto feature = std::make_unique<T>( m_featuresList.size(), std::forward<Args>( args )... );
m_featuresList.emplace_back( std::move( feature ) );
}
inline PCB_IO_ODBPP* GetODBPlugin() { return m_plugin; }
BOARD* m_board;
PCB_IO_ODBPP* m_plugin;
wxString m_layerName;
uint32_t m_symIndex = 0;
std::list<std::unique_ptr<ODB_FEATURE>> m_featuresList;
std::map<BOARD_ITEM*, std::vector<uint32_t>> m_featureIDMap;
};
class ODB_FEATURE : public ATTR_RECORD_WRITER
{
public:
// template <typename T> using check_type = attribute::is_feature<T>;
ODB_FEATURE( uint32_t aIndex ) : m_index( aIndex ) {}
virtual void WriteFeatures( std::ostream& ost );
virtual ~ODB_FEATURE() = default;
protected:
enum class FEATURE_TYPE
{
LINE,
ARC,
PAD,
SURFACE
};
virtual FEATURE_TYPE GetFeatureType() = 0;
virtual void WriteRecordContent( std::ostream& ost ) = 0;
const uint32_t m_index;
};
class ODB_LINE : public ODB_FEATURE
{
public:
ODB_LINE( uint32_t aIndex, const std::pair<wxString, wxString>& aStart,
const std::pair<wxString, wxString>& aEnd, uint32_t aSym ) :
ODB_FEATURE( aIndex ), m_start( aStart ), m_end( aEnd ), m_symIndex( aSym )
{
}
inline virtual FEATURE_TYPE GetFeatureType() override { return FEATURE_TYPE::LINE; }
protected:
virtual void WriteRecordContent( std::ostream& ost ) override;
private:
std::pair<wxString, wxString> m_start;
std::pair<wxString, wxString> m_end;
uint32_t m_symIndex;
};
class ODB_ARC : public ODB_FEATURE
{
public:
inline virtual FEATURE_TYPE GetFeatureType() override { return FEATURE_TYPE::ARC; }
ODB_ARC( uint32_t aIndex, const std::pair<wxString, wxString>& aStart,
const std::pair<wxString, wxString>& aEnd,
const std::pair<wxString, wxString>& aCenter, uint32_t aSym,
ODB_DIRECTION aDirection ) :
ODB_FEATURE( aIndex ), m_start( aStart ), m_end( aEnd ), m_center( aCenter ),
m_symIndex( aSym ), m_direction( aDirection )
{
}
protected:
virtual void WriteRecordContent( std::ostream& ost ) override;
private:
std::pair<wxString, wxString> m_start;
std::pair<wxString, wxString> m_end;
std::pair<wxString, wxString> m_center;
uint32_t m_symIndex;
ODB_DIRECTION m_direction;
};
class ODB_PAD : public ODB_FEATURE
{
public:
ODB_PAD( uint32_t aIndex, const std::pair<wxString, wxString>& aCenter, uint32_t aSym,
EDA_ANGLE aAngle = ANGLE_0, bool aMirror = false, double aResize = 1.0 ) :
ODB_FEATURE( aIndex ), m_center( aCenter ), m_symIndex( aSym ), m_angle( aAngle ),
m_mirror( aMirror ), m_resize( aResize )
{
}
inline virtual FEATURE_TYPE GetFeatureType() override { return FEATURE_TYPE::PAD; }
protected:
virtual void WriteRecordContent( std::ostream& ost ) override;
private:
std::pair<wxString, wxString> m_center;
uint32_t m_symIndex;
EDA_ANGLE m_angle;
bool m_mirror;
double m_resize;
};
class ODB_SURFACE_DATA;
class ODB_SURFACE : public ODB_FEATURE
{
public:
ODB_SURFACE( uint32_t aIndex, const SHAPE_POLY_SET::POLYGON& aPolygon,
FILL_T aFillType = FILL_T::FILLED_SHAPE );
virtual ~ODB_SURFACE() = default;
inline virtual FEATURE_TYPE GetFeatureType() override { return FEATURE_TYPE::SURFACE; }
std::unique_ptr<ODB_SURFACE_DATA> m_surfaces;
protected:
virtual void WriteRecordContent( std::ostream& ost ) override;
};
class ODB_SURFACE_DATA
{
public:
ODB_SURFACE_DATA( const SHAPE_POLY_SET::POLYGON& aPolygon );
struct SURFACE_LINE
{
enum class LINE_TYPE
{
SEGMENT,
ARC
};
SURFACE_LINE() = default;
SURFACE_LINE( const VECTOR2I& aEnd ) : m_end( aEnd ) {}
SURFACE_LINE( const VECTOR2I& aEnd, const VECTOR2I& aCenter, ODB_DIRECTION aDirection ) :
m_end( aEnd ), m_type( LINE_TYPE::ARC ), m_center( aCenter ),
m_direction( aDirection )
{
}
VECTOR2I m_end;
LINE_TYPE m_type = LINE_TYPE::SEGMENT;
VECTOR2I m_center;
ODB_DIRECTION m_direction;
};
void AddPolygonHoles( const SHAPE_POLY_SET::POLYGON& aPolygon );
void WriteData( std::ostream& ost ) const;
std::vector<std::vector<SURFACE_LINE>> m_polygons;
};
#endif // _ODB_FEATURE_H_

View File

@ -0,0 +1,671 @@
/*
* 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 <string.h>
#include "odb_entity.h"
const std::string odb_fonts1 = R"(
XSIZE 0.302000
YSIZE 0.302000
OFFSET 0.000000
CHAR !
LINE 0.000000 0.000000 0.000000 0.200000 P R 0.012000
LINE 0.000000 -0.100000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR "
LINE -0.050000 0.100000 -0.050000 0.200000 P R 0.012000
LINE 0.050000 0.100000 0.050000 0.200000 P R 0.012000
ECHAR
CHAR #
LINE -0.050000 -0.100000 -0.050000 0.200000 P R 0.012000
LINE 0.050000 -0.100000 0.050000 0.200000 P R 0.012000
LINE -0.100000 0.000000 0.100000 0.000000 P R 0.012000
LINE -0.100000 0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR $
LINE 0.000000 -0.100000 0.000000 0.200000 P R 0.012000
LINE -0.100000 -0.050000 0.050000 -0.050000 P R 0.012000
LINE 0.050000 -0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.050000 0.050000 P R 0.012000
LINE -0.050000 0.050000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 -0.050000 0.150000 P R 0.012000
LINE -0.050000 0.150000 0.100000 0.150000 P R 0.012000
ECHAR
CHAR %
LINE -0.100000 -0.100000 0.100000 0.200000 P R 0.012000
LINE -0.075000 0.175000 -0.075000 0.175000 P R 0.012000
LINE 0.075000 -0.075000 0.075000 -0.075000 P R 0.012000
ECHAR
CHAR &
LINE 0.100000 -0.100000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.000000 0.150000 P R 0.012000
LINE 0.000000 0.150000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 -0.100000 0.000000 P R 0.012000
LINE -0.100000 0.000000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.100000 0.000000 P R 0.012000
ECHAR
CHAR '
LINE 0.000000 0.050000 0.050000 0.150000 P R 0.012000
LINE 0.025000 0.175000 0.025000 0.175000 P R 0.012000
ECHAR
CHAR (
LINE 0.050000 -0.100000 -0.050000 0.000000 P R 0.012000
LINE -0.050000 0.000000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.050000 0.200000 P R 0.012000
ECHAR
CHAR )
LINE -0.050000 -0.100000 0.050000 0.000000 P R 0.012000
LINE 0.050000 0.000000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 -0.050000 0.200000 P R 0.012000
ECHAR
CHAR *
LINE -0.100000 -0.050000 0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 0.100000 -0.050000 P R 0.012000
LINE 0.000000 -0.100000 0.000000 0.200000 P R 0.012000
ECHAR
CHAR +
LINE -0.100000 0.050000 0.100000 0.050000 P R 0.012000
LINE 0.000000 -0.050000 0.000000 0.150000 P R 0.012000
ECHAR
CHAR ,
LINE 0.000000 -0.100000 0.050000 0.000000 P R 0.012000
LINE 0.025000 0.025000 0.025000 0.025000 P R 0.012000
ECHAR
CHAR -
LINE -0.100000 0.050000 0.100000 0.050000 P R 0.012000
ECHAR
CHAR .
LINE 0.025000 -0.075000 0.025000 -0.075000 P R 0.012000
ECHAR
CHAR /
LINE -0.100000 -0.050000 0.100000 0.150000 P R 0.012000
ECHAR
CHAR 0
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 0.100000 0.150000 P R 0.012000
ECHAR
CHAR 1
LINE -0.050000 0.150000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.000000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
ECHAR
CHAR 2
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR 3
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE 0.100000 0.200000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 0.000000 0.050000 P R 0.012000
LINE 0.050000 0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR 4
LINE 0.050000 -0.100000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 -0.100000 0.000000 P R 0.012000
LINE -0.100000 0.000000 0.100000 0.000000 P R 0.012000
ECHAR
CHAR 5
LINE 0.100000 0.200000 -0.100000 0.200000 P R 0.012000
LINE -0.100000 0.200000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR 6
LINE 0.100000 0.200000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.100000 0.050000 P R 0.012000
ECHAR
CHAR 7
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE 0.100000 0.200000 -0.050000 -0.100000 P R 0.012000
ECHAR
CHAR 8
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE -0.050000 0.050000 0.050000 0.050000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE -0.100000 0.150000 -0.100000 0.100000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.100000 P R 0.012000
LINE -0.100000 0.000000 -0.100000 -0.050000 P R 0.012000
LINE 0.100000 0.000000 0.100000 -0.050000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE -0.050000 0.200000 -0.100000 0.150000 P R 0.012000
LINE 0.050000 0.050000 0.100000 0.100000 P R 0.012000
LINE -0.050000 0.050000 -0.100000 0.100000 P R 0.012000
LINE 0.050000 0.050000 0.100000 0.000000 P R 0.012000
LINE -0.050000 0.050000 -0.100000 0.000000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR 9
LINE -0.100000 -0.100000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 -0.050000 0.050000 P R 0.012000
LINE -0.050000 0.050000 0.100000 0.050000 P R 0.012000
ECHAR
CHAR :
LINE 0.000000 0.012000 0.000000 0.012000 P R 0.012000
LINE 0.000000 -0.025000 0.000000 -0.025000 P R 0.012000
ECHAR
CHAR ;
LINE 0.000000 0.025000 -0.050000 -0.100000 P R 0.012000
LINE 0.000000 0.025000 0.000000 0.025000 P R 0.012000
LINE 0.000000 0.175000 0.000000 0.175000 P R 0.012000
ECHAR
CHAR <
LINE 0.100000 0.200000 -0.050000 0.050000 P R 0.012000
LINE -0.050000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR =
LINE -0.100000 0.100000 0.100000 0.100000 P R 0.012000
LINE -0.100000 0.000000 0.100000 0.000000 P R 0.012000
ECHAR
CHAR >
LINE -0.100000 0.200000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.100000 -0.100000 P R 0.012000
ECHAR
CHAR ?
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 0.000000 0.000000 P R 0.012000
LINE 0.000000 -0.100000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR @
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.000000 0.000000 P R 0.012000
LINE 0.000000 0.000000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR A
LINE -0.100000 -0.100000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.050000 0.100000 0.050000 P R 0.012000
ECHAR
CHAR B
LINE -0.100000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.100000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.050000 0.050000 0.050000 P R 0.012000
ECHAR
CHAR C
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
ECHAR
CHAR D
LINE -0.100000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.100000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.050000 0.200000 P R 0.012000
ECHAR
CHAR E
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.050000 0.050000 0.050000 P R 0.012000
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
ECHAR
CHAR F
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE -0.100000 0.050000 0.050000 0.050000 P R 0.012000
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
ECHAR
CHAR G
LINE -0.050000 0.200000 0.100000 0.200000 P R 0.012000
LINE -0.050000 0.200000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.050000 P R 0.012000
ECHAR
CHAR H
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE 0.100000 0.200000 0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.050000 0.100000 0.050000 P R 0.012000
ECHAR
CHAR I
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.000000 0.200000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR J
LINE 0.100000 0.200000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR K
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE 0.100000 0.200000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR L
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR M
LINE -0.100000 -0.100000 -0.100000 0.200000 P R 0.012000
LINE -0.100000 0.200000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 0.100000 0.200000 P R 0.012000
LINE 0.100000 0.200000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR N
LINE -0.100000 -0.100000 -0.100000 0.200000 P R 0.012000
LINE -0.100000 0.200000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 0.100000 0.200000 P R 0.012000
ECHAR
CHAR O
LINE -0.100000 -0.050000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR P
LINE -0.100000 -0.100000 -0.100000 0.200000 P R 0.012000
LINE -0.100000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.100000 0.050000 P R 0.012000
ECHAR
CHAR Q
LINE -0.100000 -0.050000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE 0.000000 0.000000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR R
LINE -0.100000 -0.100000 -0.100000 0.200000 P R 0.012000
LINE -0.100000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE 0.100000 0.150000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.100000 0.050000 P R 0.012000
LINE -0.050000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR S
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.050000 0.050000 P R 0.012000
LINE 0.050000 0.050000 -0.050000 0.050000 P R 0.012000
LINE -0.050000 0.050000 -0.100000 0.100000 P R 0.012000
LINE -0.100000 0.100000 -0.100000 0.150000 P R 0.012000
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
ECHAR
CHAR T
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR U
LINE -0.100000 0.200000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.100000 0.200000 P R 0.012000
ECHAR
CHAR V
LINE -0.100000 0.200000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.100000 0.200000 P R 0.012000
ECHAR
CHAR W
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 0.100000 0.200000 P R 0.012000
ECHAR
CHAR X
LINE -0.100000 0.200000 0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 0.200000 P R 0.012000
ECHAR
CHAR Y
LINE -0.100000 0.200000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 0.100000 0.200000 P R 0.012000
LINE 0.000000 0.050000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR Z
LINE -0.100000 0.200000 0.100000 0.200000 P R 0.012000
LINE 0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR [
LINE 0.050000 0.200000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.050000 -0.100000 P R 0.012000
ECHAR)";
const std::string odb_fonts2 = R"(
CHAR \
LINE -0.100000 0.200000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR ]
LINE -0.050000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
ECHAR
CHAR ^
LINE -0.100000 0.100000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR _
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR `
LINE -0.050000 0.200000 0.000000 0.050000 P R 0.012000
ECHAR
CHAR a
LINE -0.050000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 0.000000 P R 0.012000
LINE -0.050000 0.000000 0.100000 0.000000 P R 0.012000
ECHAR
CHAR b
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 -0.100000 0.050000 P R 0.012000
ECHAR
CHAR c
LINE 0.100000 0.100000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR d
LINE 0.100000 0.200000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 0.100000 0.050000 P R 0.012000
ECHAR
CHAR e
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.000000 P R 0.012000
LINE 0.050000 0.000000 -0.100000 0.000000 P R 0.012000
ECHAR
CHAR f
LINE -0.050000 -0.100000 -0.050000 0.150000 P R 0.012000
LINE -0.050000 0.150000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.050000 0.200000 P R 0.012000
LINE 0.050000 0.200000 0.100000 0.150000 P R 0.012000
LINE -0.100000 0.050000 0.000000 0.050000 P R 0.012000
ECHAR
CHAR g
LINE -0.050000 -0.150000 0.050000 -0.150000 P R 0.012000
LINE 0.050000 -0.150000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.100000 0.000000 P R 0.012000
LINE -0.100000 0.000000 -0.050000 -0.050000 P R 0.012000
LINE -0.050000 -0.050000 0.100000 -0.050000 P R 0.012000
ECHAR
CHAR h
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.050000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR i
LINE 0.000000 0.100000 0.000000 -0.050000 P R 0.012000
LINE 0.000000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.000000 0.200000 0.000000 0.200000 P R 0.012000
ECHAR
CHAR j
LINE 0.050000 0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.000000 -0.150000 P R 0.012000
LINE 0.000000 -0.150000 -0.050000 -0.150000 P R 0.012000
LINE -0.050000 -0.150000 -0.100000 -0.100000 P R 0.012000
LINE 0.050000 0.200000 0.050000 0.200000 P R 0.012000
ECHAR
CHAR k
LINE -0.100000 0.200000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.000000 0.000000 0.000000 P R 0.012000
LINE 0.000000 0.000000 0.100000 -0.100000 P R 0.012000
LINE -0.050000 0.000000 0.050000 0.100000 P R 0.012000
ECHAR
CHAR l
LINE -0.050000 0.200000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.000000 -0.050000 P R 0.012000
LINE 0.000000 -0.050000 0.050000 -0.100000 P R 0.012000
ECHAR
CHAR m
LINE -0.100000 -0.100000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.000000 0.050000 P R 0.012000
LINE 0.000000 0.050000 0.000000 0.000000 P R 0.012000
LINE 0.000000 0.050000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR n
LINE -0.100000 0.100000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.000000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR o
LINE -0.100000 -0.050000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR p
LINE -0.100000 -0.150000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.100000 0.000000 P R 0.012000
LINE 0.100000 0.000000 0.050000 -0.050000 P R 0.012000
LINE 0.050000 -0.050000 -0.100000 -0.050000 P R 0.012000
ECHAR
CHAR q
LINE 0.100000 -0.150000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.100000 0.000000 P R 0.012000
LINE -0.100000 0.000000 -0.050000 -0.050000 P R 0.012000
LINE -0.050000 -0.050000 0.100000 -0.050000 P R 0.012000
ECHAR
CHAR r
LINE -0.100000 0.100000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 0.000000 0.000000 0.100000 P R 0.012000
LINE 0.000000 0.100000 0.050000 0.050000 P R 0.012000
ECHAR
CHAR s
LINE -0.100000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 -0.050000 0.050000 0.000000 P R 0.012000
LINE 0.050000 0.000000 -0.050000 0.000000 P R 0.012000
LINE -0.050000 0.000000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR t
LINE -0.050000 0.200000 -0.050000 -0.050000 P R 0.012000
LINE -0.050000 -0.050000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 -0.050000 P R 0.012000
LINE -0.100000 0.100000 0.050000 0.100000 P R 0.012000
ECHAR
CHAR u
LINE -0.100000 0.100000 -0.100000 -0.050000 P R 0.012000
LINE -0.100000 -0.050000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.100000 0.000000 P R 0.012000
LINE 0.100000 -0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR v
LINE -0.100000 0.100000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR w
LINE -0.100000 0.100000 -0.050000 -0.100000 P R 0.012000
LINE -0.050000 -0.100000 0.000000 0.000000 P R 0.012000
LINE 0.000000 0.000000 0.050000 -0.100000 P R 0.012000
LINE 0.050000 -0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR x
LINE -0.100000 0.100000 0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 0.100000 P R 0.012000
ECHAR
CHAR y
LINE -0.100000 0.100000 -0.100000 0.000000 P R 0.012000
LINE -0.100000 0.000000 -0.050000 -0.050000 P R 0.012000
LINE -0.050000 -0.050000 0.100000 -0.050000 P R 0.012000
LINE 0.100000 0.100000 0.100000 -0.100000 P R 0.012000
LINE 0.100000 -0.100000 0.050000 -0.150000 P R 0.012000
LINE 0.050000 -0.150000 -0.050000 -0.150000 P R 0.012000
ECHAR
CHAR z
LINE -0.100000 0.100000 0.100000 0.100000 P R 0.012000
LINE 0.100000 0.100000 -0.100000 -0.100000 P R 0.012000
LINE -0.100000 -0.100000 0.100000 -0.100000 P R 0.012000
ECHAR
CHAR {
LINE 0.050000 0.200000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 -0.050000 0.150000 P R 0.012000
LINE -0.050000 0.150000 -0.050000 0.100000 P R 0.012000
LINE -0.050000 0.100000 -0.100000 0.050000 P R 0.012000
LINE -0.100000 0.050000 -0.050000 0.000000 P R 0.012000
LINE -0.050000 0.000000 -0.050000 -0.050000 P R 0.012000
LINE -0.050000 -0.050000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 0.050000 -0.100000 P R 0.012000
ECHAR
CHAR |
LINE 0.000000 0.200000 0.000000 -0.100000 P R 0.012000
ECHAR
CHAR }
LINE -0.050000 0.200000 0.000000 0.200000 P R 0.012000
LINE 0.000000 0.200000 0.050000 0.150000 P R 0.012000
LINE 0.050000 0.150000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.050000 P R 0.012000
LINE 0.100000 0.050000 0.050000 0.000000 P R 0.012000
LINE 0.050000 0.000000 0.050000 -0.050000 P R 0.012000
LINE 0.050000 -0.050000 0.000000 -0.100000 P R 0.012000
LINE 0.000000 -0.100000 -0.050000 -0.100000 P R 0.012000
ECHAR
CHAR ~
LINE -0.100000 0.150000 -0.050000 0.200000 P R 0.012000
LINE -0.050000 0.200000 0.050000 0.100000 P R 0.012000
LINE 0.050000 0.100000 0.100000 0.150000 P R 0.012000
ECHAR)";
// just implementation here
void ODB_FONTS_ENTITY::GenerateFiles( ODB_TREE_WRITER& writer )
{
auto fileproxy = writer.CreateFileProxy( "standard" );
fileproxy.GetStream() << odb_fonts1 << odb_fonts2 << std::endl;
}

View File

@ -0,0 +1,285 @@
/*
* 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 <confirm.h>
#include <gestfich.h>
#include <kiface_base.h>
#include <pcb_edit_frame.h>
#include <trigo.h>
#include <build_version.h>
#include <macros.h>
#include <wildcards_and_files_ext.h>
#include <locale_io.h>
#include <board.h>
#include <board_design_settings.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_track.h>
#include <vector>
#include <cctype>
#include <odb_netlist.h>
#include <wx/filedlg.h>
#include <wx/log.h>
#include "odb_util.h"
// Compute the side code for a pad. Returns "" if there is no copper
std::string ODB_NET_LIST::ComputePadAccessSide( BOARD* aBoard, LSET aLayerMask )
{
// Non-copper is not interesting here
aLayerMask &= LSET::AllCuMask();
if( !aLayerMask.any() )
return "";
// Traditional TH pad
if( aLayerMask[F_Cu] && aLayerMask[B_Cu] )
return "B";
// Front SMD pad
if( aLayerMask[F_Cu] )
return "T";
// Back SMD pad
if( aLayerMask[B_Cu] )
return "D";
// Inner
for( int layer = In1_Cu; layer < B_Cu; ++layer )
{
if( aLayerMask[layer] )
return "I";
}
// This shouldn't happen
wxLogDebug( "Unhandled layer mask input when compute pad access side of ODB++ netlist file." );
return "";
}
void ODB_NET_LIST::InitPadNetPoints( BOARD* aBoard,
std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords )
{
VECTOR2I origin = aBoard->GetDesignSettings().GetAuxOrigin();
for( FOOTPRINT* footprint : aBoard->Footprints() )
{
for( PAD* pad : footprint->Pads() )
{
ODB_NET_RECORD net_point;
net_point.side = ComputePadAccessSide( aBoard, pad->GetLayerSet() );
// It could be a mask only pad, we only handle pads with copper here
if( !net_point.side.empty() && net_point.side != "I" )
{
if( pad->GetNetCode() == 0 )
net_point.netname = "$NONE$";
else
net_point.netname = pad->GetNetname();
// net_point.pin = pad->GetNumber();
net_point.refdes = footprint->GetReference();
const VECTOR2I& drill = pad->GetDrillSize();
net_point.hole = pad->HasHole();
if( !net_point.hole )
net_point.drill_radius = 0;
else
net_point.drill_radius = std::min( drill.x, drill.y );
net_point.smd = pad->GetAttribute() == PAD_ATTRIB::SMD
|| pad->GetAttribute() == PAD_ATTRIB::CONN;
net_point.is_via = false;
net_point.mechanical = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
net_point.x_location = pad->GetPosition().x - origin.x;
net_point.y_location = origin.y - pad->GetPosition().y;
net_point.x_size = pad->GetSize().x;
// Rule: round pads have y = 0
if( pad->GetShape() == PAD_SHAPE::CIRCLE )
net_point.y_size = net_point.x_size;
else
net_point.y_size = pad->GetSize().y;
// net_point.rotation = ( ANGLE_360 - pad->GetOrientation() ).Normalize().AsDegrees();
// if( net_point.rotation < 0 )
// net_point.rotation += 360;
// always output NET end point as net test point
net_point.epoint = "e";
// the value indicates which sides are *not* accessible
net_point.soldermask = 3;
if( pad->GetLayerSet()[F_Mask] )
net_point.soldermask &= ~1;
if( pad->GetLayerSet()[B_Mask] )
net_point.soldermask &= ~2;
aRecords[pad->GetNetCode()].push_back( net_point );
}
}
}
}
// Compute the side code for a via.
std::string ODB_NET_LIST::ComputeViaAccessSide( BOARD* aBoard, int top_layer, int bottom_layer )
{
// Easy case for through vias: top_layer is component, bottom_layer is
// solder, side code is Both
if( ( top_layer == F_Cu ) && ( bottom_layer == B_Cu ) )
return "B";
// Blind via, reachable from front, Top
if( top_layer == F_Cu )
return "T";
// Blind via, reachable from bottom, Down
if( bottom_layer == B_Cu )
return "D";
// It's a buried via, accessible from some inner layer, Inner
return "I";
}
void ODB_NET_LIST::InitViaNetPoints( BOARD* aBoard,
std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords )
{
VECTOR2I origin = aBoard->GetDesignSettings().GetAuxOrigin();
// Enumerate all the track segments and keep the vias
for( auto track : aBoard->Tracks() )
{
if( track->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( track );
PCB_LAYER_ID top_layer, bottom_layer;
via->LayerPair( &top_layer, &bottom_layer );
ODB_NET_RECORD net_point;
net_point.side = ComputeViaAccessSide( aBoard, top_layer, bottom_layer );
if( net_point.side != "I" )
{
NETINFO_ITEM* net = track->GetNet();
net_point.smd = false;
net_point.hole = true;
if( net->GetNetCode() == 0 )
net_point.netname = "$NONE$";
else
net_point.netname = net->GetNetname();
net_point.refdes = "VIA";
net_point.is_via = true;
net_point.drill_radius = via->GetDrillValue();
net_point.mechanical = false;
net_point.x_location = via->GetPosition().x - origin.x;
net_point.y_location = origin.y - via->GetPosition().y;
// via always has drill radius, Width and Height are 0
net_point.x_size = 0;
net_point.y_size = 0; // Round so height = 0
net_point.epoint = "e"; // only buried via is "m" net mid point
// the value indicates which sides are *not* accessible
net_point.soldermask = 3;
if( via->GetLayerSet()[F_Mask] )
net_point.soldermask &= ~1;
if( via->GetLayerSet()[B_Mask] )
net_point.soldermask &= ~2;
aRecords[net->GetNetCode()].push_back( net_point );
}
}
}
}
void ODB_NET_LIST::WriteNetPointRecords( std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords,
std::ostream& aStream )
{
aStream << "H optimize n staggered n" << std::endl;
for( const auto& [key, vec] : aRecords )
{
aStream << "$" << key << " " << ODB::GenLegalNetName( vec.front().netname ) << std::endl;
}
aStream << "#" << std::endl << "#Netlist points" << std::endl << "#" << std::endl;
for( const auto& [key, vec] : aRecords )
{
for( const auto& net_point : vec )
{
aStream << key << " ";
if( net_point.hole )
aStream << ODB::Data2String( net_point.drill_radius );
else
aStream << 0;
aStream << " " << ODB::Data2String( net_point.x_location ) << " "
<< ODB::Data2String( net_point.y_location ) << " " << net_point.side << " ";
if( !net_point.hole )
aStream << ODB::Data2String( net_point.x_size ) << " "
<< ODB::Data2String( net_point.y_size ) << " ";
std::string exp;
if( net_point.soldermask == 3 )
exp = "c";
else if( net_point.soldermask == 2 )
exp = "s";
else if( net_point.soldermask == 1 )
exp = "p";
else if( net_point.soldermask == 0 )
exp = "e";
aStream << net_point.epoint << " " << exp;
if( net_point.hole )
aStream << " staggered 0 0 0";
if( net_point.is_via )
aStream << " v";
aStream << std::endl;
}
}
}
void ODB_NET_LIST::Write( std::ostream& aStream )
{
std::map<size_t, std::vector<ODB_NET_RECORD>> net_point_records;
InitViaNetPoints( m_board, net_point_records );
InitPadNetPoints( m_board, net_point_records );
WriteNetPointRecords( net_point_records, aStream );
}

View File

@ -0,0 +1,77 @@
/*
* 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_NETLIST_H_
#define _ODB_NETLIST_H_
#include <wx/string.h>
// Structure for holding the ODB net point record.
struct ODB_NET_RECORD
{
bool smd;
bool hole;
bool is_via;
wxString netname;
std::string refdes;
int drill_radius;
bool mechanical;
std::string side; // B: Both, T: Top, D: Down
// All these in PCB units, will be output in decimils
int x_location;
int y_location;
// Width and height of non-drilled pads (only when radius = 0).
int x_size; // Width
int y_size; // Height
// int rotation;
std::string epoint; // e: net end point, m: net mid point
int soldermask; // !< e — Solder mask exposed point. soldermask = 0
// !< c — Solder mask covered point. = 3
// !< p — Solder mask covered point on top side of product model. = 1
// !< s — Solder mask covered point on bottom side of product model. = 2
};
class BOARD;
class ODB_NET_LIST
{
public:
ODB_NET_LIST( BOARD* aBoard ) : m_board( aBoard ) {}
virtual ~ODB_NET_LIST() {}
void Write( std::ostream& aStream );
private:
BOARD* m_board;
std::string ComputePadAccessSide( BOARD* aBoard, LSET aLayerMask );
std::string ComputeViaAccessSide( BOARD* aBoard, int top_layer, int bottom_layer );
void InitPadNetPoints( BOARD* aBoard, std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords );
void InitViaNetPoints( BOARD* aBoard, std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords );
/// Writes a list of records to the given output stream
void WriteNetPointRecords( std::map<size_t, std::vector<ODB_NET_RECORD>>& aRecords,
std::ostream& aStream );
};
#endif // _ODB_NETLIST_H_

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/>.
*/
#include <string>
#include <algorithm>
#include <locale>
#include "odb_util.h"
#include <wx/chartype.h>
#include <wx/dir.h>
#include "idf_helpers.h"
#include "odb_defines.h"
#include "pcb_io_odbpp.h"
namespace ODB
{
wxString GenODBString( const wxString& aStr )
{
wxString str;
for( size_t ii = 0; ii < aStr.Len(); ++ii )
{
// Rule: we can only use the standard ASCII, control excluded
wxUniChar ch = aStr[ii];
if( ch > 126 || !std::isgraph( static_cast<unsigned char>( ch ) ) )
ch = '?';
str += ch;
}
// Rule: only uppercase
str.MakeUpper();
return str;
}
wxString GenLegalNetName( const wxString& aStr )
{
std::string str = aStr.ToStdString();
wxString out;
out.reserve( str.size() );
for( auto c : str )
{
if( ( c >= 33 && c <= 126 ) && c != ';' )
{
out.append( 1, c );
}
else
{
out.append( 1, '_' ); // Replace invalid characters with underscore
}
}
return out;
}
// The names of these ODB++ entities must comply with
// the rules for legal entity names:
// product, model, step, layer, symbol, and attribute.
wxString GenLegalEntityName( const wxString& aStr )
{
std::string str = aStr.ToStdString();
wxString out;
out.reserve( str.size() );
for( auto c : str )
{
if( isalpha( c ) )
c = tolower( c );
else if( isdigit( c ) || c == '-' || c == '_' || c == '+' || c == '.' )
;
else
c = '_';
out.append( 1, c );
}
if( out.length() > 64 )
{
out.Truncate( 64 );
}
while( !out.IsEmpty() && ( out[0] == '.' || out[0] == '-' || out[0] == '+' ) )
{
out.erase( 0, 1 );
}
while( !out.IsEmpty() && out.Last() == '.' )
{
out.RemoveLast();
}
return out;
}
wxString Double2String( double aVal )
{
// We don't want to output -0.0 as this value is just 0 for fabs
if( aVal == -0.0 )
aVal = 0.0;
wxString str = wxString::FromCDouble( aVal, PCB_IO_ODBPP::m_sigfig );
// Remove all but the last trailing zeros from str
while( str.EndsWith( wxT( "00" ) ) )
str.RemoveLast();
return str;
}
std::string Double2String( double aVal, int32_t aDigits )
{
// We don't want to output -0.0 as this value is just 0 for fabs
if( aVal == -0.0 )
aVal = 0.0;
wxString str = wxString::FromCDouble( aVal, aDigits );
return str.ToStdString();
}
wxString SymDouble2String( double aVal )
{
return Double2String( PCB_IO_ODBPP::m_symbolScale * aVal );
}
wxString Data2String( double aVal )
{
return Double2String( PCB_IO_ODBPP::m_scale * aVal );
}
std::pair<wxString, wxString> AddXY( const VECTOR2I& aVec )
{
// TODO: to deal with user preference x y increment setting
std::pair<wxString, wxString> xy =
std::pair<wxString, wxString>( Double2String( PCB_IO_ODBPP::m_scale * aVec.x ),
Double2String( -PCB_IO_ODBPP::m_scale * aVec.y ) );
return xy;
}
VECTOR2I GetShapePosition( const PCB_SHAPE& aShape )
{
VECTOR2D pos{};
switch( aShape.GetShape() )
{
// Rectangles in KiCad are mapped by their corner while ODBPP uses the center
case SHAPE_T::RECTANGLE:
pos = aShape.GetPosition()
+ VECTOR2I( aShape.GetRectangleWidth() / 2.0, aShape.GetRectangleHeight() / 2.0 );
break;
// Both KiCad and ODBPP use the center of the circle
case SHAPE_T::CIRCLE:
// KiCad uses the exact points on the board
case SHAPE_T::POLY:
case SHAPE_T::BEZIER:
case SHAPE_T::SEGMENT:
case SHAPE_T::ARC: pos = aShape.GetPosition(); break;
}
return pos;
}
} // namespace ODB
void ODB_TREE_WRITER::CreateEntityDirectory( const wxString& aPareDir,
const wxString& aSubDir /*= wxEmptyString*/ )
{
wxFileName path = wxFileName::DirName( aPareDir );
wxArrayString subDirs = wxFileName::DirName( aSubDir.Lower() ).GetDirs();
for( size_t i = 0; i < subDirs.GetCount(); i++ )
path.AppendDir( subDirs[i] );
if( !path.DirExists() )
{
if( !path.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
{
throw( std::runtime_error( "Could not create directory" + path.GetPath() ) );
}
}
m_currentPath = path.GetPath();
}
ODB_FILE_WRITER::ODB_FILE_WRITER( ODB_TREE_WRITER& aTreeWriter, const wxString& aFileName ) :
m_treeWriter( aTreeWriter )
{
CreateFile( aFileName );
}
void ODB_FILE_WRITER::CreateFile( const wxString& aFileName )
{
if( aFileName.IsEmpty() || m_treeWriter.GetCurrentPath().IsEmpty() )
return;
wxFileName fn;
fn.SetPath( m_treeWriter.GetCurrentPath() );
fn.SetFullName( aFileName );
wxString dirPath = fn.GetPath();
if( !wxDir::Exists( dirPath ) )
{
if( !wxDir::Make( dirPath ) )
throw( std::runtime_error( "Could not create directory" + dirPath ) );
}
if( !fn.IsDirWritable() || ( fn.Exists() && !fn.IsFileWritable() ) )
return;
if( m_ostream.is_open() )
m_ostream.close();
m_ostream.open( TO_UTF8( fn.GetFullPath() ),
std::ios_base::out | std::ios_base::trunc | std::ios_base::binary );
m_ostream.imbue( std::locale::classic() );
if( !m_ostream.is_open() || !m_ostream.good() )
throw std::runtime_error( "Failed to open file: " + fn.GetFullPath() );
}
bool ODB_FILE_WRITER::CloseFile()
{
if( m_ostream.is_open() )
{
m_ostream.close();
if( !m_ostream.good() )
{
throw std::runtime_error( "close file failed" );
return false;
}
}
return true;
}
void ODB_TEXT_WRITER::WriteEquationLine( const std::string& var, int value )
{
WriteIndent();
m_ostream << var << "=" << value << std::endl;
}
void ODB_TEXT_WRITER::WriteEquationLine( const wxString& var, const wxString& value )
{
WriteIndent();
m_ostream << var << "=" << value << std::endl;
}
void ODB_TEXT_WRITER::WriteIndent()
{
if( in_array )
m_ostream << " ";
}
void ODB_TEXT_WRITER::BeginArray( const std::string& a )
{
if( in_array )
throw std::runtime_error( "already in array" );
in_array = true;
m_ostream << a << " {" << std::endl;
}
void ODB_TEXT_WRITER::EndArray()
{
if( !in_array )
throw std::runtime_error( "not in array" );
in_array = false;
m_ostream << "}" << std::endl << std::endl;
}
ODB_TEXT_WRITER::ARRAY_PROXY::ARRAY_PROXY( ODB_TEXT_WRITER& aWriter, const std::string& aStr ) :
m_writer( aWriter )
{
m_writer.BeginArray( aStr );
}
ODB_TEXT_WRITER::ARRAY_PROXY::~ARRAY_PROXY()
{
m_writer.EndArray();
}
ODB_DRILL_TOOLS::ODB_DRILL_TOOLS( const wxString& aUnits, const wxString& aThickness,
const wxString& aUserParams ) :
m_units( aUnits ), m_thickness( aThickness ), m_userParams( aUserParams )
{
}
void ODB_DRILL_TOOLS::GenerateFile( std::ostream& aStream )
{
ODB_TEXT_WRITER twriter( aStream );
twriter.WriteEquationLine( "UNITS", m_units );
twriter.WriteEquationLine( "THICKNESS", m_thickness );
twriter.WriteEquationLine( "USER_PARAMS", m_userParams );
for( const auto& tool : m_tools )
{
const auto array_proxy = twriter.MakeArrayProxy( "TOOLS" );
twriter.WriteEquationLine( "NUM", tool.m_num );
twriter.WriteEquationLine( "TYPE", tool.m_type );
twriter.WriteEquationLine( "TYPE2", tool.m_type2 );
twriter.WriteEquationLine( "MIN_TOL", tool.m_minTol );
twriter.WriteEquationLine( "MAX_TOL", tool.m_maxTol );
twriter.WriteEquationLine( "BIT", tool.m_bit );
twriter.WriteEquationLine( "FINISH_SIZE", tool.m_finishSize );
twriter.WriteEquationLine( "DRILL_SIZE", tool.m_drillSize );
}
}

View File

@ -0,0 +1,355 @@
#ifndef _ODB_UTIL_H_
#define _ODB_UTIL_H_
#include <map>
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <wx/string.h>
#include "pcb_shape.h"
#include <wx/filename.h>
enum class ODB_POLARITY
{
POSITIVE,
NEGATIVE
};
enum class ODB_CONTEXT
{
BOARD,
MISC
};
enum class ODB_DIELECTRIC_TYPE
{
NONE,
PREPREG,
CORE
};
enum class ODB_TYPE
{
UNDEFINED,
SIGNAL,
POWER_GROUND,
DIELECTRIC,
MIXED,
SOLDER_MASK,
SOLDER_PASTE,
SILK_SCREEN,
DRILL,
ROUT,
DOCUMENT,
COMPONENT,
MASK,
CONDUCTIVE_PASTE,
};
enum class ODB_SUBTYPE
{
COVERLAY,
COVERCOAT,
STIFFENER,
BEND_AREA,
FLEX_AREA,
RIGID_AREA,
PSA,
SILVER_MASK,
CARBON_MASK,
};
enum class ODB_FID_TYPE
{
COPPER,
LAMINATE,
HOLE
};
namespace ODB
{
wxString GenODBString( const wxString& aStr );
wxString GenLegalNetName( const wxString& aStr );
wxString GenLegalEntityName( const wxString& aStr );
wxString Double2String( double aVal );
std::string Double2String( double aVal, int32_t aDigits );
wxString Data2String( double aVal );
wxString SymDouble2String( double aVal );
std::pair<wxString, wxString> AddXY( const VECTOR2I& aVec );
VECTOR2I GetShapePosition( const PCB_SHAPE& aShape );
template <typename T>
class EnumStringMap
{
public:
static std::map<T, std::string>& GetMap()
{
static_assert( std::is_enum_v<T>, "Template parameter T must be an enum type" );
static std::map<T, std::string> map = []()
{
std::map<T, std::string> result;
if constexpr( std::is_same_v<T, ODB_POLARITY> )
{
result[ODB_POLARITY::POSITIVE] = "POSITIVE";
result[ODB_POLARITY::NEGATIVE] = "NEGATIVE";
}
if constexpr( std::is_same_v<T, ODB_CONTEXT> )
{
result[ODB_CONTEXT::BOARD] = "BOARD";
result[ODB_CONTEXT::MISC] = "MISC";
}
if constexpr( std::is_same_v<T, ODB_TYPE> )
{
//just for logical reasons.TYPE field must be defined.
result[ODB_TYPE::UNDEFINED] = "";
result[ODB_TYPE::SIGNAL] = "SIGNAL";
result[ODB_TYPE::POWER_GROUND] = "POWER_GROUND";
result[ODB_TYPE::DIELECTRIC] = "DIELECTRIC";
result[ODB_TYPE::MIXED] = "MIXED";
result[ODB_TYPE::SOLDER_MASK] = "SOLDER_MASK";
result[ODB_TYPE::SOLDER_PASTE] = "SOLDER_PASTE";
result[ODB_TYPE::SILK_SCREEN] = "SILK_SCREEN";
result[ODB_TYPE::DRILL] = "DRILL";
result[ODB_TYPE::ROUT] = "ROUT";
result[ODB_TYPE::DOCUMENT] = "DOCUMENT";
result[ODB_TYPE::COMPONENT] = "COMPONENT";
result[ODB_TYPE::MASK] = "MASK";
result[ODB_TYPE::CONDUCTIVE_PASTE] = "CONDUCTIVE_PASTE";
}
if constexpr( std::is_same_v<T, ODB_SUBTYPE> )
{
result[ODB_SUBTYPE::COVERLAY] = "COVERLAY";
result[ODB_SUBTYPE::COVERCOAT] = "COVERCOAT";
result[ODB_SUBTYPE::STIFFENER] = "STIFFENER";
result[ODB_SUBTYPE::BEND_AREA] = "BEND_AREA";
result[ODB_SUBTYPE::FLEX_AREA] = "FLEX_AREA";
result[ODB_SUBTYPE::RIGID_AREA] = "RIGID_AREA";
result[ODB_SUBTYPE::PSA] = "PSA";
result[ODB_SUBTYPE::SILVER_MASK] = "SILVER_MASK";
result[ODB_SUBTYPE::CARBON_MASK] = "CARBON_MASK";
}
if constexpr( std::is_same_v<T, ODB_DIELECTRIC_TYPE> )
{
result[ODB_DIELECTRIC_TYPE::NONE] = "NONE";
result[ODB_DIELECTRIC_TYPE::PREPREG] = "PREPREG";
result[ODB_DIELECTRIC_TYPE::CORE] = "CORE";
}
if constexpr( std::is_same_v<T, ODB_FID_TYPE> )
{
result[ODB_FID_TYPE::COPPER] = "C";
result[ODB_FID_TYPE::LAMINATE] = "L";
result[ODB_FID_TYPE::HOLE] = "H";
}
return result;
}();
return map;
}
};
template <typename T>
std::string Enum2String( T value )
{
const auto& map = EnumStringMap<T>::GetMap();
auto it = map.find( value );
if( it != map.end() )
{
return it->second;
}
else
{
throw std::out_of_range( "Enum value not found in map" );
}
}
class CHECK_ONCE
{
public:
bool operator()()
{
if( first )
{
first = false;
return true;
}
return false;
}
private:
bool first = true;
};
} // namespace ODB
class ODB_TREE_WRITER;
class ODB_FILE_WRITER
{
public:
ODB_FILE_WRITER( ODB_TREE_WRITER& aTreeWriter, const wxString& aFileName );
virtual ~ODB_FILE_WRITER() { CloseFile(); }
ODB_FILE_WRITER( ODB_FILE_WRITER&& ) = delete;
ODB_FILE_WRITER& operator=( ODB_FILE_WRITER&& ) = delete;
ODB_FILE_WRITER( ODB_FILE_WRITER const& ) = delete;
ODB_FILE_WRITER& operator=( ODB_FILE_WRITER const& ) = delete;
void CreateFile( const wxString& aFileName );
bool CloseFile();
inline std::ostream& GetStream() { return m_ostream; }
private:
ODB_TREE_WRITER& m_treeWriter;
std::ofstream m_ostream;
};
class ODB_TREE_WRITER
{
public:
ODB_TREE_WRITER( const wxString& aDir ) : m_currentPath( aDir ) {}
ODB_TREE_WRITER( const wxString& aPareDir, const wxString& aSubDir )
{
CreateEntityDirectory( aPareDir, aSubDir );
}
virtual ~ODB_TREE_WRITER() {}
[[nodiscard]] ODB_FILE_WRITER CreateFileProxy( const wxString& aFileName )
{
return ODB_FILE_WRITER( *this, aFileName );
}
void CreateEntityDirectory( const wxString& aPareDir, const wxString& aSubDir = wxEmptyString );
inline const wxString GetCurrentPath() const { return m_currentPath; }
inline void SetCurrentPath( const wxString& aDir ) { m_currentPath = aDir; }
inline void SetRootPath( const wxString& aDir ) { m_rootPath = aDir; }
inline const wxString GetRootPath() const { return m_rootPath; }
private:
wxString m_currentPath;
wxString m_rootPath;
};
class ODB_TEXT_WRITER
{
public:
ODB_TEXT_WRITER( std::ostream& aStream ) : m_ostream( aStream ) {}
virtual ~ODB_TEXT_WRITER() {}
// void WriteEquationLine( const std::string &var, const std::string &value );
void WriteEquationLine( const std::string& var, int value );
void WriteEquationLine( const wxString& var, const wxString& value );
template <typename T>
void write_line_enum( const std::string& var, const T& value )
{
WriteEquationLine( var, ODB::Enum2String( value ) );
}
class ARRAY_PROXY
{
friend ODB_TEXT_WRITER;
public:
~ARRAY_PROXY();
private:
ARRAY_PROXY( ODB_TEXT_WRITER& aWriter, const std::string& aStr );
ODB_TEXT_WRITER& m_writer;
ARRAY_PROXY( ARRAY_PROXY&& ) = delete;
ARRAY_PROXY& operator=( ARRAY_PROXY&& ) = delete;
ARRAY_PROXY( ARRAY_PROXY const& ) = delete;
ARRAY_PROXY& operator=( ARRAY_PROXY const& ) = delete;
};
[[nodiscard]] ARRAY_PROXY MakeArrayProxy( const std::string& aStr )
{
return ARRAY_PROXY( *this, aStr );
}
private:
void WriteIndent();
void BeginArray( const std::string& a );
void EndArray();
std::ostream& m_ostream;
bool in_array = false;
};
class ODB_DRILL_TOOLS
{
public:
struct TOOLS
{
uint32_t m_num;
wxString m_type;
wxString m_type2 = wxT( "STANDARD" );
uint32_t m_minTol;
uint32_t m_maxTol;
wxString m_bit = wxEmptyString;
wxString m_finishSize;
wxString m_drillSize;
TOOLS() : m_num( 0 ), m_minTol( 0 ), m_maxTol( 0 ) {}
};
ODB_DRILL_TOOLS( const wxString& aUnits, const wxString& aThickness = "0",
const wxString& aUserParams = wxEmptyString );
void AddDrillTools( const wxString& aType, const wxString& aFinishSize )
{
TOOLS tool;
tool.m_num = m_tools.size() + 1;
tool.m_type = aType;
tool.m_finishSize = aFinishSize;
tool.m_drillSize = aFinishSize;
m_tools.push_back( tool );
}
void GenerateFile( std::ostream& aStream );
wxString m_units;
wxString m_thickness;
wxString m_userParams;
std::vector<ODB_DRILL_TOOLS::TOOLS> m_tools;
};
#endif // _ODB_UTIL_H_

View File

@ -0,0 +1,170 @@
/*
* 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 "pcb_io_odbpp.h"
#include "progress_reporter.h"
#include "odb_util.h"
#include "odb_attribute.h"
#include "odb_defines.h"
#include "odb_feature.h"
#include "odb_entity.h"
#include "wx/log.h"
double PCB_IO_ODBPP::m_scale = 1.0 / PCB_IU_PER_MM;
double PCB_IO_ODBPP::m_symbolScale = 1.0 / PL_IU_PER_MM;
int PCB_IO_ODBPP::m_sigfig = 4;
std::string PCB_IO_ODBPP::m_unitsStr = "MM";
PCB_IO_ODBPP::~PCB_IO_ODBPP()
{
ClearLoadedFootprints();
}
void PCB_IO_ODBPP::ClearLoadedFootprints()
{
m_loaded_footprints.clear();
}
void PCB_IO_ODBPP::CreateEntity()
{
Make<ODB_FONTS_ENTITY>();
Make<ODB_INPUT_ENTITY>();
Make<ODB_MATRIX_ENTITY>( m_board, this );
Make<ODB_STEP_ENTITY>( m_board, this );
Make<ODB_MISC_ENTITY>();
Make<ODB_SYMBOLS_ENTITY>();
Make<ODB_USER_ENTITY>();
Make<ODB_WHEELS_ENTITY>();
}
bool PCB_IO_ODBPP::GenerateFiles( ODB_TREE_WRITER& writer )
{
for( const auto entity : m_entities )
{
if( !entity->CreateDirectiryTree( writer ) )
{
throw std::runtime_error( "Failed in create directiry tree process" );
return false;
}
try
{
entity->GenerateFiles( writer );
}
catch( const std::exception& e )
{
throw std::runtime_error( "Failed in generate files process.\n"
+ std::string( e.what() ) );
return false;
}
}
return true;
}
bool PCB_IO_ODBPP::ExportODB( const wxString& aFileName )
{
try
{
std::shared_ptr<ODB_TREE_WRITER> writer =
std::make_shared<ODB_TREE_WRITER>( aFileName, "odb" );
writer->SetRootPath( writer->GetCurrentPath() );
if( m_progressReporter )
{
m_progressReporter->SetNumPhases( 3 );
m_progressReporter->BeginPhase( 0 );
m_progressReporter->Report( _( "Creating Entities" ) );
}
CreateEntity();
if( m_progressReporter )
{
m_progressReporter->SetCurrentProgress( 1.0 );
m_progressReporter->AdvancePhase( _( "Init Entity Data" ) );
}
for( auto const& entity : m_entities )
{
entity->InitEntityData();
}
if( m_progressReporter )
{
m_progressReporter->SetCurrentProgress( 1.0 );
m_progressReporter->AdvancePhase( _( "Generating Entities Files" ) );
}
if( !GenerateFiles( *writer ) )
return false;
return true;
}
catch( const std::exception& e )
{
wxLogError( "Exception in ODB++ ExportODB process: %s", e.what() );
std::cerr << e.what() << std::endl;
return false;
}
}
std::vector<FOOTPRINT*> PCB_IO_ODBPP::GetImportedCachedLibraryFootprints()
{
std::vector<FOOTPRINT*> retval;
retval.reserve( m_loaded_footprints.size() );
for( const auto& fp : m_loaded_footprints )
{
retval.push_back( static_cast<FOOTPRINT*>( fp->Clone() ) );
}
return retval;
}
void PCB_IO_ODBPP::SaveBoard( const wxString& aFileName, BOARD* aBoard,
const std::map<std::string, UTF8>* aProperties )
{
m_board = aBoard;
if( auto it = aProperties->find( "units" ); it != aProperties->end() )
{
if( it->second == "inch" )
{
m_unitsStr = "INCH";
m_scale = 25.4 / PCB_IU_PER_MM;
m_symbolScale = 25.4 / PL_IU_PER_MM;
}
}
if( auto it = aProperties->find( "sigfig" ); it != aProperties->end() )
m_sigfig = std::stoi( it->second );
ExportODB( aFileName );
}

View File

@ -0,0 +1,182 @@
/*
* 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 _PCB_IO_ODBPP_H_
#define _PCB_IO_ODBPP_H_
#include <pcb_io/pcb_io.h>
#include <pcb_io/pcb_io_mgr.h>
#include <pcb_io/common/plugin_common_layer_mapping.h>
#include <eda_shape.h>
#include <layer_ids.h> // PCB_LAYER_ID
#include <font/font.h>
#include <geometry/shape_segment.h>
#include <stroke_params.h>
#include <memory>
#include "odb_entity.h"
class BOARD;
class BOARD_ITEM;
class EDA_TEXT;
class FOOTPRINT;
class PROGRESS_REPORTER;
class NETINFO_ITEM;
class PAD;
class PCB_SHAPE;
class PCB_VIA;
class PROGRESS_REPORTER;
class SHAPE_POLY_SET;
class SHAPE_SEGMENT;
class EDA_DATA::SUB_NET;
class PCB_IO_ODBPP : public PCB_IO
{
public:
PCB_IO_ODBPP() : PCB_IO( wxS( "ODBPlusPlus" ) ) { m_board = nullptr; }
~PCB_IO_ODBPP() override;
void SaveBoard( const wxString& aFileName, BOARD* aBoard,
const std::map<std::string, UTF8>* aProperties = nullptr ) override;
const IO_BASE::IO_FILE_DESC GetBoardFileDesc() const override
{
return IO_BASE::IO_FILE_DESC( _HKI( "ODB++ Production File" ), { "ZIP" } );
}
const IO_BASE::IO_FILE_DESC GetLibraryDesc() const override
{
// No library description for this plugin
return IO_BASE::IO_FILE_DESC( wxEmptyString, {} );
}
std::vector<FOOTPRINT*> GetImportedCachedLibraryFootprints() override;
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const override { return 0; }
// Reading currently disabled
bool CanReadBoard( const wxString& aFileName ) const override { return false; }
// Reading currently disabled
bool CanReadFootprint( const wxString& aFileName ) const override { return false; }
// Reading currently disabled
bool CanReadLibrary( const wxString& aFileName ) const override { return false; }
public:
inline std::vector<std::pair<PCB_LAYER_ID, wxString>>& GetLayerNameList()
{
return m_layer_name_list;
}
inline std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>>& GetLayerElementsMap()
{
return m_layer_elements;
}
inline std::vector<std::shared_ptr<FOOTPRINT>>& GetLoadedFootprintList()
{
return m_loaded_footprints;
}
inline std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>&
GetDrillLayerItemsMap()
{
return m_drill_layers;
}
inline std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>&
GetSlotHolesMap()
{
return m_slot_holes;
}
inline std::map<const PAD*, EDA_DATA::SUB_NET_TOEPRINT*>& GetPadSubnetMap()
{
return m_topeprint_subnets;
}
inline std::map<std::pair<PCB_LAYER_ID, ZONE*>, EDA_DATA::SUB_NET_PLANE*>& GetPlaneSubnetMap()
{
return m_plane_subnets;
}
inline std::map<PCB_TRACK*, EDA_DATA::SUB_NET*>& GetViaTraceSubnetMap()
{
return m_via_trace_subnets;
}
std::shared_ptr<ODB_TREE_WRITER> m_writer;
bool GenerateFiles( ODB_TREE_WRITER& writer );
bool ExportODB( const wxString& aFileName );
void CreateEntity();
bool CreateDirectories( ODB_TREE_WRITER& writer );
// Frees the memory allocated for the loaded footprints in #m_loaded_footprints.
void ClearLoadedFootprints();
static double m_scale;
static double m_symbolScale;
static int m_sigfig;
static std::string m_unitsStr;
private:
template <typename T, typename... Args>
void Make( Args&&... args )
{
std::shared_ptr<ODB_ENTITY_BASE> entity =
std::make_shared<T>( std::forward<Args>( args )... );
if( entity )
m_entities.push_back( entity );
}
BOARD* m_board;
std::vector<std::shared_ptr<FOOTPRINT>> m_loaded_footprints;
std::vector<std::pair<PCB_LAYER_ID, wxString>>
m_layer_name_list; //<! layer name in matrix entity to the internal layer id
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_drill_layers; //<! Drill sets are output as layers (to/from pairs)
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
m_slot_holes; //<! Storage vector of slotted holes that need to be output as cutouts
std::map<PCB_LAYER_ID, std::map<int, std::vector<BOARD_ITEM*>>>
m_layer_elements; //<! Storage map of layer to element list
std::map<const PAD*, EDA_DATA::SUB_NET_TOEPRINT*> m_topeprint_subnets;
std::map<std::pair<PCB_LAYER_ID, ZONE*>, EDA_DATA::SUB_NET_PLANE*> m_plane_subnets;
std::map<PCB_TRACK*, EDA_DATA::SUB_NET*> m_via_trace_subnets;
std::vector<std::shared_ptr<ODB_ENTITY_BASE>> m_entities;
};
#endif // _PCB_IO_ODBPP_H_

View File

@ -45,8 +45,11 @@
#include <pcb_io/easyeda/pcb_io_easyeda_plugin.h>
#include <pcb_io/easyedapro/pcb_io_easyedapro.h>
#include <pcb_io/ipc2581/pcb_io_ipc2581.h>
#include <pcb_io/odbpp/pcb_io_odbpp.h>
#include <reporter.h>
#define FMT_UNIMPLEMENTED _( "Plugin \"%s\" does not implement the \"%s\" function." )
#define FMT_NOTFOUND _( "Plugin type \"%s\" is not found." )
@ -328,4 +331,9 @@ static PCB_IO_MGR::REGISTER_PLUGIN registerIPC2581Plugin(
PCB_IO_MGR::IPC2581,
wxT( "IPC-2581" ),
[]() -> PCB_IO* { return new PCB_IO_IPC2581; } );
static PCB_IO_MGR::REGISTER_PLUGIN registerODBPPPlugin(
PCB_IO_MGR::ODBPP,
wxT( "ODB++" ),
[]() -> PCB_IO* { return new PCB_IO_ODBPP; } );
// clang-format on

View File

@ -69,6 +69,7 @@ public:
PCAD,
SOLIDWORKS_PCB,
IPC2581,
ODBPP,
// add your type here.
// etc.

View File

@ -201,6 +201,13 @@ public:
bool compress;
};
struct DIALOG_EXPORT_ODBPP
{
int precision;
int units;
bool compress;
};
struct DIALOG_EXPORT_SVG
{
bool black_and_white;
@ -379,6 +386,8 @@ public:
DIALOG_EXPORT_2581 m_Export2581;
DIALOG_EXPORT_ODBPP m_ExportODBPP;
DIALOG_EXPORT_SVG m_ExportSvg;
DIALOG_EXPORT_VRML m_ExportVrml;

View File

@ -512,6 +512,8 @@ int BOARD_EDITOR_CONTROL::GenerateFabFiles( const TOOL_EVENT& aEvent )
m_frame->RecreateBOMFileFromBoard( dummy );
else if( aEvent.IsAction( &PCB_ACTIONS::generateIPC2581File ) )
m_frame->GenIPC2581File( dummy );
else if( aEvent.IsAction( &PCB_ACTIONS::generateODBPPFile ) )
m_frame->GenODBPPFiles( dummy );
else
wxFAIL_MSG( wxT( "GenerateFabFiles(): unexpected request" ) );
@ -1695,6 +1697,7 @@ void BOARD_EDITOR_CONTROL::setTransitions()
Go( &BOARD_EDITOR_CONTROL::GenerateFabFiles, PCB_ACTIONS::generateD356File.MakeEvent() );
Go( &BOARD_EDITOR_CONTROL::GenerateFabFiles, PCB_ACTIONS::generateBOM.MakeEvent() );
Go( &BOARD_EDITOR_CONTROL::GenerateFabFiles, PCB_ACTIONS::generateIPC2581File.MakeEvent() );
Go( &BOARD_EDITOR_CONTROL::GenerateFabFiles, PCB_ACTIONS::generateODBPPFile.MakeEvent() );
// Track & via size control
Go( &BOARD_EDITOR_CONTROL::TrackWidthInc, PCB_ACTIONS::trackWidthInc.MakeEvent() );

View File

@ -1029,6 +1029,13 @@ TOOL_ACTION PCB_ACTIONS::generateIPC2581File( TOOL_ACTION_ARGS()
.Tooltip( _( "Generate an IPC-2581 file" ) )
.Icon( BITMAPS::post_xml ) );
TOOL_ACTION PCB_ACTIONS::generateODBPPFile( TOOL_ACTION_ARGS()
.Name( "pcbnew.EditorControl.generateODBPPFile" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "ODB++ Output File..." ) )
.Tooltip( _( "Generate ODB++ output files" ) )
.Icon( BITMAPS::post_odb ) );
TOOL_ACTION PCB_ACTIONS::generateD356File( TOOL_ACTION_ARGS()
.Name( "pcbnew.EditorControl.generateD356File" )
.Scope( AS_GLOBAL )

View File

@ -435,6 +435,7 @@ public:
static TOOL_ACTION generatePosFile;
static TOOL_ACTION generateReportFile;
static TOOL_ACTION generateIPC2581File;
static TOOL_ACTION generateODBPPFile;
static TOOL_ACTION generateD356File;
static TOOL_ACTION generateBOM;

View File

@ -447,6 +447,7 @@ set( BMAPS_MID
post_gerber
post_rpt
post_xml
post_odb
preference
print_button
project

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="Слой_1"
data-name="Слой 1"
viewBox="0 0 24 24"
version="1.1"
sodipodi:docname="post_odb.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview159354"
showgrid="true"
inkscape:zoom="30.291667"
inkscape:cx="13.171939"
inkscape:cy="13.980743"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="text5854"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid6877"
empspacing="2"
spacingx="0.5"
spacingy="0.5"
originx="0"
originy="0"
units="px"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata43">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>post_odb</dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs159331">
<style
id="style159329">.cls-1{fill:#b9b9b9;}.cls-2{fill:#1a81c4;}.cls-3{fill:#fff;}.cls-4{fill:#bf2641;}.cls-5{fill:none;stroke:#f5f5f5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style>
</defs>
<title
id="title159333">post_odb</title>
<rect
class="cls-1"
x="3.0489998"
y="0"
width="17.937201"
height="17.983299"
rx="2.9934001"
id="rect159335"
style="fill:#8f8f8f;fill-opacity:1" />
<rect
class="cls-2"
x="0.00010001659"
y="2.9995"
width="18.7099"
height="9.2299004"
id="rect159337"
style="fill:#42b8eb;fill-opacity:1" />
<g
aria-label="xml"
id="text5854"
style="font-size:8.52777px;line-height:1.25;font-family:'Tiresias LPfont';-inkscape-font-specification:'Tiresias LPfont';stroke-width:0.700254">
<path
class="cls-3"
d="M 2.4739587,7.5242975 A 2.4445396,2.4329348 0 0 1 3.0182779,5.8948399 1.8741713,1.8652742 0 0 1 4.515156,5.2568698 1.8667734,1.8579115 0 0 1 6.0200571,5.8948399 2.4198452,2.4083576 0 0 1 6.5685442,7.5242975 v 0.086072 A 2.4325571,2.4210092 0 0 1 6.0200571,9.2479159 1.8705245,1.8616447 0 0 1 4.5232833,9.8768639 1.8812567,1.8723259 0 0 1 3.0182779,9.2442863 2.4406843,2.4290979 0 0 1 2.4739587,7.6103695 Z m 1.0022601,0.084102 a 2.003791,1.9942786 0 0 0 0.2553842,1.0602415 0.9640203,0.95944389 0 0 0 1.5751291,0 1.948463,1.9392132 0 0 0 0.2636156,-1.0602415 V 7.5223272 A 1.9218931,1.9127694 0 0 0 5.3067321,6.474115 a 0.95193358,0.94741455 0 0 0 -1.5751291,0 1.98837,1.9789308 0 0 0 -0.2556967,1.0482122 z"
id="path26539"
style="fill:#ffffff;stroke-width:0.727902" />
<path
class="cls-3"
d="M 7.5318324,7.805352 A 3.2017341,2.8187362 0 0 1 8.0279112,6.1589374 1.6036387,1.4118082 0 0 1 9.4226163,5.5333798 1.5206901,1.3387821 0 0 1 10.126319,5.6737531 1.5772322,1.3885606 0 0 1 10.664728,6.0740213 v -2.347749 h 1.085588 V 9.880695 H 10.83899 L 10.731651,9.3518548 A 1.6005143,1.4090576 0 0 1 10.173588,9.8075802 1.5695724,1.381817 0 0 1 9.413747,9.9635702 1.6418374,1.4454375 0 0 1 8.0279112,9.3908079 2.7377059,2.410216 0 0 1 7.5318324,7.888316 Z m 1.0856889,0.08323 a 1.9575056,1.7233448 0 0 0 0.256808,0.9427733 0.90044047,0.79272798 0 0 0 0.8163832,0.3509334 1.0920386,0.96140672 0 0 0 0.5836635,-0.1299918 1.1243915,0.98988956 0 0 0 0.390755,-0.370898 V 6.8192779 A 1.1368892,1.0008923 0 0 0 10.271453,6.4509531 1.0569643,0.93052813 0 0 0 9.6995819,6.3183881 0.87685606,0.77196479 0 0 0 8.8816868,6.7309012 2.4783782,2.1819097 0 0 0 8.6179245,7.805352 Z"
id="path26593"
style="fill:#ffffff;stroke-width:0.662215" />
<path
class="cls-3"
d="m 16.749071,7.8650048 a 2.4272,2.4272 0 0 1 -0.4373,1.5137 1.4574,1.4574 0 0 1 -1.2324,0.5733 1.414,1.414 0 0 1 -0.6943,-0.1621 1.3569,1.3569 0 0 1 -0.4922,-0.4766 l -0.0957,0.5557 h -0.8068 v -6.1895 h 0.9614 v 2.3848 a 1.3812,1.3812 0 0 1 0.4756,-0.4209 1.3576,1.3576 0 0 1 0.6444,-0.1465 1.4227,1.4227 0 0 1 1.24,0.6269 2.84,2.84 0 0 1 0.4375,1.6582 z m -0.9609,-0.083 a 2.2162,2.2162 0 0 0 -0.2305,-1.0825 0.7714,0.7714 0 0 0 -0.7275,-0.4121 0.9453,0.9453 0 0 0 -0.5361,0.1421 1.0023,1.0023 0 0 0 -0.3428,0.3965 v 1.8008 a 0.9408,0.9408 0 0 0 0.3408,0.3994 0.9862,0.9862 0 0 0 0.5459,0.1406 0.7975,0.7975 0 0 0 0.7246,-0.3516 1.74,1.74 0 0 0 0.2256,-0.95 z"
id="path26537"
style="fill:#ffffff" />
</g>
<path
class="cls-3"
d="M 2.07,9.9365 H 0.9965 V 8.9 H 2.07 Z"
id="path159339" />
<ellipse
class="cls-4"
cx="17.0075"
cy="16.996099"
rx="6.9924002"
ry="7.0039001"
id="ellipse159347"
style="fill:#f2647e;fill-opacity:1" />
<line
class="cls-5"
x1="17.1112"
y1="13.6254"
x2="17.1112"
y2="20.629299"
id="line159349" />
<polyline
class="cls-5"
points="15.362 17.16 18.111 14.406 20.861 17.16"
id="polyline159351"
transform="translate(-1,-1)" />
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="Слой_1"
data-name="Слой 1"
viewBox="0 0 24 24"
version="1.1"
sodipodi:docname="post_odb.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview159354"
showgrid="true"
inkscape:zoom="15.145834"
inkscape:cx="1.8817056"
inkscape:cy="13.832187"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="Слой_1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid6877"
empspacing="2"
spacingx="0.5"
spacingy="0.5"
originx="0"
originy="0"
units="px"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata43">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>post_odb</dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs159331">
<style
id="style159329">.cls-1{fill:#b9b9b9;}.cls-2{fill:#1a81c4;}.cls-3{fill:#fff;}.cls-4{fill:#bf2641;}.cls-5{fill:none;stroke:#f5f5f5;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style>
</defs>
<title
id="title159333">post_odb</title>
<rect
class="cls-1"
x="3.0489998"
y="0"
width="17.937201"
height="17.983299"
rx="2.9934001"
id="rect159335" />
<rect
class="cls-2"
x="0.00010001659"
y="2.9995"
width="18.7099"
height="9.2299004"
id="rect159337" />
<g
aria-label="xml"
id="text5854"
style="font-size:8.52777px;line-height:1.25;font-family:'Tiresias LPfont';-inkscape-font-specification:'Tiresias LPfont';stroke-width:0.700254"
transform="translate(0.42916093,0.03301238)">
<path
class="cls-3"
d="M 16.537701,8.0612976 A 2.5309534,2.2753582 0 0 1 16.082227,9.4788421 1.5215652,1.3679058 0 0 1 14.798716,10.015759 1.4774119,1.3282115 0 0 1 14.075163,9.8631659 1.4072317,1.2651185 0 0 1 13.562334,9.4175876 l -0.099,0.5198696 H 12.621732 V 4.1416781 h 1.002138 v 2.2327391 a 1.4352108,1.2902721 0 0 1 0.494702,-0.3933493 1.4036065,1.2618594 0 0 1 0.671779,-0.1378853 1.4828963,1.3331419 0 0 1 1.292061,0.5867229 2.9601223,2.6611864 0 0 1 0.455476,1.5530064 z M 15.536493,7.9829119 A 2.3043315,2.0716222 0 0 0 15.296858,6.9693296 0.80470339,0.72343822 0 0 0 14.538911,6.5825821 0.99507336,0.89458313 0 0 0 13.979699,6.7162892 1.038204,0.93335811 0 0 0 13.62387,7.0875766 v 1.6869641 a 0.98531315,0.88580861 0 0 0 0.353969,0.3737945 1.0355083,0.9309347 0 0 0 0.569158,0.1313671 0.8301728,0.74633554 0 0 0 0.754416,-0.3289192 1.8179028,1.6343169 0 0 0 0.23508,-0.8894855 z"
id="path26651"
style="fill:#ffffff;stroke-width:0.617173" />
<path
class="cls-3"
d="m 2.7200892,7.7128856 a 2.3461,2.3461 0 0 1 0.5224,-1.5713002 1.7987,1.7987 0 0 1 1.4366,-0.6152 1.7916,1.7916 0 0 1 1.4443001,0.6152 2.3224,2.3224 0 0 1 0.5264,1.5713002 v 0.083 a 2.3346,2.3346 0 0 1 -0.5264,1.5791 1.7952,1.7952 0 0 1 -1.4365001,0.6065 1.8055,1.8055 0 0 1 -1.4444,-0.61 2.3424,2.3424 0 0 1 -0.5224,-1.5756 z m 0.9619,0.0811 a 1.9231,1.9231 0 0 0 0.2451,1.0224 0.9252,0.9252 0 0 0 1.5117001,0 1.87,1.87 0 0 0 0.253,-1.0224 v -0.083 a 1.8445,1.8445 0 0 0 -0.253,-1.0108002 0.9136,0.9136 0 0 0 -1.5117001,0 1.9083,1.9083 0 0 0 -0.2454,1.0108002 z"
id="path26539"
style="fill:#ffffff" />
<path
class="cls-3"
d="M 7.3845385,7.952119 A 3.1015978,2.6397544 0 0 1 7.8651021,6.4102472 1.5534838,1.3221623 0 0 1 9.2161869,5.8244106 1.4731296,1.2537732 0 0 1 9.8978802,5.9558707 1.5279033,1.3003909 0 0 1 10.419451,6.330723 V 4.1320494 h 1.051637 v 5.763634 H 10.588265 L 10.484283,9.4004232 A 1.5504571,1.3195864 0 0 1 9.9436722,9.8272112 1.520483,1.2940754 0 0 1 9.2075949,9.9732972 1.5904879,1.3536564 0 0 1 7.8651021,9.436903 2.6520823,2.257174 0 0 1 7.3845385,8.029815 Z M 8.4362717,8.030065 A 1.8962832,1.6139172 0 0 0 8.6850479,8.9129741 0.87227859,0.74239198 0 0 0 9.4758982,9.2416242 1.0578843,0.90036008 0 0 0 10.041308,9.1198866 1.0892255,0.92703434 0 0 0 10.419842,8.7725396 V 7.0286579 A 1.1013322,0.93733841 0 0 0 10.038476,6.6837205 1.0239071,0.87144221 0 0 0 9.4844901,6.5595731 0.84943181,0.72294718 0 0 0 8.6921753,6.9458929 2.4008653,2.0433644 0 0 0 8.4366623,7.952119 Z"
id="path26593"
style="fill:#ffffff;stroke-width:0.630744" />
</g>
<path
class="cls-3"
d="M 2.07,9.9365 H 0.9965 V 8.9 H 2.07 Z"
id="path159339" />
<ellipse
class="cls-4"
cx="17.0075"
cy="16.996099"
rx="6.9924002"
ry="7.0039001"
id="ellipse159347" />
<line
class="cls-5"
x1="17.1112"
y1="13.6254"
x2="17.1112"
y2="20.629299"
id="line159349" />
<polyline
class="cls-5"
points="15.362 17.16 18.111 14.406 20.861 17.16"
id="polyline159351"
transform="translate(-1,-1)" />
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB