diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 878e5b2893..bc56606b16 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -181,11 +181,25 @@ if( KICAD_SCRIPTING AND KICAD_SCRIPTING_ACTION_MENU ) ) endif() +if( 1) set( PCBNEW_IMPORT_DXF import_dxf/dialog_dxf_import.cpp import_dxf/dialog_dxf_import_base.cpp import_dxf/dxf2brd_items.cpp ) +endif() + +set( PCBNEW_IMPORT_GFX + import_gfx/dialog_import_gfx_base.cpp + import_gfx/dialog_import_gfx.cpp + import_gfx/graphics_import_mgr.cpp + import_gfx/graphics_importer.cpp + import_gfx/graphics_importer_pcbnew.cpp + import_gfx/graphics_importer_buffer.cpp + import_gfx/dxf_import_plugin.cpp + import_gfx/svg_import_plugin.cpp + import_gfx/nanosvg.cpp + ) set( PCBNEW_EXPORTERS exporters/export_d356.cpp @@ -209,6 +223,7 @@ set( PCBNEW_CLASS_SRCS ${PCBNEW_DIALOGS} ${PCBNEW_EXPORTERS} ${PCBNEW_IMPORT_DXF} + ${PCBNEW_IMPORT_GFX} autorouter/rect_placement/rect_placement.cpp autorouter/spread_footprints.cpp @@ -653,6 +668,7 @@ endif () set( PCBNEW_KIFACE_LIBRARIES 3d-viewer + connectivity pcbcommon pnsrouter pcad2kicadpcb @@ -663,7 +679,6 @@ set( PCBNEW_KIFACE_LIBRARIES gal lib_dxf idf3 - connectivity ${wxWidgets_LIBRARIES} ${GITHUB_PLUGIN_LIBRARIES} ${GDI_PLUS_LIBRARIES} diff --git a/pcbnew/import_gfx/dialog_import_gfx.cpp b/pcbnew/import_gfx/dialog_import_gfx.cpp new file mode 100644 index 0000000000..5ebfca5ee9 --- /dev/null +++ b/pcbnew/import_gfx/dialog_import_gfx.cpp @@ -0,0 +1,325 @@ +/** + * @file dialog_gfx_import.cpp + * @brief Dialog to import a vector graphics file on a given board layer. + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2018 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dialog_import_gfx.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// Keys to store setup in config +#define IMPORT_GFX_LAYER_OPTION_KEY "GfxImportBrdLayer" +#define IMPORT_GFX_COORD_ORIGIN_KEY "GfxImportCoordOrigin" +#define IMPORT_GFX_LAST_FILE_KEY "GfxImportLastFile" +#define IMPORT_GFX_GRID_UNITS_KEY "GfxImportGridUnits" +#define IMPORT_GFX_GRID_OFFSET_X_KEY "GfxImportGridOffsetX" +#define IMPORT_GFX_GRID_OFFSET_Y_KEY "GfxImportGridOffsetY" + + +// Static members of DIALOG_IMPORT_GFX, to remember the user's choices during the session +wxString DIALOG_IMPORT_GFX::m_filename; +int DIALOG_IMPORT_GFX::m_offsetSelection = 0; +LAYER_NUM DIALOG_IMPORT_GFX::m_layer = Dwgs_User; + + +DIALOG_IMPORT_GFX::DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aUseModuleItems ) + : DIALOG_IMPORT_GFX_BASE( aParent ) +{ + m_parent = aParent; + + if( aUseModuleItems ) + m_importer.reset( new GRAPHICS_IMPORTER_MODULE( m_parent->GetBoard()->m_Modules ) ); + else + m_importer.reset( new GRAPHICS_IMPORTER_BOARD( m_parent->GetBoard() ) ); + + m_config = Kiface().KifaceSettings(); + m_gridUnits = 0; + m_gridOffsetX = 0.0; + m_gridOffsetY = 0.0; + + if( m_config ) + { + m_layer = m_config->Read( IMPORT_GFX_LAYER_OPTION_KEY, (long)Dwgs_User ); + m_offsetSelection = m_config->Read( IMPORT_GFX_COORD_ORIGIN_KEY, (long)0 ); + m_filename = m_config->Read( IMPORT_GFX_LAST_FILE_KEY, wxEmptyString ); + m_config->Read( IMPORT_GFX_GRID_UNITS_KEY, &m_gridUnits, 0 ); + m_config->Read( IMPORT_GFX_GRID_OFFSET_X_KEY, &m_gridOffsetX, 0.0 ); + m_config->Read( IMPORT_GFX_GRID_OFFSET_Y_KEY, &m_gridOffsetY, 0.0 ); + } + + m_PCBGridUnits->SetSelection( m_gridUnits ); + wxString tmpStr; + tmpStr << m_gridOffsetX; + m_PCBXCoord->SetValue( tmpStr ); + tmpStr = ""; + tmpStr << m_gridOffsetY; + m_PCBYCoord->SetValue( tmpStr ); + + m_textCtrlFileName->SetValue( m_filename ); + m_rbOffsetOption->SetSelection( m_offsetSelection ); + + // Configure the layers list selector + m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys + m_SelLayerBox->SetNotAllowedLayerSet( LSET::AllCuMask() ); // Do not use copper layers + m_SelLayerBox->SetBoardFrame( m_parent ); + m_SelLayerBox->Resync(); + + if( m_SelLayerBox->SetLayerSelection( m_layer ) < 0 ) + { + m_layer = Dwgs_User; + m_SelLayerBox->SetLayerSelection( m_layer ); + } + + m_sdbSizer1OK->SetDefault(); + GetSizer()->Fit( this ); + GetSizer()->SetSizeHints( this ); + Centre(); +} + + +DIALOG_IMPORT_GFX::~DIALOG_IMPORT_GFX() +{ + m_offsetSelection = m_rbOffsetOption->GetSelection(); + m_layer = m_SelLayerBox->GetLayerSelection(); + + if( m_config ) + { + m_config->Write( IMPORT_GFX_LAYER_OPTION_KEY, (long)m_layer ); + m_config->Write( IMPORT_GFX_COORD_ORIGIN_KEY, m_offsetSelection ); + m_config->Write( IMPORT_GFX_LAST_FILE_KEY, m_filename ); + + m_config->Write( IMPORT_GFX_GRID_UNITS_KEY, GetPCBGridUnits() ); + m_config->Write( IMPORT_GFX_GRID_OFFSET_X_KEY, m_PCBXCoord->GetValue() ); + m_config->Write( IMPORT_GFX_GRID_OFFSET_Y_KEY, m_PCBYCoord->GetValue() ); + } +} + + +void DIALOG_IMPORT_GFX::OnBrowseFiles( wxCommandEvent& event ) +{ + wxString path; + wxString filename; + + if( !m_filename.IsEmpty() ) + { + wxFileName fn( m_filename ); + path = fn.GetPath(); + filename = fn.GetFullName(); + } + + // Generate the list of handled file formats + wxString wildcardsDesc; + wxString allWildcards; + + for( auto pluginType : GRAPHICS_IMPORT_MGR::GFX_FILE_TYPES ) + { + auto plugin = GRAPHICS_IMPORT_MGR::GetPlugin( pluginType ); + const auto wildcards = plugin->GetWildcards(); + + wildcardsDesc += "|" + plugin->GetName() + " (" + wildcards + ")|" + wildcards; + allWildcards += wildcards + ";"; + } + + wildcardsDesc = "All supported formats|" + allWildcards + wildcardsDesc; + + wxFileDialog dlg( m_parent, _( "Open File" ), path, filename, + wildcardsDesc, wxFD_OPEN|wxFD_FILE_MUST_EXIST ); + + if( dlg.ShowModal() != wxID_OK ) + return; + + wxString fileName = dlg.GetPath(); + + if( fileName.IsEmpty() ) + return; + + m_filename = fileName; + m_textCtrlFileName->SetValue( fileName ); +} + + +void DIALOG_IMPORT_GFX::OnOKClick( wxCommandEvent& event ) +{ + m_filename = m_textCtrlFileName->GetValue(); + + if( m_filename.IsEmpty() ) + return; + + double offsetX = 0; + double offsetY = 0; + + m_offsetSelection = m_rbOffsetOption->GetSelection(); + + switch( m_offsetSelection ) + { + case 0: + offsetX = m_parent->GetPageSizeIU().x * MM_PER_IU / 2; + offsetY = m_parent->GetPageSizeIU().y * MM_PER_IU / 2; + break; + + case 1: + break; + + case 2: + offsetY = m_parent->GetPageSizeIU().y * MM_PER_IU / 2; + break; + + case 3: + offsetY = m_parent->GetPageSizeIU().y * MM_PER_IU; + break; + + case 4: + GetPCBGridOffsets( offsetX, offsetY ); + + if( GetPCBGridUnits() ) + { + offsetX *= 25.4; + offsetY *= 25.4; + } + break; + } + + // Set coordinates offset for import (offset is given in mm) + //m_importer.SetOffset( offsetX, offsetY ); + m_layer = m_SelLayerBox->GetLayerSelection(); + m_importer->SetLayer( PCB_LAYER_ID( m_layer ) ); + auto plugin = GRAPHICS_IMPORT_MGR::GetPluginByExt( wxFileName( m_filename ).GetExt() ); + + if( plugin ) + { + m_importer->SetScale( 1e6 ); // @todo: add a setting in the dialog and apply it here + m_importer->SetLineWidth( 0.1 ); // @todo add a setting in the dialog and apply it here + m_importer->SetPlugin( std::move( plugin ) ); + + if( m_importer->Load( m_filename ) ) + m_importer->Import( 1.0, 1.0 ); // @todo + + EndModal( wxID_OK ); + } + else + { + DisplayError( this, _( "There is no plugin to handle this file type" ) ); + } +} + +void DIALOG_IMPORT_GFX::onChangeHeight( wxUpdateUIEvent &event) +{ + double heightInput; + + heightInput = DoubleValueFromString(UNSCALED_UNITS,m_tcHeight->GetValue()); + + if(m_cbKeepAspectRatio->GetValue()) + { + // @todo: implement scaling of Y + } +} + + +bool InvokeDialogImportGfxBoard( PCB_BASE_FRAME* aCaller ) +{ + DIALOG_IMPORT_GFX dlg( aCaller ); + bool success = ( dlg.ShowModal() == wxID_OK ); + + if( success ) + { + PICKED_ITEMS_LIST picklist; + BOARD* board = aCaller->GetBoard(); + auto& items = dlg.GetImportedItems(); + + for( auto it = items.begin(); it != items.end(); ++it ) + { + BOARD_ITEM* item = static_cast( it->release() ); + board->Add( item ); + + ITEM_PICKER itemWrapper( item, UR_NEW ); + picklist.PushItem( itemWrapper ); + } + + aCaller->SaveCopyInUndoList( picklist, UR_NEW, wxPoint( 0, 0 ) ); + aCaller->OnModify(); + } + + return success; +} + + +bool InvokeDialogImportGfxModule( PCB_BASE_FRAME* aCaller, MODULE* aModule ) +{ + wxASSERT( aModule ); + + DIALOG_IMPORT_GFX dlg( aCaller, true ); + bool success = ( dlg.ShowModal() == wxID_OK ); + + if( success ) + { + aCaller->SaveCopyInUndoList( aModule, UR_CHANGED ); + aCaller->OnModify(); + auto& list = dlg.GetImportedItems(); + + for( auto it = list.begin(); it != list.end(); ++it ) + { + aModule->Add( static_cast( it->release() ) ); + } + } + + return success; +} + + +void DIALOG_IMPORT_GFX::OriginOptionOnUpdateUI( wxUpdateUIEvent& event ) +{ + bool enable = m_rbOffsetOption->GetSelection() == 4; + + m_PCBGridUnits->Enable( enable ); + m_PCBXCoord->Enable( enable ); + m_PCBYCoord->Enable( enable ); +} + + +int DIALOG_IMPORT_GFX::GetPCBGridUnits( void ) +{ + return m_PCBGridUnits->GetSelection(); +} + + +void DIALOG_IMPORT_GFX::GetPCBGridOffsets( double &aXOffset, double &aYOffset ) +{ + aXOffset = DoubleValueFromString( UNSCALED_UNITS, m_PCBXCoord->GetValue() ); + aYOffset = DoubleValueFromString( UNSCALED_UNITS, m_PCBYCoord->GetValue() ); + return; +} diff --git a/pcbnew/import_gfx/dialog_import_gfx.h b/pcbnew/import_gfx/dialog_import_gfx.h new file mode 100644 index 0000000000..1b70b04d6d --- /dev/null +++ b/pcbnew/import_gfx/dialog_import_gfx.h @@ -0,0 +1,68 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2016 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dialog_import_gfx_base.h" +#include + +class PCB_BASE_FRAME; + +class DIALOG_IMPORT_GFX : public DIALOG_IMPORT_GFX_BASE +{ +public: + DIALOG_IMPORT_GFX( PCB_BASE_FRAME* aParent, bool aUseModuleItems = false ); + ~DIALOG_IMPORT_GFX(); + + /** + * Function GetImportedItems() + * + * Returns a list of items imported from a vector graphics file. + */ + std::list>& GetImportedItems() + { + return m_importer->GetItems(); + } + +private: + PCB_BASE_FRAME* m_parent; + wxConfigBase* m_config; // Current config + std::unique_ptr m_importer; + int m_gridUnits; + double m_gridOffsetX; + double m_gridOffsetY; + double m_newHeight; + double m_newWidth; + + static wxString m_filename; + static int m_offsetSelection; + static LAYER_NUM m_layer; + + // Virtual event handlers + void OnCancelClick( wxCommandEvent& event ) override { event.Skip(); } + void OnOKClick( wxCommandEvent& event ) override; + void OnBrowseFiles( wxCommandEvent& event ) override; + void OriginOptionOnUpdateUI( wxUpdateUIEvent& event ) override; + void onChangeHeight( wxUpdateUIEvent& event ) override; + int GetPCBGridUnits( void ); + void GetPCBGridOffsets( double &aXOffset, double &aYOffset ); +}; diff --git a/pcbnew/import_gfx/dialog_import_gfx_base.cpp b/pcbnew/import_gfx/dialog_import_gfx_base.cpp new file mode 100644 index 0000000000..bc8e34f2de --- /dev/null +++ b/pcbnew/import_gfx/dialog_import_gfx_base.cpp @@ -0,0 +1,216 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jul 11 2018) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "pcb_layer_box_selector.h" + +#include "dialog_import_gfx_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_IMPORT_GFX_BASE::DIALOG_IMPORT_GFX_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* bSizerMain; + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizerFile; + bSizerFile = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText37 = new wxStaticText( this, wxID_ANY, _("File:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText37->Wrap( -1 ); + bSizerFile->Add( m_staticText37, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_textCtrlFileName = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_textCtrlFileName->SetMinSize( wxSize( 300,-1 ) ); + + bSizerFile->Add( m_textCtrlFileName, 1, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxTOP, 5 ); + + m_buttonBrowse = new wxButton( this, wxID_ANY, _("Browse"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerFile->Add( m_buttonBrowse, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxTOP, 5 ); + + + bSizerMain->Add( bSizerFile, 0, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer10; + bSizer10 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer5; + bSizer5 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText3 = new wxStaticText( this, wxID_ANY, _("Units:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + bSizer5->Add( m_staticText3, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + wxString m_PCBGridUnitsChoices[] = { _("mm"), _("inch") }; + int m_PCBGridUnitsNChoices = sizeof( m_PCBGridUnitsChoices ) / sizeof( wxString ); + m_PCBGridUnits = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_PCBGridUnitsNChoices, m_PCBGridUnitsChoices, 0 ); + m_PCBGridUnits->SetSelection( 0 ); + m_PCBGridUnits->SetToolTip( _("Select PCB grid units") ); + + bSizer5->Add( m_PCBGridUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer10->Add( bSizer5, 0, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer3; + bSizer3 = new wxBoxSizer( wxHORIZONTAL ); + + wxString m_rbOffsetOptionChoices[] = { _("Center of page"), _("Upper left corner of page"), _("Center left side of page"), _("Lower left corner of page"), _("User defined position") }; + int m_rbOffsetOptionNChoices = sizeof( m_rbOffsetOptionChoices ) / sizeof( wxString ); + m_rbOffsetOption = new wxRadioBox( this, wxID_ORIGIN_SELECT, _("Place origin (0,0) point:"), wxDefaultPosition, wxDefaultSize, m_rbOffsetOptionNChoices, m_rbOffsetOptionChoices, 1, wxRA_SPECIFY_COLS ); + m_rbOffsetOption->SetSelection( 0 ); + bSizer3->Add( m_rbOffsetOption, 0, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer4; + bSizer4 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer6; + bSizer6 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText4 = new wxStaticText( this, wxID_ANY, _("X Position:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText4->Wrap( -1 ); + bSizer6->Add( m_staticText4, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_PCBXCoord = new wxTextCtrl( this, wxID_ANY, _("0.0"), wxDefaultPosition, wxDefaultSize, 0 ); + #ifdef __WXGTK__ + if ( !m_PCBXCoord->HasFlag( wxTE_MULTILINE ) ) + { + m_PCBXCoord->SetMaxLength( 10 ); + } + #else + m_PCBXCoord->SetMaxLength( 10 ); + #endif + m_PCBXCoord->SetToolTip( _("DXF origin on PCB Grid, X Coordinate") ); + + bSizer6->Add( m_PCBXCoord, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer4->Add( bSizer6, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText5 = new wxStaticText( this, wxID_ANY, _("Y Position:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText5->Wrap( -1 ); + bSizer7->Add( m_staticText5, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_PCBYCoord = new wxTextCtrl( this, wxID_ANY, _("0.0"), wxDefaultPosition, wxDefaultSize, 0 ); + #ifdef __WXGTK__ + if ( !m_PCBYCoord->HasFlag( wxTE_MULTILINE ) ) + { + m_PCBYCoord->SetMaxLength( 10 ); + } + #else + m_PCBYCoord->SetMaxLength( 10 ); + #endif + m_PCBYCoord->SetToolTip( _("DXF origin on PCB Grid, Y Coordinate") ); + + bSizer7->Add( m_PCBYCoord, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer4->Add( bSizer7, 1, wxEXPAND, 5 ); + + + bSizer3->Add( bSizer4, 1, wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizer10->Add( bSizer3, 0, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer101; + bSizer101 = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizer11; + bSizer11 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticText6 = new wxStaticText( this, wxID_ANY, _("Height:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText6->Wrap( -1 ); + bSizer11->Add( m_staticText6, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_tcHeight = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bSizer11->Add( m_tcHeight, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText7 = new wxStaticText( this, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText7->Wrap( -1 ); + bSizer11->Add( m_staticText7, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_tcWidth = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_tcWidth->Enable( false ); + + bSizer11->Add( m_tcWidth, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer101->Add( bSizer11, 0, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer15; + bSizer15 = new wxBoxSizer( wxVERTICAL ); + + m_cbKeepAspectRatio = new wxCheckBox( this, wxID_ANY, _("Keep aspect ratio"), wxDefaultPosition, wxDefaultSize, 0 ); + m_cbKeepAspectRatio->SetValue(true); + bSizer15->Add( m_cbKeepAspectRatio, 0, wxALL, 5 ); + + + bSizer101->Add( bSizer15, 0, wxALL|wxEXPAND, 5 ); + + + bSizer10->Add( bSizer101, 0, wxALL|wxEXPAND, 5 ); + + + bSizerMain->Add( bSizer10, 1, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizer8; + bSizer8 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextBrdlayer = new wxStaticText( this, wxID_ANY, _("Layer:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextBrdlayer->Wrap( -1 ); + bSizer8->Add( m_staticTextBrdlayer, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP, 5 ); + + m_SelLayerBox = new PCB_LAYER_BOX_SELECTOR( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bSizer8->Add( m_SelLayerBox, 1, wxALL|wxEXPAND, 5 ); + + + bSizerMain->Add( bSizer8, 0, wxALL|wxEXPAND, 5 ); + + m_staticline8 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizerMain->Add( m_staticline8, 0, wxALL|wxEXPAND, 5 ); + + m_sdbSizer1 = new wxStdDialogButtonSizer(); + m_sdbSizer1OK = new wxButton( this, wxID_OK ); + m_sdbSizer1->AddButton( m_sdbSizer1OK ); + m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer1->AddButton( m_sdbSizer1Cancel ); + m_sdbSizer1->Realize(); + + bSizerMain->Add( m_sdbSizer1, 0, wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnBrowseFiles ), NULL, this ); + m_rbOffsetOption->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::OriginOptionOnUpdateUI ), NULL, this ); + m_tcHeight->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::onChangeHeight ), NULL, this ); + m_cbKeepAspectRatio->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::onKeepAspectRatioUpdate ), NULL, this ); + m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnCancelClick ), NULL, this ); + m_sdbSizer1OK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnOKClick ), NULL, this ); +} + +DIALOG_IMPORT_GFX_BASE::~DIALOG_IMPORT_GFX_BASE() +{ + // Disconnect Events + m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnBrowseFiles ), NULL, this ); + m_rbOffsetOption->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::OriginOptionOnUpdateUI ), NULL, this ); + m_tcHeight->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::onChangeHeight ), NULL, this ); + m_cbKeepAspectRatio->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_BASE::onKeepAspectRatioUpdate ), NULL, this ); + m_sdbSizer1Cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnCancelClick ), NULL, this ); + m_sdbSizer1OK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_BASE::OnOKClick ), NULL, this ); + +} diff --git a/pcbnew/import_gfx/dialog_import_gfx_base.fbp b/pcbnew/import_gfx/dialog_import_gfx_base.fbp new file mode 100644 index 0000000000..b305c2a55e --- /dev/null +++ b/pcbnew/import_gfx/dialog_import_gfx_base.fbp @@ -0,0 +1,1963 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_import_gfx_base + 1000 + none + + 1 + dialog_import_gfx + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_IMPORT_GFX_BASE + + -1,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Import vector graphics file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizerMain + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + + bSizerFile + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + File: + 0 + + 0 + + + 0 + + 1 + m_staticText37 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxTOP + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + 300,-1 + 1 + m_textCtrlFileName + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxTOP + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Browse + + 0 + + 0 + + + 0 + + 1 + m_buttonBrowse + 1 + + + protected + 1 + + + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + OnBrowseFiles + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + + bSizer10 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + + bSizer5 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Units: + 0 + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "mm" "inch" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_PCBGridUnits + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + Select PCB grid units + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + bSizer3 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "Center of page" "Upper left corner of page" "Center left side of page" "Lower left corner of page" "User defined position" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ORIGIN_SELECT + Place origin (0,0) point: + 1 + + 0 + + + 0 + + 1 + m_rbOffsetOption + 1 + + + protected + 1 + + Resizable + 0 + 1 + + wxRA_SPECIFY_COLS + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OriginOptionOnUpdateUI + + + + 5 + wxALIGN_CENTER_VERTICAL + 1 + + + bSizer4 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer6 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + X Position: + 0 + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 10 + + 0 + + 1 + m_PCBXCoord + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + DXF origin on PCB Grid, X Coordinate + + wxFILTER_NUMERIC + wxTextValidator + + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer7 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Y Position: + 0 + + 0 + + + 0 + + 1 + m_staticText5 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 10 + + 0 + + 1 + m_PCBYCoord + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + DXF origin on PCB Grid, Y Coordinate + + wxFILTER_NUMERIC + wxTextValidator + + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + bSizer101 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + + bSizer11 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Height: + 0 + + 0 + + + 0 + + 1 + m_staticText6 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Set imported image height in PCB + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_tcHeight + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NUMERIC + wxTextValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + onChangeHeight + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Width: + 0 + + 0 + + + 0 + + 1 + m_staticText7 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Set imported image width in PCB + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_tcWidth + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NUMERIC + wxTextValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + bSizer15 + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + Keep aspect ratio of original image + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Keep aspect ratio + + 0 + + + 0 + + 1 + m_cbKeepAspectRatio + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + onKeepAspectRatioUpdate + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + + bSizer8 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxLEFT|wxTOP + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Layer: + 0 + + 0 + + + 0 + + 1 + m_staticTextBrdlayer + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_SelLayerBox + 1 + + + protected + 1 + + Resizable + -1 + 1 + + + PCB_LAYER_BOX_SELECTOR; pcb_layer_box_selector.h; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_staticline8 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_RIGHT|wxBOTTOM|wxLEFT|wxRIGHT + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer1 + protected + + OnCancelClick + + + + OnOKClick + + + + + + + + diff --git a/pcbnew/import_gfx/dialog_import_gfx_base.h b/pcbnew/import_gfx/dialog_import_gfx_base.h new file mode 100644 index 0000000000..3784ebac4d --- /dev/null +++ b/pcbnew/import_gfx/dialog_import_gfx_base.h @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jul 11 2018) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_IMPORT_GFX_BASE_H__ +#define __DIALOG_IMPORT_GFX_BASE_H__ + +#include +#include +#include +class PCB_LAYER_BOX_SELECTOR; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +#define wxID_ORIGIN_SELECT 1000 + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_IMPORT_GFX_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_IMPORT_GFX_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticText37; + wxTextCtrl* m_textCtrlFileName; + wxButton* m_buttonBrowse; + wxStaticText* m_staticText3; + wxChoice* m_PCBGridUnits; + wxRadioBox* m_rbOffsetOption; + wxStaticText* m_staticText4; + wxTextCtrl* m_PCBXCoord; + wxStaticText* m_staticText5; + wxTextCtrl* m_PCBYCoord; + wxStaticText* m_staticText6; + wxTextCtrl* m_tcHeight; + wxStaticText* m_staticText7; + wxTextCtrl* m_tcWidth; + wxCheckBox* m_cbKeepAspectRatio; + wxStaticText* m_staticTextBrdlayer; + PCB_LAYER_BOX_SELECTOR* m_SelLayerBox; + wxStaticLine* m_staticline8; + wxStdDialogButtonSizer* m_sdbSizer1; + wxButton* m_sdbSizer1OK; + wxButton* m_sdbSizer1Cancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnBrowseFiles( wxCommandEvent& event ) { event.Skip(); } + virtual void OriginOptionOnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); } + virtual void onChangeHeight( wxUpdateUIEvent& event ) { event.Skip(); } + virtual void onKeepAspectRatioUpdate( wxUpdateUIEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOKClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_IMPORT_GFX_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Import vector graphics file"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_IMPORT_GFX_BASE(); + +}; + +#endif //__DIALOG_IMPORT_GFX_BASE_H__ diff --git a/pcbnew/import_gfx/dxf_import_plugin.cpp b/pcbnew/import_gfx/dxf_import_plugin.cpp new file mode 100644 index 0000000000..59eace3a33 --- /dev/null +++ b/pcbnew/import_gfx/dxf_import_plugin.cpp @@ -0,0 +1,976 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2018 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +// The DXF reader lib (libdxfrw) comes from LibreCAD project, a 2D CAD program +// libdxfrw can be found on http://sourceforge.net/projects/libdxfrw/ +// or (latest sources) on +// https://github.com/LibreCAD/LibreCAD/tree/master/libraries/libdxfrw/src +// +// There is no doc to use it, but have a look to +// https://github.com/LibreCAD/LibreCAD/blob/master/librecad/src/lib/filters/rs_filterdxf.cpp +// and https://github.com/LibreCAD/LibreCAD/blob/master/librecad/src/lib/filters/rs_filterdxf.h +// Each time a dxf entity is read, a "call back" fuction is called +// like void DXF_IMPORT_PLUGIN::addLine( const DRW_Line& data ) when a line is read. +// this function just add the BOARD entity from dxf parameters (start and end point ...) + + +#include "dxf_import_plugin.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +// minimum bulge value before resorting to a line segment; +// the value 0.0218 is equivalent to about 5 degrees arc, +#define MIN_BULGE 0.0218 + +DXF_IMPORT_PLUGIN::DXF_IMPORT_PLUGIN() : DL_CreationAdapter() +{ + m_xOffset = 0.0; // X coord offset for conversion (in mm) + m_yOffset = 0.0; // Y coord offset for conversion (in mm) + m_DXF2mm = 1.0; // The scale factor to convert DXF units to mm + m_version = 0; // the dxf version, not yet used + m_defaultThickness = 0.2; // default thickness (in mm) + m_brdLayer = Dwgs_User; // The default import layer + m_importAsfootprintGraphicItems = true; + m_minX = std::numeric_limits::max(); + m_maxX = std::numeric_limits::min(); + m_minY = std::numeric_limits::max(); + m_maxY = std::numeric_limits::min(); +} + + +DXF_IMPORT_PLUGIN::~DXF_IMPORT_PLUGIN() +{ +} + + +bool DXF_IMPORT_PLUGIN::Load( const wxString& aFileName ) +{ + //dxfRW dxf( aFileName.ToUTF8() ); + //return dxf.read( this, true ); + return true; +} + + +bool DXF_IMPORT_PLUGIN::Import( float aXScale, float aYScale ) +{ + //wxCHECK( m_importer, false ); + //m_internalImporter.ImportTo( *m_importer ); + + return true; +} + + +unsigned int DXF_IMPORT_PLUGIN::GetImageWidth() const { + return m_maxX - m_minX; +} + + +unsigned int DXF_IMPORT_PLUGIN::GetImageHeight() const { + return m_maxY - m_minY; +} + +// coordinate conversions from dxf to internal units +int DXF_IMPORT_PLUGIN::mapX( double aDxfCoordX ) +{ + return Millimeter2iu( m_xOffset + ( aDxfCoordX * m_DXF2mm ) ); +} + + +int DXF_IMPORT_PLUGIN::mapY( double aDxfCoordY ) +{ + return Millimeter2iu( m_yOffset - ( aDxfCoordY * m_DXF2mm ) ); +} + + +int DXF_IMPORT_PLUGIN::mapDim( double aDxfValue ) +{ + return Millimeter2iu( aDxfValue * m_DXF2mm ); +} + + +int DXF_IMPORT_PLUGIN::mapWidth( double aDxfWidth ) +{ + // Always return the default line width +#if 0 + // mapWidth returns the aDxfValue if aDxfWidth > 0 m_defaultThickness + if( aDxfWidth > 0.0 ) + return Millimeter2iu( aDxfWidth * m_DXF2mm ); +#endif + return Millimeter2iu( m_defaultThickness ); +} + +bool DXF_IMPORT_PLUGIN::ImportDxfFile( const wxString& aFile ) +{ + LOCALE_IO locale; + + DL_Dxf dxf_reader; + std::string filename = TO_UTF8( aFile ); + bool success = true; + + if( !dxf_reader.in( filename, this ) ) // if file open failed + success = false; + + return success; +} + + +void DXF_IMPORT_PLUGIN::reportMsg( const char* aMessage ) +{ + // Add message to keep trace of not handled dxf entities + m_messages += aMessage; + m_messages += '\n'; +} + + +void DXF_IMPORT_PLUGIN::addSpline( const DL_SplineData& aData ) +{ + // Called when starting reading a spline + m_curr_entity.Clear(); + m_curr_entity.m_EntityParseStatus = 1; + m_curr_entity.m_EntityFlag = aData.flags; + m_curr_entity.m_EntityType = DL_ENTITY_SPLINE; + m_curr_entity.m_SplineDegree = aData.degree; + m_curr_entity.m_SplineTangentStartX = aData.tangentStartX; + m_curr_entity.m_SplineTangentStartY = aData.tangentStartY; + m_curr_entity.m_SplineTangentEndX = aData.tangentEndX; + m_curr_entity.m_SplineTangentEndY = aData.tangentEndY; + m_curr_entity.m_SplineKnotsCount = aData.nKnots; + m_curr_entity.m_SplineControlCount = aData.nControl; + m_curr_entity.m_SplineFitCount = aData.nFit; +} + + +void DXF_IMPORT_PLUGIN::addControlPoint( const DL_ControlPointData& aData ) +{ + // Called for every spline control point, when reading a spline entity + m_curr_entity.m_SplineControlPointList.push_back( SPLINE_CTRL_POINT( aData.x , aData.y, aData.w ) ); +} + +void DXF_IMPORT_PLUGIN::addFitPoint( const DL_FitPointData& aData ) +{ + // Called for every spline fit point, when reading a spline entity + // we store only the X,Y coord values in a wxRealPoint + m_curr_entity.m_SplineFitPointList.push_back( wxRealPoint( aData.x, aData.y ) ); +} + + +void DXF_IMPORT_PLUGIN::addKnot( const DL_KnotData& aData) +{ + // Called for every spline knot value, when reading a spline entity + m_curr_entity.m_SplineKnotsList.push_back( aData.k ); +} + + +void DXF_IMPORT_PLUGIN::addLayer( const DL_LayerData& aData ) +{ + // Not yet useful in Pcbnew. +#if 0 + wxString name = wxString::FromUTF8( aData.name.c_str() ); + wxLogMessage( name ); +#endif +} + + +void DXF_IMPORT_PLUGIN::addLine( const DL_LineData& aData ) +{ + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + wxPoint start( mapX( aData.x1 ), mapY( aData.y1 ) ); + segm->SetStart( start ); + wxPoint end( mapX( aData.x2 ), mapY( aData.y2 ) ); + segm->SetEnd( end ); + segm->SetWidth( mapWidth( attributes.getWidth() ) ); + m_newItemsList.push_back( segm ); +} + + +void DXF_IMPORT_PLUGIN::addPolyline(const DL_PolylineData& aData ) +{ + // Convert DXF Polylines into a series of KiCad Lines and Arcs. + // A Polyline (as opposed to a LWPolyline) may be a 3D line or + // even a 3D Mesh. The only type of Polyline which is guaranteed + // to import correctly is a 2D Polyline in X and Y, which is what + // we assume of all Polylines. The width used is the width of the Polyline. + // per-vertex line widths, if present, are ignored. + + m_curr_entity.Clear(); + m_curr_entity.m_EntityParseStatus = 1; + m_curr_entity.m_EntityFlag = aData.flags; + m_curr_entity.m_EntityType = DL_ENTITY_POLYLINE; +} + + +void DXF_IMPORT_PLUGIN::addVertex( const DL_VertexData& aData ) +{ + if( m_curr_entity.m_EntityParseStatus == 0 ) + return; // Error + + int lineWidth = mapWidth( attributes.getWidth() ); + + const DL_VertexData* vertex = &aData; + + if( m_curr_entity.m_EntityParseStatus == 1 ) // This is the first vertex of an entity + { + m_curr_entity.m_LastCoordinate.x = m_xOffset + vertex->x * m_DXF2mm; + m_curr_entity.m_LastCoordinate.y = m_yOffset - vertex->y * m_DXF2mm; + m_curr_entity.m_PolylineStart = m_curr_entity.m_LastCoordinate; + m_curr_entity.m_BulgeVertex = vertex->bulge; + m_curr_entity.m_EntityParseStatus = 2; + return; + } + + + wxRealPoint seg_end( m_xOffset + vertex->x * m_DXF2mm, + m_yOffset - vertex->y * m_DXF2mm ); + + if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE ) + insertLine( m_curr_entity.m_LastCoordinate, seg_end, lineWidth ); + else + insertArc( m_curr_entity.m_LastCoordinate, seg_end, m_curr_entity.m_BulgeVertex, lineWidth ); + + m_curr_entity.m_LastCoordinate = seg_end; + m_curr_entity.m_BulgeVertex = vertex->bulge; +} + + +void DXF_IMPORT_PLUGIN::endEntity() +{ + if( m_curr_entity.m_EntityType == DL_ENTITY_POLYLINE || + m_curr_entity.m_EntityType == DL_ENTITY_LWPOLYLINE ) + { + // Polyline flags bit 0 indicates closed (1) or open (0) polyline + if( m_curr_entity.m_EntityFlag & 1 ) + { + int lineWidth = mapWidth( attributes.getWidth() ); + + if( std::abs( m_curr_entity.m_BulgeVertex ) < MIN_BULGE ) + insertLine( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart, lineWidth ); + else + insertArc( m_curr_entity.m_LastCoordinate, m_curr_entity.m_PolylineStart, + m_curr_entity.m_BulgeVertex, lineWidth ); + } + } + + if( m_curr_entity.m_EntityType == DL_ENTITY_SPLINE ) + { + int lineWidth = mapWidth( attributes.getWidth() ); + insertSpline( lineWidth ); + } + + m_curr_entity.Clear(); +} + + +void DXF_IMPORT_PLUGIN::addCircle( const DL_CircleData& aData ) +{ + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_CIRCLE ); + wxPoint center( mapX( aData.cx ), mapY( aData.cy ) ); + segm->SetCenter( center ); + wxPoint circle_start( mapX( aData.cx + aData.radius ), mapY( aData.cy ) ); + segm->SetArcStart( circle_start ); + segm->SetWidth( mapWidth( attributes.getWidth() ) ); + m_newItemsList.push_back( segm ); +} + + +/* + * Import Arc entities. + */ +void DXF_IMPORT_PLUGIN::addArc( const DL_ArcData& aData ) +{ + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_ARC ); + + // Init arc centre: + wxPoint center( mapX( aData.cx ), mapY( aData.cy ) ); + segm->SetCenter( center ); + + // Init arc start point + double arcStartx = aData.radius; + double arcStarty = 0; + + // aData.anglex is in degrees. Our internal units are 0.1 degree + // so convert DXF angles to our units + #define DXF2ANGLEUI 10 + double startangle = aData.angle1 * DXF2ANGLEUI; + double endangle = aData.angle2 * DXF2ANGLEUI; + + RotatePoint( &arcStartx, &arcStarty, -startangle ); + wxPoint arcStart( mapX( arcStartx + aData.cx ), + mapY( arcStarty + aData.cy ) ); + segm->SetArcStart( arcStart ); + + // calculate arc angle (arcs are CCW, and should be < 0 in Pcbnew) + double angle = -( endangle - startangle ); + + if( angle > 0.0 ) + angle -= 3600.0; + + segm->SetAngle( angle ); + + segm->SetWidth( mapWidth( attributes.getWidth() ) ); + m_newItemsList.push_back( segm ); +} + + +void DXF_IMPORT_PLUGIN::addText( const DL_TextData& aData ) +{ + BOARD_ITEM* brdItem; + EDA_TEXT* textItem; + + if( m_importAsfootprintGraphicItems ) + { + TEXTE_MODULE* modText = new TEXTE_MODULE( NULL ); + brdItem = static_cast< BOARD_ITEM* >( modText ); + textItem = static_cast< EDA_TEXT* >( modText ); + } + else + { + TEXTE_PCB* pcbText = new TEXTE_PCB( NULL ); + brdItem = static_cast< BOARD_ITEM* >( pcbText ); + textItem = static_cast< EDA_TEXT* >( pcbText ); + } + + brdItem->SetLayer( ToLAYER_ID( m_brdLayer ) ); + + wxPoint refPoint( mapX( aData.ipx ), mapY( aData.ipy ) ); + wxPoint secPoint( mapX( aData.apx ), mapY( aData.apy ) ); + + if( aData.vJustification != 0 || aData.hJustification != 0 || aData.hJustification == 4 ) + { + if( aData.hJustification != 3 && aData.hJustification != 5 ) + { + wxPoint tmp = secPoint; + secPoint = refPoint; + refPoint = tmp; + } + } + + switch( aData.vJustification ) + { + case 0: //DRW_Text::VBaseLine: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + break; + + case 1: //DRW_Text::VBottom: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + break; + + case 2: //DRW_Text::VMiddle: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); + break; + + case 3: //DRW_Text::VTop: + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); + break; + } + + switch( aData.hJustification ) + { + case 0: //DRW_Text::HLeft: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + + case 1: //DRW_Text::HCenter: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + break; + + case 2: //DRW_Text::HRight: + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); + break; + + case 3: //DRW_Text::HAligned: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + + case 4: //DRW_Text::HMiddle: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + break; + + case 5: //DRW_Text::HFit: + // no equivalent options in text pcb. + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + break; + } + +#if 0 + wxString sty = wxString::FromUTF8( aData.style.c_str() ); + sty = sty.ToLower(); + + if( aData.textgen == 2 ) + { + // Text dir = left to right; + } else if( aData.textgen == 4 ) + { + // Text dir = top to bottom; + } else + { + } +#endif + + wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) ); + + textItem->SetTextPos( refPoint ); + textItem->SetTextAngle( aData.angle * 10 ); + + // The 0.8 factor gives a better height/width ratio with our font + textItem->SetTextWidth( mapDim( aData.height * 0.8 ) ); + textItem->SetTextHeight( mapDim( aData.height ) ); + textItem->SetThickness( mapWidth( aData.height * DEFAULT_TEXT_WIDTH ) ); // Gives a reasonable text thickness + textItem->SetText( text ); + + m_newItemsList.push_back( static_cast< BOARD_ITEM* >( brdItem ) ); +} + + +void DXF_IMPORT_PLUGIN::addMText( const DL_MTextData& aData ) +{ + wxString text = toNativeString( wxString::FromUTF8( aData.text.c_str() ) ); + wxString attrib, tmp; + + /* Some texts start by '\' and have formating chars (font name, font option...) + * ending with ';' + * Here are some mtext formatting codes: + * Format code Purpose + * \0...\o Turns overline on and off + * \L...\l Turns underline on and off + * \~ Inserts a nonbreaking space + \\ Inserts a backslash + \\\{...\} Inserts an opening and closing brace + \\ \File name; Changes to the specified font file + \\ \Hvalue; Changes to the text height specified in drawing units + \\ \Hvaluex; Changes the text height to a multiple of the current text height + \\ \S...^...; Stacks the subsequent text at the \, #, or ^ symbol + \\ \Tvalue; Adjusts the space between characters, from.75 to 4 times + \\ \Qangle; Changes obliquing angle + \\ \Wvalue; Changes width factor to produce wide text + \\ \A Sets the alignment value; valid values: 0, 1, 2 (bottom, center, top) while( text.StartsWith( wxT("\\") ) ) + */ + while( text.StartsWith( wxT( "\\" ) ) ) + { + attrib << text.BeforeFirst( ';' ); + tmp = text.AfterFirst( ';' ); + text = tmp; + } + + BOARD_ITEM* brdItem; + EDA_TEXT* textItem; + + if( m_importAsfootprintGraphicItems ) + { + TEXTE_MODULE* modText = new TEXTE_MODULE( NULL ); + brdItem = static_cast< BOARD_ITEM* >( modText ); + textItem = static_cast< EDA_TEXT* >( modText ); + } + else + { + TEXTE_PCB* pcbText = new TEXTE_PCB( NULL ); + brdItem = static_cast< BOARD_ITEM* >( pcbText ); + textItem = static_cast< EDA_TEXT* >( pcbText ); + } + + brdItem->SetLayer( ToLAYER_ID( m_brdLayer ) ); + wxPoint textpos( mapX( aData.ipx ), mapY( aData.ipy ) ); + + textItem->SetTextPos( textpos ); + textItem->SetTextAngle( aData.angle * 10 ); + + // The 0.8 factor gives a better height/width ratio with our font + textItem->SetTextWidth( mapDim( aData.height * 0.8 ) ); + textItem->SetTextHeight( mapDim( aData.height ) ); + textItem->SetThickness( mapWidth( aData.height * DEFAULT_TEXT_WIDTH ) ); // Gives a reasonable text thickness + textItem->SetText( text ); + + // Initialize text justifications: + if( aData.attachmentPoint <= 3 ) + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); + } + else if( aData.attachmentPoint <= 6 ) + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); + } + else + { + textItem->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); + } + + if( aData.attachmentPoint % 3 == 1 ) + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); + } + else if( aData.attachmentPoint % 3 == 2 ) + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); + } + else + { + textItem->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); + } + +#if 0 // These setting have no meaning in Pcbnew + if( data.alignH == 1 ) + { + // Text is left to right; + } + else if( data.alignH == 3 ) + { + // Text is top to bottom; + } + else + { + // use ByStyle; + } + + if( aData.alignV == 1 ) + { + // use AtLeast; + } + else + { + // useExact; + } +#endif + + m_newItemsList.push_back( static_cast< BOARD_ITEM* >( brdItem ) ); +} + + +void DXF_IMPORT_PLUGIN::setVariableInt( const std::string& key, int value, int code ) +{ + // Called for every int variable in the DXF file (e.g. "$INSUNITS"). + + if( key == "$DWGCODEPAGE" ) + { + m_codePage = value; + return; + } + + if( key == "$INSUNITS" ) // Drawing units + { + switch( value ) + { + case 1: // inches + m_DXF2mm = 25.4; + break; + + case 2: // feet + m_DXF2mm = 304.8; + break; + + case 4: // mm + m_DXF2mm = 1.0; + break; + + case 5: // centimeters + m_DXF2mm = 10.0; + break; + + case 6: // meters + m_DXF2mm = 1000.0; + break; + + case 8: // microinches + m_DXF2mm = 2.54e-5; + break; + + case 9: // mils + m_DXF2mm = 0.0254; + break; + + case 10: // yards + m_DXF2mm = 914.4; + break; + + case 11: // Angstroms + m_DXF2mm = 1.0e-7; + break; + + case 12: // nanometers + m_DXF2mm = 1.0e-6; + break; + + case 13: // micrometers + m_DXF2mm = 1.0e-3; + break; + + case 14: // decimeters + m_DXF2mm = 100.0; + break; + + default: + // use the default of 1.0 for: + // 0: Unspecified Units + // 3: miles + // 7: kilometers + // 15: decameters + // 16: hectometers + // 17: gigameters + // 18: AU + // 19: lightyears + // 20: parsecs + m_DXF2mm = 1.0; + break; + } + + return; + } +} + + +void DXF_IMPORT_PLUGIN::setVariableString( const std::string& key, const std::string& value, + int code ) +{ + // Called for every string variable in the DXF file (e.g. "$ACADVER"). +} + + +wxString DXF_IMPORT_PLUGIN::toDxfString( const wxString& aStr ) +{ + wxString res; + int j = 0; + + for( unsigned i = 0; i 175 || c < 11 ) + { + res.append( aStr.Mid( j, i - j ) ); + j = i; + + switch( c ) + { + case 0x0A: + res += wxT( "\\P" ); + break; + + // diameter: +#ifdef __WINDOWS_ + // windows, as always, is special. + case 0x00D8: +#else + case 0x2205: +#endif + res += wxT( "%%C" ); + break; + + // degree: + case 0x00B0: + res += wxT( "%%D" ); + break; + + // plus/minus + case 0x00B1: + res += wxT( "%%P" ); + break; + + default: + j--; + break; + } + + j++; + } + } + + res.append( aStr.Mid( j ) ); + return res; +} + + +wxString DXF_IMPORT_PLUGIN::toNativeString( const wxString& aData ) +{ + wxString res; + + // Ignore font tags: + int j = 0; + + for( unsigned i = 0; i < aData.length(); ++i ) + { + if( aData[ i ] == 0x7B ) // is '{' ? + { + if( aData[ i + 1 ] == 0x5c && aData[ i + 2 ] == 0x66 ) // is "\f" ? + { + // found font tag, append parsed part + res.append( aData.Mid( j, i - j ) ); + + // skip to ';' + for( unsigned k = i + 3; k < aData.length(); ++k ) + { + if( aData[ k ] == 0x3B ) + { + i = j = ++k; + break; + } + } + + // add to '}' + for( unsigned k = i; k < aData.length(); ++k ) + { + if( aData[ k ] == 0x7D ) + { + res.append( aData.Mid( i, k - i ) ); + i = j = ++k; + break; + } + } + } + } + } + + res.append( aData.Mid( j ) ); + +#if 1 + wxRegEx regexp; + // Line feed: + regexp.Compile( wxT( "\\\\P" ) ); + regexp.Replace( &res, wxT( "\n" ) ); + + // Space: + regexp.Compile( wxT( "\\\\~" ) ); + regexp.Replace( &res, wxT( " " ) ); + + // diameter: + regexp.Compile( wxT( "%%[cC]" ) ); +#ifdef __WINDOWS__ + // windows, as always, is special. + regexp.Replace( &res, wxChar( 0xD8 ) ); +#else + // Empty_set, diameter is 0x2300 + regexp.Replace( &res, wxChar( 0x2205 ) ); +#endif + + // degree: + regexp.Compile( wxT( "%%[dD]" ) ); + regexp.Replace( &res, wxChar( 0x00B0 ) ); + // plus/minus + regexp.Compile( wxT( "%%[pP]" ) ); + regexp.Replace( &res, wxChar( 0x00B1 ) ); +#endif + + return res; +} + + +void DXF_IMPORT_PLUGIN::addTextStyle( const DL_StyleData& aData ) +{ + // TODO +} + + +void DXF_IMPORT_PLUGIN::insertLine( const wxRealPoint& aSegStart, + const wxRealPoint& aSegEnd, int aWidth ) +{ + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + wxPoint segment_startpoint( Millimeter2iu( aSegStart.x ), Millimeter2iu( aSegStart.y ) ); + wxPoint segment_endpoint( Millimeter2iu( aSegEnd.x ), Millimeter2iu( aSegEnd.y ) ); + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetStart( segment_startpoint ); + segm->SetEnd( segment_endpoint ); + segm->SetWidth( aWidth ); + + m_newItemsList.push_back( segm ); +} + + +void DXF_IMPORT_PLUGIN::insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, + double aBulge, int aWidth ) +{ + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : new DRAWSEGMENT; + + wxPoint segment_startpoint( Millimeter2iu( aSegStart.x ), Millimeter2iu( aSegStart.y ) ); + wxPoint segment_endpoint( Millimeter2iu( aSegEnd.x ), Millimeter2iu( aSegEnd.y ) ); + + // ensure aBulge represents an angle from +/- ( 0 .. approx 359.8 deg ) + if( aBulge < -2000.0 ) + aBulge = -2000.0; + else if( aBulge > 2000.0 ) + aBulge = 2000.0; + + double ang = 4.0 * atan( aBulge ); + + // reflect the Y values to put everything in a RHCS + wxRealPoint sp( aSegStart.x, -aSegStart.y ); + wxRealPoint ep( aSegEnd.x, -aSegEnd.y ); + // angle from end->start + double offAng = atan2( ep.y - sp.y, ep.x - sp.x ); + // length of subtended segment = 1/2 distance between the 2 points + double d = 0.5 * sqrt( (sp.x - ep.x) * (sp.x - ep.x) + (sp.y - ep.y) * (sp.y - ep.y) ); + // midpoint of the subtended segment + double xm = ( sp.x + ep.x ) * 0.5; + double ym = ( sp.y + ep.y ) * 0.5; + double radius = d / sin( ang * 0.5 ); + + if( radius < 0.0 ) + radius = -radius; + + // calculate the height of the triangle with base d and hypotenuse r + double dh2 = radius * radius - d * d; + + // this should only ever happen due to rounding errors when r == d + if( dh2 < 0.0 ) + dh2 = 0.0; + + double h = sqrt( dh2 ); + + if( ang < 0.0 ) + offAng -= M_PI_2; + else + offAng += M_PI_2; + + // for angles greater than 180 deg we need to flip the + // direction in which the arc center is found relative + // to the midpoint of the subtended segment. + if( ang < -M_PI ) + offAng += M_PI; + else if( ang > M_PI ) + offAng -= M_PI; + + // center point + double cx = h * cos( offAng ) + xm; + double cy = h * sin( offAng ) + ym; + + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_ARC ); + segm->SetCenter( wxPoint( Millimeter2iu( cx ), Millimeter2iu( -cy ) ) ); + + if( ang < 0.0 ) + { + segm->SetArcStart( wxPoint( Millimeter2iu( ep.x ), Millimeter2iu( -ep.y ) ) ); + segm->SetAngle( RAD2DECIDEG( ang ) ); + } + else + { + segm->SetArcStart( wxPoint( Millimeter2iu( sp.x ), Millimeter2iu( -sp.y ) ) ); + segm->SetAngle( RAD2DECIDEG( -ang ) ); + } + + segm->SetWidth( aWidth ); + + m_newItemsList.push_back( segm ); + return; +} + + +#include "tinyspline_lib/tinysplinecpp.h" + +void DXF_IMPORT_PLUGIN::insertSpline( int aWidth ) +{ + #if 0 // Debug only + wxLogMessage("spl deg %d kn %d ctr %d fit %d", + m_curr_entity.m_SplineDegree, + m_curr_entity.m_SplineKnotsList.size(), + m_curr_entity.m_SplineControlPointList.size(), + m_curr_entity.m_SplineFitPointList.size() ); + #endif + + // Very basic conversion to segments + unsigned imax = m_curr_entity.m_SplineControlPointList.size(); + + if( imax < 2 ) // malformed spline + return; + +#if 0 // set to 1 to approximate the spline by segments between 2 control points + wxPoint startpoint( mapX( m_curr_entity.m_SplineControlPointList[0].m_x ), + mapY( m_curr_entity.m_SplineControlPointList[0].m_y ) ); + + for( unsigned int ii = 1; ii < imax; ++ii ) + { + wxPoint endpoint( mapX( m_curr_entity.m_SplineControlPointList[ii].m_x ), + mapY( m_curr_entity.m_SplineControlPointList[ii].m_y ) ); + + if( startpoint != endpoint ) + { + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetStart( startpoint ); + segm->SetEnd( endpoint ); + segm->SetWidth( aWidth ); + m_newItemsList.push_back( segm ); + startpoint = endpoint; + } + } +#else // Use bezier curves, supported by pcbnew, to approximate the spline + tinyspline::BSpline dxfspline( m_curr_entity.m_SplineControlPointList.size(), + /* coord dim */ 2, m_curr_entity.m_SplineDegree ); + std::vector ctrlp; + + for( unsigned ii = 0; ii < imax; ++ii ) + { + ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_x ); + ctrlp.push_back( m_curr_entity.m_SplineControlPointList[ii].m_y ); + } + + dxfspline.setCtrlp( ctrlp ); + dxfspline.setKnots( m_curr_entity.m_SplineKnotsList ); + tinyspline::BSpline beziers( dxfspline.toBeziers() ); + + std::vector coords = beziers.ctrlp(); + + // Each Bezier curve uses 4 vertices (a start point, 2 control points and a end point). + // So we can have more than one Bezier curve ( there are one curve each four vertices) + for( unsigned ii = 0; ii < coords.size(); ii += 8 ) + { + DRAWSEGMENT* segm = ( m_importAsfootprintGraphicItems ) ? + static_cast< DRAWSEGMENT* >( new EDGE_MODULE( NULL ) ) : + new DRAWSEGMENT; + segm->SetLayer( ToLAYER_ID( m_brdLayer ) ); + segm->SetShape( S_CURVE ); + segm->SetStart( wxPoint( mapX( coords[ii] ), mapY( coords[ii+1] ) ) ); + segm->SetBezControl1( wxPoint( mapX( coords[ii+2] ), mapY( coords[ii+3] ) ) ); + segm->SetBezControl2( wxPoint( mapX( coords[ii+4] ), mapY( coords[ii+5] ) ) ); + segm->SetEnd( wxPoint( mapX( coords[ii+6] ), mapY( coords[ii+7] ) ) ); + segm->SetWidth( aWidth ); + segm->RebuildBezierToSegmentsPointsList( aWidth ); + m_newItemsList.push_back( segm ); + } +#endif +} + diff --git a/pcbnew/import_gfx/dxf_import_plugin.h b/pcbnew/import_gfx/dxf_import_plugin.h new file mode 100644 index 0000000000..27938536a0 --- /dev/null +++ b/pcbnew/import_gfx/dxf_import_plugin.h @@ -0,0 +1,335 @@ +/**************************************************************************** + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 1992-2018 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 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +**********************************************************************/ + + +#ifndef DXF2BRD_ITEMS_H +#define DXF2BRD_ITEMS_H + +#include "graphics_import_plugin.h" +#include "graphics_importer_buffer.h" + +#include "dl_dxf.h" +#include "dl_creationadapter.h" +#include "wx/wx.h" +#include + +class BOARD; +class BOARD_ITEM; + +/** + * A helper class to store a spline control point (in X,Y plane only) + */ +struct SPLINE_CTRL_POINT +{ + double m_x; + double m_y; + double m_weight; + + SPLINE_CTRL_POINT( double a_x, double a_y, double a_weight ) + : m_x( a_x ), m_y( a_y ), m_weight( a_weight ) + {} +}; + +/** + * A helper class to parse a DXF entity (polyline and spline) + */ +class DXF2BRD_ENTITY_DATA +{ +public: + int m_EntityType; // the DXF type of entity + int m_EntityParseStatus; // Inside a entity: status od parsing: + // 0 = no entity + // 1 = first item of entity + // 2 = entity in progress + int m_EntityFlag; // a info flag to parse entities + + wxRealPoint m_LastCoordinate; // the last vertex coordinate read (unit = mm) + wxRealPoint m_PolylineStart; // The first point of the polyline entity, when reading a polyline (unit = mm) + double m_BulgeVertex; // the last vertex bulge value read + + // for spline parsing: parameters + unsigned int m_SplineDegree; + unsigned int m_SplineKnotsCount; + unsigned int m_SplineControlCount; + unsigned int m_SplineFitCount; + double m_SplineTangentStartX; // tangeant dir X for the start point + double m_SplineTangentStartY; // tangeant dir Y for the start point + double m_SplineTangentEndX; // tangeant dir X for the end point + double m_SplineTangentEndY; // tangeant dir Y for the end point + + // for spline parsing: buffers to store control points, fit points and knot + std::vector m_SplineKnotsList; // knots list, code 40 + // control points list coordinates, code 10, 20 & 30 (only X and Y cood and Weight) + std::vector m_SplineControlPointList; + // fit points list, code 11, 21 & 31 (only X and Y cood) + std::vector m_SplineFitPointList; + + DXF2BRD_ENTITY_DATA() { Clear(); }; + + // Reset the entity parameters + void Clear() + { + m_EntityType = DL_UNKNOWN; + m_EntityParseStatus = 0; + m_EntityFlag = 0; + m_SplineDegree = 1; + m_SplineKnotsCount = 0; + m_SplineControlCount = 0; + m_SplineFitCount = 0; + m_SplineTangentStartX = 0.0; + m_SplineTangentStartY = 0.0; + m_SplineTangentEndX = 0.0; + m_SplineTangentEndY = 0.0; + m_SplineKnotsList.clear(); + m_SplineControlPointList.clear(); + m_SplineFitPointList.clear(); + } +}; + + +/** + * This class import DXF ASCII files and convert basic entities to board entities. + * It depends on the dxflib library. + */ +class DXF_IMPORT_PLUGIN : public GRAPHICS_IMPORT_PLUGIN, public DL_CreationAdapter +{ +private: + std::list m_newItemsList; // The list of new items added to the board + double m_xOffset; // X coord offset for conversion (in mm) + double m_yOffset; // Y coord offset for conversion (in mm) + double m_defaultThickness; // default line thickness for conversion (in mm) + double m_DXF2mm; // The scale factor to convert DXF units to mm + int m_brdLayer; // The board layer to place imported DXF items + int m_version; // the dxf version, not used here + std::string m_codePage; // The code page, not used here + bool m_importAsfootprintGraphicItems; // Use module items instead of board items when true. + // true when the items are imported in the footprint editor + std::string m_messages; // messages generated during dxf file parsing. + // Each message ends by '\n' + DXF2BRD_ENTITY_DATA m_curr_entity; // the current entity parameters when parsing a DXF entity + + int m_minX, m_maxX; + int m_minY, m_maxY; + + GRAPHICS_IMPORTER_BUFFER m_internalImporter; + + +public: + DXF_IMPORT_PLUGIN(); + ~DXF_IMPORT_PLUGIN(); + + const wxString GetName() const override + { + return "AutoCAD DXF"; + } + + const wxArrayString GetFileExtensions() const override + { + return wxArrayString( 1, { "dxf" } ); + } + + bool Load( const wxString& aFileName ) override; + bool Import( float aXScale, float aYScale ) override; + + unsigned int GetImageWidth() const override; + unsigned int GetImageHeight() const override; + + /** + * Allows the import DXF items converted to board graphic items or footprint + * graphic items. + * @param aImportAsFootprintGraphic = true to import in a footprint, false to import on a board + */ + void ImportAsFootprintGraphic( bool aImportAsFootprintGraphic ) + { + m_importAsfootprintGraphicItems = aImportAsFootprintGraphic; + } + + + /** + * Set the default line width when importing dxf items like lines to Pcbnew. + * because dxf files have no line width explicit parameter, it will be most + * of time the line width of imported lines + * @param aWidth = line width in mm + */ + void SetDefaultLineWidthMM( double aWidth ) + { + m_defaultThickness = aWidth; + } + + /** + * Set the coordinate offset between the imported dxf items + * and Pcbnew. + * because dxf files have the Y axis from bottom to top; + * aOffsetX = 0, and aOffsetY = - vertical page size to import a full page + * @param aOffsetX = the X offset in mm + * @param aOffsetY = the Y offset in mm + */ + void SetOffset( double aOffsetX, double aOffsetY ) + { + m_xOffset = aOffsetX; + m_yOffset = aOffsetY; + } + + /** + * Set the layer number to import dxf items. + * the layer should be a techicanl layer, not a copper layer + */ + void SetBrdLayer( int aBrdLayer ) { m_brdLayer = aBrdLayer; } + + /** + * Implementation of the method used for communicate + * with this filter. + * + * @param aFile = the full filename. + */ + bool ImportDxfFile( const wxString& aFile ); + + /** + * @return the list of new BOARD_ITEM + */ + const std::list& GetItemsList() const + { + return m_newItemsList; + } + + /** + * @return the list of messages in one string. Each message ends by '\n' + */ + std::string& GetMessages() { return m_messages; } + +private: + // report message to keep trace of not supported dxf entities: + void reportMsg( const char* aMessage ); + + // coordinate conversions from dxf to internal units + int mapX( double aDxfCoordX ); + int mapY( double aDxfCoordY ); + int mapDim( double aDxfValue ); + // mapWidth returns ( in internal units) the aDxfValue if aDxfWidth > 0 + // or m_defaultThickness + int mapWidth( double aDxfWidth ); + + // Functions to aid in the creation of a Polyline + void insertLine( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, int aWidth ); + void insertArc( const wxRealPoint& aSegStart, const wxRealPoint& aSegEnd, + double aBulge, int aWidth ); + // Add a dxf spline (stored in m_curr_entity) to the board, after conversion to segments + void insertSpline( int aWidth ); + + // Methods from DL_CreationAdapter: + // They are something like"call back" fonctions, + // called when the corresponding object is read in dxf file + + /** + * Called for every string variable in the DXF file (e.g. "$ACADVER"). + */ + virtual void setVariableString( const std::string& key, const std::string& value, + int code ) override; + + /** + * Called for every int variable in the DXF file (e.g. "$ACADMAINTVER"). + */ + virtual void setVariableInt( const std::string& key, int value, int code ) override; + + /** + * Called for every double variable in the DXF file (e.g. "$DIMEXO"). + */ + virtual void setVariableDouble( const std::string& key, double value, int code ) override {} + + virtual void addLayer( const DL_LayerData& aData ) override; + virtual void addLine( const DL_LineData& aData) override; + virtual void addCircle( const DL_CircleData& aData ) override; + virtual void addArc( const DL_ArcData& aData ) override; + //virtual void addLWPolyline( const DRW_LWPolyline& aData ) override; + virtual void addText( const DL_TextData& aData ) override; + virtual void addPolyline( const DL_PolylineData& aData ) override; + + /** Called for every polyline vertex */ + virtual void addVertex( const DL_VertexData& aData ) override; + virtual void addMText( const DL_MTextData& aData) override; + virtual void addTextStyle( const DL_StyleData& aData ) override; + + virtual void endEntity() override; + + /** Called for every spline */ + virtual void addSpline( const DL_SplineData& aData ) override; + + /** Called for every spline control point */ + virtual void addControlPoint( const DL_ControlPointData& aData ) override; + + /** Called for every spline fit point */ + virtual void addFitPoint( const DL_FitPointData& aData ) override; + + /** Called for every spline knot value */ + virtual void addKnot( const DL_KnotData& aData ) override; + + // Not yet handled DXF entities: + virtual void addDimAlign( const DL_DimensionData&, + const DL_DimAlignedData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimLinear( const DL_DimensionData&, + const DL_DimLinearData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimRadial( const DL_DimensionData&, + const DL_DimRadialData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimDiametric( const DL_DimensionData&, + const DL_DimDiametricData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimAngular( const DL_DimensionData&, + const DL_DimAngularData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimAngular3P( const DL_DimensionData&, + const DL_DimAngular3PData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addDimOrdinate( const DL_DimensionData&, + const DL_DimOrdinateData& ) override { reportMsg( "DL_Dimension not managed" ); } + virtual void addLeader( const DL_LeaderData& ) override { reportMsg( "DL_Leader not managed" ); } + virtual void addLeaderVertex( const DL_LeaderVertexData& ) override { reportMsg( "DL_LeaderVertex not managed" ); } + + virtual void addHatch( const DL_HatchData& ) override { reportMsg( "DL_Hatch not managed" ); } + + virtual void addTrace( const DL_TraceData& ) override { reportMsg( "DL_Trace not managed" ); } + virtual void add3dFace( const DL_3dFaceData& ) override { reportMsg( "DL_3dFace not managed" ); } + virtual void addSolid( const DL_SolidData& ) override { reportMsg( "DL_Solid not managed" ); } + + virtual void addImage( const DL_ImageData& ) override { reportMsg( "DL_ImageDa not managed" ); } + virtual void linkImage( const DL_ImageDefData& ) override { reportMsg( "DL_ImageDef not managed" ); } + virtual void addHatchLoop( const DL_HatchLoopData& ) override { reportMsg( "DL_HatchLoop not managed" ); } + virtual void addHatchEdge( const DL_HatchEdgeData& ) override { reportMsg( "DL_HatchEdge not managed" ); } + + /** + * Converts a native unicode string into a DXF encoded string. + * + * DXF endoding includes the following special sequences: + * - %%%c for a diameter sign + * - %%%d for a degree sign + * - %%%p for a plus/minus sign + */ + static wxString toDxfString( const wxString& aStr ); + + /** + * Converts a DXF encoded string into a native Unicode string. + */ + static wxString toNativeString( const wxString& aData ); + + void writeLine(); + void writeMtext(); +}; + +#endif // DXF2BRD_ITEMS_H diff --git a/pcbnew/import_gfx/examples/basic_ellipses.dxf b/pcbnew/import_gfx/examples/basic_ellipses.dxf new file mode 100644 index 0000000000..8b0bb14d52 --- /dev/null +++ b/pcbnew/import_gfx/examples/basic_ellipses.dxf @@ -0,0 +1,2992 @@ +999 +dxfrw 0.5.13 +0 +SECTION +2 +HEADER +9 +$ACADVER +1 +AC1009 +9 +$DWGCODEPAGE +3 +ANSI_1252 +9 +$INSBASE +10 +0 +20 +0 +30 +0 +9 +$EXTMIN +10 +0 +20 +0 +30 +0 +9 +$EXTMAX +10 +286.021046008 +20 +141.572897408 +30 +0 +9 +$LIMMIN +10 +0 +20 +0 +9 +$LIMMAX +10 +420 +20 +297 +9 +$ORTHOMODE +70 +0 +9 +$LTSCALE +40 +1 +9 +$TEXTSTYLE +7 +STANDARD +9 +$CLAYER +8 +0 +9 +$DIMASZ +40 +2.5 +9 +$DIMLFAC +40 +1 +9 +$DIMSCALE +40 +1 +9 +$DIMEXO +40 +0.625 +9 +$DIMEXE +40 +1.25 +9 +$DIMTXT +40 +2.5 +9 +$DIMTSZ +40 +0 +9 +$DIMUNIT +70 +2 +9 +$DIMSTYLE +2 +STANDARD +9 +$DIMGAP +40 +0.625 +9 +$DIMTIH +70 +0 +9 +$LUNITS +70 +2 +9 +$LUPREC +70 +4 +9 +$AUNITS +70 +0 +9 +$AUPREC +70 +2 +9 +$SNAPSTYLE +70 +0 +9 +$PLIMMIN +10 +0 +20 +0 +9 +$PLIMMAX +10 +841 +20 +594 +0 +ENDSEC +0 +SECTION +2 +CLASSES +0 +ENDSEC +0 +SECTION +2 +TABLES +0 +TABLE +2 +VPORT +70 +1 +0 +VPORT +2 +*ACTIVE +70 +0 +10 +0 +20 +0 +11 +1 +21 +1 +12 +190.125 +22 +76.75 +13 +0 +23 +0 +14 +10 +24 +10 +15 +10 +25 +10 +16 +0 +26 +0 +36 +1 +17 +0 +27 +0 +37 +0 +40 +166 +41 +2.36596385542 +42 +50 +43 +0 +44 +0 +50 +0 +51 +0 +71 +0 +72 +100 +73 +1 +74 +3 +75 +0 +76 +1 +77 +0 +78 +0 +0 +ENDTAB +0 +TABLE +2 +LTYPE +70 +4 +0 +LTYPE +2 +BYBLOCK +70 +0 +3 + +72 +65 +73 +0 +40 +0 +0 +LTYPE +2 +BYLAYER +70 +0 +3 + +72 +65 +73 +0 +40 +0 +0 +LTYPE +2 +CONTINUOUS +70 +0 +3 +Solid line +72 +65 +73 +0 +40 +0 +0 +LTYPE +2 +DOT +70 +0 +3 +Dot . . . . . . . . . . . . . . . . . . . . . . +72 +65 +73 +2 +40 +6.35 +49 +0 +49 +-6.35 +0 +LTYPE +2 +DOT2 +70 +0 +3 +Dot (.5x) ..................................... +72 +65 +73 +2 +40 +3.175 +49 +0 +49 +-3.175 +0 +LTYPE +2 +DOTX2 +70 +0 +3 +Dot (2x) . . . . . . . . . . . . . +72 +65 +73 +2 +40 +12.7 +49 +0 +49 +-12.7 +0 +LTYPE +2 +DASHED +70 +0 +3 +Dot . . . . . . . . . . . . . . . . . . . . . . +72 +65 +73 +2 +40 +19.05 +49 +12.7 +49 +-6.35 +0 +LTYPE +2 +DASHED2 +70 +0 +3 +Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +72 +65 +73 +2 +40 +9.525 +49 +6.35 +49 +-3.175 +0 +LTYPE +2 +DASHEDX2 +70 +0 +3 +Dashed (2x) ____ ____ ____ ____ ____ ___ +72 +65 +73 +2 +40 +38.1 +49 +25.4 +49 +-12.7 +0 +LTYPE +2 +DASHDOT +70 +0 +3 +Dash dot __ . __ . __ . __ . __ . __ . __ . __ +72 +65 +73 +4 +40 +25.4 +49 +12.7 +49 +-6.35 +49 +0 +49 +-6.35 +0 +LTYPE +2 +DASHDOT2 +70 +0 +3 +Dash dot (.5x) _._._._._._._._._._._._._._._. +72 +65 +73 +4 +40 +12.7 +49 +6.35 +49 +-3.175 +49 +0 +49 +-3.175 +0 +LTYPE +2 +DASHDOTX2 +70 +0 +3 +Dash dot (2x) ____ . ____ . ____ . ___ +72 +65 +73 +4 +40 +50.8 +49 +25.4 +49 +-12.7 +49 +0 +49 +-12.7 +0 +LTYPE +2 +DIVIDE +70 +0 +3 +Divide ____ . . ____ . . ____ . . ____ . . ____ +72 +65 +73 +6 +40 +31.75 +49 +12.7 +49 +-6.35 +49 +0 +49 +-6.35 +49 +0 +49 +-6.35 +0 +LTYPE +2 +DIVIDE2 +70 +0 +3 +Divide (.5x) __..__..__..__..__..__..__..__.._ +72 +65 +73 +6 +40 +15.875 +49 +6.35 +49 +-3.175 +49 +0 +49 +-3.175 +49 +0 +49 +-3.175 +0 +LTYPE +2 +DIVIDEX2 +70 +0 +3 +Divide (2x) ________ . . ________ . . _ +72 +65 +73 +6 +40 +63.5 +49 +25.4 +49 +-12.7 +49 +0 +49 +-12.7 +49 +0 +49 +-12.7 +0 +LTYPE +2 +BORDER +70 +0 +3 +Border __ __ . __ __ . __ __ . __ __ . __ __ . +72 +65 +73 +6 +40 +44.45 +49 +12.7 +49 +-6.35 +49 +12.7 +49 +-6.35 +49 +0 +49 +-6.35 +0 +LTYPE +2 +BORDER2 +70 +0 +3 +Border (.5x) __.__.__.__.__.__.__.__.__.__.__. +72 +65 +73 +6 +40 +22.225 +49 +6.35 +49 +-3.175 +49 +6.35 +49 +-3.175 +49 +0 +49 +-3.175 +0 +LTYPE +2 +BORDERX2 +70 +0 +3 +Border (2x) ____ ____ . ____ ____ . ___ +72 +65 +73 +6 +40 +88.9 +49 +25.4 +49 +-12.7 +49 +25.4 +49 +-12.7 +49 +0 +49 +-12.7 +0 +LTYPE +2 +CENTER +70 +0 +3 +Center ____ _ ____ _ ____ _ ____ _ ____ _ ____ +72 +65 +73 +4 +40 +50.8 +49 +31.75 +49 +-6.35 +49 +6.35 +49 +-6.35 +0 +LTYPE +2 +CENTER2 +70 +0 +3 +Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ ___ +72 +65 +73 +4 +40 +28.575 +49 +19.05 +49 +-3.175 +49 +3.175 +49 +-3.175 +0 +LTYPE +2 +CENTERX2 +70 +0 +3 +Center (2x) ________ __ ________ __ _____ +72 +65 +73 +4 +40 +101.6 +49 +63.5 +49 +-12.7 +49 +12.7 +49 +-12.7 +0 +ENDTAB +0 +TABLE +2 +LAYER +70 +1 +0 +LAYER +2 +0 +70 +0 +62 +7 +6 +CONTINUOUS +0 +ENDTAB +0 +TABLE +2 +STYLE +70 +3 +0 +STYLE +2 +STANDARD +70 +0 +40 +0 +41 +1 +50 +0 +71 +0 +42 +1 +3 +TXT +4 + +0 +ENDTAB +0 +TABLE +2 +VIEW +70 +0 +0 +ENDTAB +0 +TABLE +2 +UCS +70 +0 +0 +ENDTAB +0 +TABLE +2 +APPID +70 +1 +0 +APPID +2 +ACAD +70 +0 +0 +APPID +2 +LIBRECAD +70 +0 +0 +ENDTAB +0 +TABLE +2 +DIMSTYLE +70 +1 +0 +DIMSTYLE +2 +STANDARD +70 +0 +3 + +4 + +5 + +6 + +7 + +40 +1 +41 +2.5 +42 +0.625 +43 +0.38 +44 +1.25 +45 +0 +46 +0 +47 +0 +48 +0 +140 +2.5 +141 +0.09 +142 +0 +143 +25.4 +144 +1 +145 +0 +146 +1 +147 +0.625 +71 +0 +72 +0 +73 +0 +74 +1 +75 +0 +76 +0 +77 +0 +78 +0 +170 +0 +171 +2 +172 +0 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +0 +ENDTAB +0 +ENDSEC +0 +SECTION +2 +BLOCKS +0 +BLOCK +8 +0 +2 +$MODEL_SPACE +70 +0 +10 +0 +20 +0 +30 +0 +3 +$MODEL_SPACE +1 + +0 +ENDBLK +8 +0 +0 +BLOCK +8 +0 +2 +$PAPER_SPACE +70 +0 +10 +0 +20 +0 +30 +0 +3 +$PAPER_SPACE +1 + +0 +ENDBLK +8 +0 +0 +ENDSEC +0 +SECTION +2 +ENTITIES +0 +POLYLINE +5 +31 +8 +0 +6 +BYLAYER +62 +256 +66 +1 +10 +0 +20 +0 +30 +0 +70 +1 +0 +VERTEX +5 +32 +8 +0 +6 +BYLAYER +62 +256 +10 +114.5 +20 +76.5 +30 +0 +0 +VERTEX +5 +33 +8 +0 +6 +BYLAYER +62 +256 +10 +115.06685415 +20 +74.8221771035 +30 +0 +0 +VERTEX +5 +34 +8 +0 +6 +BYLAYER +62 +256 +10 +115.838319688 +20 +73.2044075157 +30 +0 +0 +VERTEX +5 +35 +8 +0 +6 +BYLAYER +62 +256 +10 +116.812538085 +20 +71.6505885853 +30 +0 +0 +VERTEX +5 +36 +8 +0 +6 +BYLAYER +62 +256 +10 +117.987162364 +20 +70.1644635983 +30 +0 +0 +VERTEX +5 +37 +8 +0 +6 +BYLAYER +62 +256 +10 +119.359362753 +20 +68.7496127599 +30 +0 +0 +VERTEX +5 +38 +8 +0 +6 +BYLAYER +62 +256 +10 +120.9258335 +20 +67.4094445696 +30 +0 +0 +VERTEX +5 +39 +8 +0 +6 +BYLAYER +62 +256 +10 +122.68280084 +20 +66.14718761 +30 +0 +0 +VERTEX +5 +3A +8 +0 +6 +BYLAYER +62 +256 +10 +124.626032085 +20 +64.9658827688 +30 +0 +0 +VERTEX +5 +3B +8 +0 +6 +BYLAYER +62 +256 +10 +126.750845821 +20 +63.8683759127 +30 +0 +0 +VERTEX +5 +3C +8 +0 +6 +BYLAYER +62 +256 +10 +129.052123185 +20 +62.8573110318 +30 +0 +0 +VERTEX +5 +3D +8 +0 +6 +BYLAYER +62 +256 +10 +131.524320199 +20 +61.9351238702 +30 +0 +0 +VERTEX +5 +3E +8 +0 +6 +BYLAYER +62 +256 +10 +134.161481123 +20 +61.1040360573 +30 +0 +0 +VERTEX +5 +3F +8 +0 +6 +BYLAYER +62 +256 +10 +136.957252806 +20 +60.3660497565 +30 +0 +0 +VERTEX +5 +40 +8 +0 +6 +BYLAYER +62 +256 +10 +139.904899989 +20 +59.7229428415 +30 +0 +0 +VERTEX +5 +41 +8 +0 +6 +BYLAYER +62 +256 +10 +142.997321531 +20 +59.1762646132 +30 +0 +0 +VERTEX +5 +42 +8 +0 +6 +BYLAYER +62 +256 +10 +146.227067519 +20 +58.7273320672 +30 +0 +0 +VERTEX +5 +43 +8 +0 +6 +BYLAYER +62 +256 +10 +149.586357211 +20 +58.3772267214 +30 +0 +0 +VERTEX +5 +44 +8 +0 +6 +BYLAYER +62 +256 +10 +153.067097784 +20 +58.1267920103 +30 +0 +0 +VERTEX +5 +45 +8 +0 +6 +BYLAYER +62 +256 +10 +156.660903829 +20 +57.9766312531 +30 +0 +0 +VERTEX +5 +46 +8 +0 +6 +BYLAYER +62 +256 +10 +160.359117554 +20 +57.9271062 +30 +0 +0 +VERTEX +5 +47 +8 +0 +6 +BYLAYER +62 +256 +10 +164.152829636 +20 +57.9783361613 +30 +0 +0 +VERTEX +5 +48 +8 +0 +6 +BYLAYER +62 +256 +10 +168.032900691 +20 +58.1301977196 +30 +0 +0 +VERTEX +5 +49 +8 +0 +6 +BYLAYER +62 +256 +10 +171.989983289 +20 +58.3823250271 +30 +0 +0 +VERTEX +5 +4A +8 +0 +6 +BYLAYER +62 +256 +10 +176.01454447 +20 +58.734110687 +30 +0 +0 +VERTEX +5 +4B +8 +0 +6 +BYLAYER +62 +256 +10 +180.096888714 +20 +59.1847072167 +30 +0 +0 +VERTEX +5 +4C +8 +0 +6 +BYLAYER +62 +256 +10 +184.227181297 +20 +59.7330290899 +30 +0 +0 +VERTEX +5 +4D +8 +0 +6 +BYLAYER +62 +256 +10 +188.395471982 +20 +60.3777553511 +30 +0 +0 +VERTEX +5 +4E +8 +0 +6 +BYLAYER +62 +256 +10 +192.591718991 +20 +61.1173327983 +30 +0 +0 +VERTEX +5 +4F +8 +0 +6 +BYLAYER +62 +256 +10 +196.805813198 +20 +61.9499797245 +30 +0 +0 +VERTEX +5 +50 +8 +0 +6 +BYLAYER +62 +256 +10 +201.027602481 +20 +62.8736902105 +30 +0 +0 +VERTEX +5 +51 +8 +0 +6 +BYLAYER +62 +256 +10 +205.246916179 +20 +63.8862389568 +30 +0 +0 +VERTEX +5 +52 +8 +0 +6 +BYLAYER +62 +256 +10 +209.453589597 +20 +64.9851866447 +30 +0 +0 +VERTEX +5 +53 +8 +0 +6 +BYLAYER +62 +256 +10 +213.637488489 +20 +66.167885813 +30 +0 +0 +VERTEX +5 +54 +8 +0 +6 +BYLAYER +62 +256 +10 +217.788533477 +20 +67.4314872359 +30 +0 +0 +VERTEX +5 +55 +8 +0 +6 +BYLAYER +62 +256 +10 +221.89672433 +20 +68.7729467867 +30 +0 +0 +VERTEX +5 +56 +8 +0 +6 +BYLAYER +62 +256 +10 +225.952164056 +20 +70.189032772 +30 +0 +0 +VERTEX +5 +57 +8 +0 +6 +BYLAYER +62 +256 +10 +229.945082745 +20 +71.6763337166 +30 +0 +0 +VERTEX +5 +58 +8 +0 +6 +BYLAYER +62 +256 +10 +233.865861108 +20 +73.2312665823 +30 +0 +0 +VERTEX +5 +59 +8 +0 +6 +BYLAYER +62 +256 +10 +237.705053644 +20 +74.8500853995 +30 +0 +0 +VERTEX +5 +5A +8 +0 +6 +BYLAYER +62 +256 +10 +241.453411404 +20 +76.5288902919 +30 +0 +0 +VERTEX +5 +5B +8 +0 +6 +BYLAYER +62 +256 +10 +245.101904265 +20 +78.2636368716 +30 +0 +0 +VERTEX +5 +5C +8 +0 +6 +BYLAYER +62 +256 +10 +248.641742688 +20 +80.0501459821 +30 +0 +0 +VERTEX +5 +5D +8 +0 +6 +BYLAYER +62 +256 +10 +252.064398893 +20 +81.8841137663 +30 +0 +0 +VERTEX +5 +5E +8 +0 +6 +BYLAYER +62 +256 +10 +255.3616274 +20 +83.7611220354 +30 +0 +0 +VERTEX +5 +5F +8 +0 +6 +BYLAYER +62 +256 +10 +258.525484898 +20 +85.6766489119 +30 +0 +0 +VERTEX +5 +60 +8 +0 +6 +BYLAYER +62 +256 +10 +261.548349376 +20 +87.6260797239 +30 +0 +0 +VERTEX +5 +61 +8 +0 +6 +BYLAYER +62 +256 +10 +264.42293849 +20 +89.6047181218 +30 +0 +0 +VERTEX +5 +62 +8 +0 +6 +BYLAYER +62 +256 +10 +267.142327102 +20 +91.6077973924 +30 +0 +0 +VERTEX +5 +63 +8 +0 +6 +BYLAYER +62 +256 +10 +269.699963967 +20 +93.6304919422 +30 +0 +0 +VERTEX +5 +64 +8 +0 +6 +BYLAYER +62 +256 +10 +272.089687514 +20 +95.6679289229 +30 +0 +0 +VERTEX +5 +65 +8 +0 +6 +BYLAYER +62 +256 +10 +274.305740689 +20 +97.7151999704 +30 +0 +0 +VERTEX +5 +66 +8 +0 +6 +BYLAYER +62 +256 +10 +276.342784827 +20 +99.7673730293 +30 +0 +0 +VERTEX +5 +67 +8 +0 +6 +BYLAYER +62 +256 +10 +278.195912509 +20 +101.819504235 +30 +0 +0 +VERTEX +5 +68 +8 +0 +6 +BYLAYER +62 +256 +10 +279.860659389 +20 +103.866649824 +30 +0 +0 +VERTEX +5 +69 +8 +0 +6 +BYLAYER +62 +256 +10 +281.333014945 +20 +105.903878043 +30 +0 +0 +VERTEX +5 +6A +8 +0 +6 +BYLAYER +62 +256 +10 +282.609432145 +20 +107.926281031 +30 +0 +0 +VERTEX +5 +6B +8 +0 +6 +BYLAYER +62 +256 +10 +283.686835986 +20 +109.928986641 +30 +0 +0 +VERTEX +5 +6C +8 +0 +6 +BYLAYER +62 +256 +10 +284.56263091 +20 +111.907170181 +30 +0 +0 +VERTEX +5 +6D +8 +0 +6 +BYLAYER +62 +256 +10 +285.234707049 +20 +113.856066033 +30 +0 +0 +VERTEX +5 +6E +8 +0 +6 +BYLAYER +62 +256 +10 +285.701445313 +20 +115.770979137 +30 +0 +0 +VERTEX +5 +6F +8 +0 +6 +BYLAYER +62 +256 +10 +285.961721289 +20 +117.647296299 +30 +0 +0 +VERTEX +5 +70 +8 +0 +6 +BYLAYER +62 +256 +10 +286.014907949 +20 +119.480497306 +30 +0 +0 +VERTEX +5 +71 +8 +0 +6 +BYLAYER +62 +256 +10 +285.860877161 +20 +121.266165817 +30 +0 +0 +VERTEX +5 +72 +8 +0 +6 +BYLAYER +62 +256 +10 +285.5 +20 +123 +30 +0 +0 +VERTEX +5 +73 +8 +0 +6 +BYLAYER +62 +256 +10 +284.93314585 +20 +124.677822897 +30 +0 +0 +VERTEX +5 +74 +8 +0 +6 +BYLAYER +62 +256 +10 +284.161680312 +20 +126.295592484 +30 +0 +0 +VERTEX +5 +75 +8 +0 +6 +BYLAYER +62 +256 +10 +283.187461915 +20 +127.849411415 +30 +0 +0 +VERTEX +5 +76 +8 +0 +6 +BYLAYER +62 +256 +10 +282.012837636 +20 +129.335536402 +30 +0 +0 +VERTEX +5 +77 +8 +0 +6 +BYLAYER +62 +256 +10 +280.640637247 +20 +130.75038724 +30 +0 +0 +VERTEX +5 +78 +8 +0 +6 +BYLAYER +62 +256 +10 +279.0741665 +20 +132.09055543 +30 +0 +0 +VERTEX +5 +79 +8 +0 +6 +BYLAYER +62 +256 +10 +277.31719916 +20 +133.35281239 +30 +0 +0 +VERTEX +5 +7A +8 +0 +6 +BYLAYER +62 +256 +10 +275.373967915 +20 +134.534117231 +30 +0 +0 +VERTEX +5 +7B +8 +0 +6 +BYLAYER +62 +256 +10 +273.249154179 +20 +135.631624087 +30 +0 +0 +VERTEX +5 +7C +8 +0 +6 +BYLAYER +62 +256 +10 +270.947876815 +20 +136.642688968 +30 +0 +0 +VERTEX +5 +7D +8 +0 +6 +BYLAYER +62 +256 +10 +268.475679801 +20 +137.56487613 +30 +0 +0 +VERTEX +5 +7E +8 +0 +6 +BYLAYER +62 +256 +10 +265.838518877 +20 +138.395963943 +30 +0 +0 +VERTEX +5 +7F +8 +0 +6 +BYLAYER +62 +256 +10 +263.042747194 +20 +139.133950243 +30 +0 +0 +VERTEX +5 +80 +8 +0 +6 +BYLAYER +62 +256 +10 +260.095100011 +20 +139.777057158 +30 +0 +0 +VERTEX +5 +81 +8 +0 +6 +BYLAYER +62 +256 +10 +257.002678469 +20 +140.323735387 +30 +0 +0 +VERTEX +5 +82 +8 +0 +6 +BYLAYER +62 +256 +10 +253.772932481 +20 +140.772667933 +30 +0 +0 +VERTEX +5 +83 +8 +0 +6 +BYLAYER +62 +256 +10 +250.413642789 +20 +141.122773279 +30 +0 +0 +VERTEX +5 +84 +8 +0 +6 +BYLAYER +62 +256 +10 +246.932902216 +20 +141.37320799 +30 +0 +0 +VERTEX +5 +85 +8 +0 +6 +BYLAYER +62 +256 +10 +243.339096171 +20 +141.523368747 +30 +0 +0 +VERTEX +5 +86 +8 +0 +6 +BYLAYER +62 +256 +10 +239.640882446 +20 +141.5728938 +30 +0 +0 +VERTEX +5 +87 +8 +0 +6 +BYLAYER +62 +256 +10 +235.847170364 +20 +141.521663839 +30 +0 +0 +VERTEX +5 +88 +8 +0 +6 +BYLAYER +62 +256 +10 +231.967099309 +20 +141.36980228 +30 +0 +0 +VERTEX +5 +89 +8 +0 +6 +BYLAYER +62 +256 +10 +228.010016711 +20 +141.117674973 +30 +0 +0 +VERTEX +5 +8A +8 +0 +6 +BYLAYER +62 +256 +10 +223.98545553 +20 +140.765889313 +30 +0 +0 +VERTEX +5 +8B +8 +0 +6 +BYLAYER +62 +256 +10 +219.903111286 +20 +140.315292783 +30 +0 +0 +VERTEX +5 +8C +8 +0 +6 +BYLAYER +62 +256 +10 +215.772818703 +20 +139.76697091 +30 +0 +0 +VERTEX +5 +8D +8 +0 +6 +BYLAYER +62 +256 +10 +211.604528018 +20 +139.122244649 +30 +0 +0 +VERTEX +5 +8E +8 +0 +6 +BYLAYER +62 +256 +10 +207.408281009 +20 +138.382667202 +30 +0 +0 +VERTEX +5 +8F +8 +0 +6 +BYLAYER +62 +256 +10 +203.194186802 +20 +137.550020275 +30 +0 +0 +VERTEX +5 +90 +8 +0 +6 +BYLAYER +62 +256 +10 +198.972397519 +20 +136.626309789 +30 +0 +0 +VERTEX +5 +91 +8 +0 +6 +BYLAYER +62 +256 +10 +194.753083821 +20 +135.613761043 +30 +0 +0 +VERTEX +5 +92 +8 +0 +6 +BYLAYER +62 +256 +10 +190.546410403 +20 +134.514813355 +30 +0 +0 +VERTEX +5 +93 +8 +0 +6 +BYLAYER +62 +256 +10 +186.362511511 +20 +133.332114187 +30 +0 +0 +VERTEX +5 +94 +8 +0 +6 +BYLAYER +62 +256 +10 +182.211466523 +20 +132.068512764 +30 +0 +0 +VERTEX +5 +95 +8 +0 +6 +BYLAYER +62 +256 +10 +178.10327567 +20 +130.727053213 +30 +0 +0 +VERTEX +5 +96 +8 +0 +6 +BYLAYER +62 +256 +10 +174.047835944 +20 +129.310967228 +30 +0 +0 +VERTEX +5 +97 +8 +0 +6 +BYLAYER +62 +256 +10 +170.054917255 +20 +127.823666283 +30 +0 +0 +VERTEX +5 +98 +8 +0 +6 +BYLAYER +62 +256 +10 +166.134138892 +20 +126.268733418 +30 +0 +0 +VERTEX +5 +99 +8 +0 +6 +BYLAYER +62 +256 +10 +162.294946356 +20 +124.649914601 +30 +0 +0 +VERTEX +5 +9A +8 +0 +6 +BYLAYER +62 +256 +10 +158.546588596 +20 +122.971109708 +30 +0 +0 +VERTEX +5 +9B +8 +0 +6 +BYLAYER +62 +256 +10 +154.898095735 +20 +121.236363128 +30 +0 +0 +VERTEX +5 +9C +8 +0 +6 +BYLAYER +62 +256 +10 +151.358257312 +20 +119.449854018 +30 +0 +0 +VERTEX +5 +9D +8 +0 +6 +BYLAYER +62 +256 +10 +147.935601107 +20 +117.615886234 +30 +0 +0 +VERTEX +5 +9E +8 +0 +6 +BYLAYER +62 +256 +10 +144.6383726 +20 +115.738877965 +30 +0 +0 +VERTEX +5 +9F +8 +0 +6 +BYLAYER +62 +256 +10 +141.474515102 +20 +113.823351088 +30 +0 +0 +VERTEX +5 +A0 +8 +0 +6 +BYLAYER +62 +256 +10 +138.451650624 +20 +111.873920276 +30 +0 +0 +VERTEX +5 +A1 +8 +0 +6 +BYLAYER +62 +256 +10 +135.57706151 +20 +109.895281878 +30 +0 +0 +VERTEX +5 +A2 +8 +0 +6 +BYLAYER +62 +256 +10 +132.857672898 +20 +107.892202608 +30 +0 +0 +VERTEX +5 +A3 +8 +0 +6 +BYLAYER +62 +256 +10 +130.300036033 +20 +105.869508058 +30 +0 +0 +VERTEX +5 +A4 +8 +0 +6 +BYLAYER +62 +256 +10 +127.910312486 +20 +103.832071077 +30 +0 +0 +VERTEX +5 +A5 +8 +0 +6 +BYLAYER +62 +256 +10 +125.694259311 +20 +101.78480003 +30 +0 +0 +VERTEX +5 +A6 +8 +0 +6 +BYLAYER +62 +256 +10 +123.657215173 +20 +99.7326269707 +30 +0 +0 +VERTEX +5 +A7 +8 +0 +6 +BYLAYER +62 +256 +10 +121.804087491 +20 +97.6804957648 +30 +0 +0 +VERTEX +5 +A8 +8 +0 +6 +BYLAYER +62 +256 +10 +120.139340611 +20 +95.633350176 +30 +0 +0 +VERTEX +5 +A9 +8 +0 +6 +BYLAYER +62 +256 +10 +118.666985055 +20 +93.5961219571 +30 +0 +0 +VERTEX +5 +AA +8 +0 +6 +BYLAYER +62 +256 +10 +117.390567855 +20 +91.5737189695 +30 +0 +0 +VERTEX +5 +AB +8 +0 +6 +BYLAYER +62 +256 +10 +116.313164014 +20 +89.571013359 +30 +0 +0 +VERTEX +5 +AC +8 +0 +6 +BYLAYER +62 +256 +10 +115.43736909 +20 +87.5928298189 +30 +0 +0 +VERTEX +5 +AD +8 +0 +6 +BYLAYER +62 +256 +10 +114.765292951 +20 +85.6439339666 +30 +0 +0 +VERTEX +5 +AE +8 +0 +6 +BYLAYER +62 +256 +10 +114.298554687 +20 +83.7290208629 +30 +0 +0 +VERTEX +5 +AF +8 +0 +6 +BYLAYER +62 +256 +10 +114.038278711 +20 +81.8527037013 +30 +0 +0 +VERTEX +5 +B0 +8 +0 +6 +BYLAYER +62 +256 +10 +113.985092051 +20 +80.019502694 +30 +0 +0 +VERTEX +5 +B1 +8 +0 +6 +BYLAYER +62 +256 +10 +114.139122839 +20 +78.233834183 +30 +0 +0 +SEQEND +5 +B2 +8 +0 +6 +BYLAYER +62 +256 +0 +ENDSEC +0 +EOF diff --git a/pcbnew/import_gfx/examples/test_outlines.dxf b/pcbnew/import_gfx/examples/test_outlines.dxf new file mode 100644 index 0000000000..c5448b172b --- /dev/null +++ b/pcbnew/import_gfx/examples/test_outlines.dxf @@ -0,0 +1,7264 @@ +999 +dxfrw 0.5.10 +0 +SECTION +2 +HEADER +9 +$ACADVER +1 +AC1021 +9 +$HANDSEED +5 +20000 +9 +$DWGCODEPAGE +3 +ANSI_1252 +9 +$INSBASE +10 +0 +20 +0 +30 +0 +9 +$EXTMIN +10 +8.01510349296 +20 +22.3703927791 +30 +0 +9 +$EXTMAX +10 +160.415103493 +20 +173.677807569 +30 +0 +9 +$LIMMIN +10 +0 +20 +0 +9 +$LIMMAX +10 +210 +20 +297 +9 +$ORTHOMODE +70 +0 +9 +$LTSCALE +40 +1 +9 +$TEXTSTYLE +7 +Standard +9 +$DIMASZ +40 +1.8 +9 +$DIMSCALE +40 +1 +9 +$DIMEXO +40 +0.0625 +9 +$DIMEXE +40 +0.18 +9 +$DIMTXT +40 +0.25 +9 +$DIMTSZ +40 +0 +9 +$DIMAUNIT +70 +0 +9 +$DIMADEC +70 +0 +9 +$DIMLUNIT +70 +2 +9 +$DIMSTYLE +2 +Standard +9 +$DIMGAP +40 +0.09 +9 +$DIMTIH +70 +1 +9 +$LUNITS +70 +2 +9 +$LUPREC +70 +4 +9 +$AUNITS +70 +0 +9 +$AUPREC +70 +0 +9 +$SPLINESEGS +70 +8 +9 +$GRIDMODE +70 +0 +9 +$SNAPSTYLE +70 +0 +9 +$PINSBASE +10 +0 +20 +0 +30 +0 +9 +$PLIMMIN +10 +0 +20 +0 +9 +$PLIMMAX +10 +210 +20 +297 +9 +$INSUNITS +70 +4 +9 +$PSVPSCALE +40 +0 +0 +ENDSEC +0 +SECTION +2 +CLASSES +0 +ENDSEC +0 +SECTION +2 +TABLES +0 +TABLE +2 +VPORT +5 +8 +330 +0 +100 +AcDbSymbolTable +70 +1 +0 +VPORT +5 +31 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord +2 +*ACTIVE +70 +0 +10 +0 +20 +0 +11 +1 +21 +1 +12 +103.477837818 +22 +82.10741479 +13 +0 +23 +0 +14 +10 +24 +10 +15 +10 +25 +10 +16 +0 +26 +0 +36 +1 +17 +0 +27 +0 +37 +0 +40 +368.920987002 +41 +0.976829268293 +42 +50 +43 +0 +44 +0 +50 +0 +51 +0 +71 +0 +72 +100 +73 +1 +74 +3 +75 +0 +76 +0 +77 +0 +78 +0 +281 +0 +65 +1 +110 +0 +120 +0 +130 +0 +111 +1 +121 +0 +131 +0 +112 +0 +122 +1 +132 +0 +79 +0 +146 +0 +348 +10020 +60 +7 +61 +5 +292 +1 +282 +1 +141 +0 +142 +0 +63 +250 +421 +3358443 +0 +ENDTAB +0 +TABLE +2 +LTYPE +5 +5 +330 +0 +100 +AcDbSymbolTable +70 +4 +0 +LTYPE +5 +14 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +ByBlock +70 +0 +3 + +72 +65 +73 +0 +40 +0 +0 +LTYPE +5 +15 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +ByLayer +70 +0 +3 + +72 +65 +73 +0 +40 +0 +0 +LTYPE +5 +16 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +Continuous +70 +0 +3 +Solid line +72 +65 +73 +0 +40 +0 +0 +LTYPE +5 +32 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DOT +70 +0 +3 +Dot . . . . . . . . . . . . . . . . . . . . . . +72 +65 +73 +2 +40 +6.35 +49 +0 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +33 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DOT2 +70 +0 +3 +Dot (.5x) ..................................... +72 +65 +73 +2 +40 +3.175 +49 +0 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +34 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DOTX2 +70 +0 +3 +Dot (2x) . . . . . . . . . . . . . +72 +65 +73 +2 +40 +12.7 +49 +0 +74 +0 +49 +-12.7 +74 +0 +0 +LTYPE +5 +35 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHED +70 +0 +3 +Dot . . . . . . . . . . . . . . . . . . . . . . +72 +65 +73 +2 +40 +19.05 +49 +12.7 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +36 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHED2 +70 +0 +3 +Dashed (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ +72 +65 +73 +2 +40 +9.525 +49 +6.35 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +37 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHEDX2 +70 +0 +3 +Dashed (2x) ____ ____ ____ ____ ____ ___ +72 +65 +73 +2 +40 +38.1 +49 +25.4 +74 +0 +49 +-12.7 +74 +0 +0 +LTYPE +5 +38 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHDOT +70 +0 +3 +Dash dot __ . __ . __ . __ . __ . __ . __ . __ +72 +65 +73 +4 +40 +25.4 +49 +12.7 +74 +0 +49 +-6.35 +74 +0 +49 +0 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +39 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHDOT2 +70 +0 +3 +Dash dot (.5x) _._._._._._._._._._._._._._._. +72 +65 +73 +4 +40 +12.7 +49 +6.35 +74 +0 +49 +-3.175 +74 +0 +49 +0 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +3A +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DASHDOTX2 +70 +0 +3 +Dash dot (2x) ____ . ____ . ____ . ___ +72 +65 +73 +4 +40 +50.8 +49 +25.4 +74 +0 +49 +-12.7 +74 +0 +49 +0 +74 +0 +49 +-12.7 +74 +0 +0 +LTYPE +5 +3B +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DIVIDE +70 +0 +3 +Divide ____ . . ____ . . ____ . . ____ . . ____ +72 +65 +73 +6 +40 +31.75 +49 +12.7 +74 +0 +49 +-6.35 +74 +0 +49 +0 +74 +0 +49 +-6.35 +74 +0 +49 +0 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +3C +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DIVIDE2 +70 +0 +3 +Divide (.5x) __..__..__..__..__..__..__..__.._ +72 +65 +73 +6 +40 +15.875 +49 +6.35 +74 +0 +49 +-3.175 +74 +0 +49 +0 +74 +0 +49 +-3.175 +74 +0 +49 +0 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +3D +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +DIVIDEX2 +70 +0 +3 +Divide (2x) ________ . . ________ . . _ +72 +65 +73 +6 +40 +63.5 +49 +25.4 +74 +0 +49 +-12.7 +74 +0 +49 +0 +74 +0 +49 +-12.7 +74 +0 +49 +0 +74 +0 +49 +-12.7 +74 +0 +0 +LTYPE +5 +3E +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +BORDER +70 +0 +3 +Border __ __ . __ __ . __ __ . __ __ . __ __ . +72 +65 +73 +6 +40 +44.45 +49 +12.7 +74 +0 +49 +-6.35 +74 +0 +49 +12.7 +74 +0 +49 +-6.35 +74 +0 +49 +0 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +3F +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +BORDER2 +70 +0 +3 +Border (.5x) __.__.__.__.__.__.__.__.__.__.__. +72 +65 +73 +6 +40 +22.225 +49 +6.35 +74 +0 +49 +-3.175 +74 +0 +49 +6.35 +74 +0 +49 +-3.175 +74 +0 +49 +0 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +40 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +BORDERX2 +70 +0 +3 +Border (2x) ____ ____ . ____ ____ . ___ +72 +65 +73 +6 +40 +88.9 +49 +25.4 +74 +0 +49 +-12.7 +74 +0 +49 +25.4 +74 +0 +49 +-12.7 +74 +0 +49 +0 +74 +0 +49 +-12.7 +74 +0 +0 +LTYPE +5 +41 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +CENTER +70 +0 +3 +Center ____ _ ____ _ ____ _ ____ _ ____ _ ____ +72 +65 +73 +4 +40 +50.8 +49 +31.75 +74 +0 +49 +-6.35 +74 +0 +49 +6.35 +74 +0 +49 +-6.35 +74 +0 +0 +LTYPE +5 +42 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +CENTER2 +70 +0 +3 +Center (.5x) ___ _ ___ _ ___ _ ___ _ ___ _ ___ +72 +65 +73 +4 +40 +28.575 +49 +19.05 +74 +0 +49 +-3.175 +74 +0 +49 +3.175 +74 +0 +49 +-3.175 +74 +0 +0 +LTYPE +5 +43 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord +2 +CENTERX2 +70 +0 +3 +Center (2x) ________ __ ________ __ _____ +72 +65 +73 +4 +40 +101.6 +49 +63.5 +74 +0 +49 +-12.7 +74 +0 +49 +12.7 +74 +0 +49 +-12.7 +74 +0 +0 +ENDTAB +0 +TABLE +2 +LAYER +5 +2 +330 +0 +100 +AcDbSymbolTable +70 +1 +0 +LAYER +5 +10 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord +2 +0 +70 +0 +62 +7 +6 +CONTINUOUS +370 +-3 +390 +F +0 +LAYER +5 +44 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord +2 +Visible Edges(PEC) +70 +0 +62 +7 +6 +CONTINUOUS +370 +50 +390 +F +0 +ENDTAB +0 +TABLE +2 +STYLE +5 +3 +330 +0 +100 +AcDbSymbolTable +70 +3 +0 +STYLE +5 +45 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord +2 +Standard +70 +0 +40 +0 +41 +1 +50 +0 +71 +0 +42 +1 +3 +txt +4 + +0 +ENDTAB +0 +TABLE +2 +VIEW +5 +6 +330 +0 +100 +AcDbSymbolTable +70 +0 +0 +ENDTAB +0 +TABLE +2 +UCS +5 +7 +330 +0 +100 +AcDbSymbolTable +70 +0 +0 +ENDTAB +0 +TABLE +2 +APPID +5 +9 +330 +0 +100 +AcDbSymbolTable +70 +1 +0 +APPID +5 +12 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord +2 +ACAD +70 +0 +0 +ENDTAB +0 +TABLE +2 +DIMSTYLE +5 +A +330 +0 +100 +AcDbSymbolTable +70 +1 +100 +AcDbDimStyleTable +71 +1 +0 +DIMSTYLE +105 +46 +330 +A +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord +2 +Standard +70 +0 +40 +1 +41 +1.8 +42 +0.0625 +43 +0.38 +44 +0.18 +45 +0 +46 +0 +47 +0 +48 +0 +140 +0.25 +141 +0.09 +142 +0 +143 +25.4 +144 +1 +145 +0 +146 +1 +147 +0.09 +148 +0 +71 +0 +72 +0 +73 +0 +74 +1 +75 +0 +76 +0 +77 +0 +78 +0 +79 +0 +170 +0 +171 +2 +172 +0 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +0 +271 +4 +272 +4 +273 +2 +274 +2 +275 +0 +276 +0 +277 +2 +278 +46 +279 +0 +280 +0 +281 +0 +282 +0 +283 +1 +284 +0 +285 +0 +286 +0 +288 +0 +289 +3 +340 +Standard +341 + +371 +-2 +372 +-2 +0 +ENDTAB +0 +TABLE +2 +BLOCK_RECORD +5 +1 +330 +0 +100 +AcDbSymbolTable +70 +2 +0 +BLOCK_RECORD +5 +1F +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord +2 +*Model_Space +70 +0 +280 +1 +281 +0 +0 +BLOCK_RECORD +5 +1E +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord +2 +*Paper_Space +70 +0 +280 +1 +281 +0 +0 +ENDTAB +0 +ENDSEC +0 +SECTION +2 +BLOCKS +0 +BLOCK +5 +20 +330 +1F +100 +AcDbEntity +8 +0 +100 +AcDbBlockBegin +2 +*Model_Space +70 +0 +10 +0 +20 +0 +30 +0 +3 +*Model_Space +1 + +0 +ENDBLK +5 +21 +330 +1F +100 +AcDbEntity +8 +0 +100 +AcDbBlockEnd +0 +BLOCK +5 +1C +330 +1B +100 +AcDbEntity +8 +0 +100 +AcDbBlockBegin +2 +*Paper_Space +70 +0 +10 +0 +20 +0 +30 +0 +3 +*Paper_Space +1 + +0 +ENDBLK +5 +1D +330 +1F +100 +AcDbEntity +8 +0 +100 +AcDbBlockEnd +0 +ENDSEC +0 +SECTION +2 +ENTITIES +0 +LINE +5 +47 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +54.0015095768 +20 +145.07427149 +11 +57.9407405889 +21 +144.379678779 +0 +LINE +5 +48 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +57.9407405889 +20 +144.379678779 +11 +55.9264217279 +21 +132.955908844 +0 +LINE +5 +49 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +55.9264217279 +20 +132.955908844 +11 +51.9871907159 +21 +133.650501555 +0 +CIRCLE +5 +4A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +76.6151034931 +20 +128.870392779 +40 +2 +0 +CIRCLE +5 +4B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +62.3755750957 +20 +142.867767753 +40 +1.6 +0 +CIRCLE +5 +4C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +60.6112785469 +20 +132.861944812 +40 +1.6 +0 +CIRCLE +5 +4D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +76.9279747741 +20 +141.354399538 +40 +1.1 +0 +CIRCLE +5 +4E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +28.165103493 +20 +25.4703927791 +40 +0.7 +0 +CIRCLE +5 +4F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +44.265103493 +20 +25.4703927791 +40 +0.7 +0 +CIRCLE +5 +50 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +36.965103493 +20 +25.4703927791 +40 +0.7 +0 +ARC +5 +51 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +78.5610437151 +20 +131.847953136 +40 +0.4 +100 +AcDbArc +50 +85.34 +51 +265.34 +0 +LINE +5 +52 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +78.5935407971 +20 +132.246630875 +11 +79.4905657101 +21 +132.173512441 +0 +ARC +5 +53 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +79.4580686281 +20 +131.774834702 +40 +0.4 +100 +AcDbArc +50 +265.34 +51 +85.34 +0 +LINE +5 +54 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +79.4255715461 +20 +131.376156963 +11 +78.5285466331 +21 +131.449275397 +0 +LINE +5 +55 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +79.1866125401 +20 +139.522499614 +11 +80.0836374531 +21 +139.44938118 +0 +ARC +5 +56 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +80.0511403711 +20 +139.050703441 +40 +0.4 +100 +AcDbArc +50 +265.34 +51 +85.34 +0 +LINE +5 +57 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +80.0186432901 +20 +138.652025702 +11 +79.1216183771 +21 +138.725144136 +0 +ARC +5 +58 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +79.1541154581 +20 +139.123821875 +40 +0.4 +100 +AcDbArc +50 +85.34 +51 +265.34 +0 +ARC +5 +59 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +73.8716354151 +20 +139.554408209 +40 +0.4 +100 +AcDbArc +50 +85.34 +51 +265.34 +0 +LINE +5 +5A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +73.9041324969 +20 +139.953085949 +11 +74.8011574099 +21 +139.879967514 +0 +ARC +5 +5B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +74.7686603281 +20 +139.481289775 +40 +0.4 +100 +AcDbArc +50 +265.34 +51 +85.34 +0 +LINE +5 +5C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +74.7361632463 +20 +139.082612036 +11 +73.8391383333 +21 +139.15573047 +0 +ARC +5 +5D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +73.2785636718 +20 +132.27853947 +40 +0.4 +100 +AcDbArc +50 +85.34 +51 +265.34 +0 +LINE +5 +5E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +73.3110607536 +20 +132.67721721 +11 +74.2080856666 +21 +132.604098775 +0 +ARC +5 +5F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +74.1755885848 +20 +132.205421036 +40 +0.4 +100 +AcDbArc +50 +265.34 +51 +85.34 +0 +LINE +5 +60 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +74.143091503 +20 +131.806743297 +11 +73.2460665899 +21 +131.879861731 +0 +LINE +5 +61 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +56.8197279711 +20 +132.003859741 +11 +57.9030164994 +21 +131.812846746 +0 +ARC +5 +62 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +57.8509220461 +20 +131.51740442 +40 +0.3 +100 +AcDbArc +50 +260 +51 +80 +0 +LINE +5 +63 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +57.7988275928 +20 +131.221962094 +11 +56.7155390645 +21 +131.412975089 +0 +ARC +5 +64 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +56.7676335178 +20 +131.708417415 +40 +0.3 +100 +AcDbArc +50 +80 +51 +260 +0 +LINE +5 +65 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +59.2022229169 +20 +145.515660013 +11 +60.2855114452 +21 +145.324647017 +0 +ARC +5 +66 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +60.2334169919 +20 +145.029204691 +40 +0.3 +100 +AcDbArc +50 +260 +51 +80 +0 +LINE +5 +67 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +60.1813225386 +20 +144.733762365 +11 +59.0980340103 +21 +144.924775361 +0 +ARC +5 +68 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +59.1501284636 +20 +145.220217687 +40 +0.3 +100 +AcDbArc +50 +80 +51 +260 +0 +CIRCLE +5 +69 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +17.415103493 +20 +33.7103927791 +40 +0.5 +0 +CIRCLE +5 +6A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +17.415103493 +20 +36.2503927791 +40 +0.5 +0 +CIRCLE +5 +6B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +17.415103493 +20 +38.7903927791 +40 +0.5 +0 +CIRCLE +5 +6C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +17.415103493 +20 +41.3303927791 +40 +0.5 +0 +CIRCLE +5 +6D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +76.1591754285 +20 +137.461644322 +40 +0.45 +0 +CIRCLE +5 +6E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +75.8748259626 +20 +133.973214105 +40 +0.45 +0 +CIRCLE +5 +6F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +68.0851733401 +20 +138.680315907 +40 +0.45 +0 +CIRCLE +5 +70 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +67.6440992029 +20 +136.178860171 +40 +0.45 +0 +CIRCLE +5 +71 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +70.3686653354 +20 +137.003365518 +40 +0.45 +0 +CIRCLE +5 +72 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +69.9275911982 +20 +134.501909782 +40 +0.45 +0 +LINE +5 +73 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +54.388745013 +20 +147.270392779 +11 +54.0015095768 +21 +145.07427149 +0 +LINE +5 +74 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +51.9871907159 +20 +133.650501555 +11 +50.7387765123 +21 +126.570392779 +0 +LINE +5 +75 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +26.115103493 +20 +28.5703927791 +11 +26.115103493 +21 +22.3703927791 +0 +LINE +5 +76 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +26.115103493 +20 +22.3703927791 +11 +46.315103493 +21 +22.3703927791 +0 +LINE +5 +77 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +46.315103493 +20 +22.3703927791 +11 +46.315103493 +21 +28.5703927791 +0 +LINE +5 +78 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +80.1469013801 +20 +126.070392779 +11 +81.4988185711 +21 +142.655860116 +0 +LINE +5 +79 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +54.388745013 +20 +147.270392779 +11 +81.4988185711 +21 +142.655860116 +0 +CIRCLE +5 +7A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +38.615103493 +20 +36.0703927791 +40 +0.75 +0 +CIRCLE +5 +7B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +31.115103493 +20 +33.8203927791 +40 +0.75 +0 +CIRCLE +5 +7C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +141.915103493 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +7D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +149.415103493 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +7E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +139.665103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +7F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +147.165103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +80 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +135.165103493 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +81 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +142.665103493 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +82 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +137.415103493 +20 +33.8203927791 +40 +0.75 +0 +CIRCLE +5 +83 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +144.915103493 +20 +36.0703927791 +40 +0.75 +0 +CIRCLE +5 +84 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +114.765103493 +20 +36.0703927791 +40 +0.75 +0 +CIRCLE +5 +85 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +107.265103493 +20 +33.8203927791 +40 +0.75 +0 +CIRCLE +5 +86 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +91.3651034931 +20 +36.0703927791 +40 +0.75 +0 +CIRCLE +5 +87 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +83.8651034931 +20 +33.8203927791 +40 +0.75 +0 +CIRCLE +5 +88 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +67.965103493 +20 +36.0703927791 +40 +0.75 +0 +CIRCLE +5 +89 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +60.465103493 +20 +33.8203927791 +40 +0.75 +0 +CIRCLE +5 +8A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +104.715103493 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +8B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +112.215103493 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +8C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +85.5151034931 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +8D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +93.0151034931 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +8E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +66.315103493 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +8F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +73.815103493 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +90 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +47.115103493 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +91 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +54.615103493 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +92 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +35.415103493 +20 +55.0703927791 +40 +0.75 +0 +CIRCLE +5 +93 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +27.915103493 +20 +52.8203927791 +40 +0.75 +0 +CIRCLE +5 +94 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +121.325103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +95 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +113.825103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +96 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +102.235103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +97 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +94.7351034931 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +98 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +83.1451034931 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +99 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +75.645103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +9A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +64.055103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +9B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +56.555103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +9C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +44.965103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +9D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +37.465103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +9E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +18.375103493 +20 +71.8203927791 +40 +0.75 +0 +CIRCLE +5 +9F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +25.875103493 +20 +74.0703927791 +40 +0.75 +0 +CIRCLE +5 +A0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +146.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +A1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +153.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +A2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +127.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +A3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +134.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +A4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +108.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +A5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +115.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +A6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +89.4151034931 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +A7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +96.9151034931 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +A8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +70.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +A9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +77.9151034931 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +AA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +58.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +AB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +32.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +AC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +39.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +AD +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +13.415103493 +20 +90.8203927791 +40 +0.75 +0 +CIRCLE +5 +AE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +20.915103493 +20 +93.0703927791 +40 +0.75 +0 +CIRCLE +5 +AF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +118.415103493 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +B0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +125.915103493 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +B1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +99.4151034931 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +B2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +106.915103493 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +B3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +80.4151034931 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +B4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +87.9151034931 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +B5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +61.415103493 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +B6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +68.915103493 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +B7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +42.415103493 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +B8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +49.915103493 +20 +112.070392779 +40 +0.75 +0 +CIRCLE +5 +B9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +23.415103493 +20 +109.820392779 +40 +0.75 +0 +CIRCLE +5 +BA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +30.915103493 +20 +112.070392779 +40 +0.75 +0 +LINE +5 +BB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +19.215103493 +20 +114.070392779 +11 +19.215103493 +21 +110.070392779 +0 +LINE +5 +BC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +19.215103493 +20 +110.070392779 +11 +17.915103493 +21 +110.070392779 +0 +LINE +5 +BD +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +114.070392779 +11 +19.215103493 +21 +114.070392779 +0 +LINE +5 +BE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +19.215103493 +20 +122.070392779 +11 +19.215103493 +21 +118.070392779 +0 +LINE +5 +BF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +19.215103493 +20 +118.070392779 +11 +17.915103493 +21 +118.070392779 +0 +LINE +5 +C0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +122.070392779 +11 +19.215103493 +21 +122.070392779 +0 +LINE +5 +C1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +23.815103493 +20 +64.0703927791 +11 +23.815103493 +21 +60.0703927791 +0 +LINE +5 +C2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +23.815103493 +20 +60.0703927791 +11 +22.515103493 +21 +60.0703927791 +0 +LINE +5 +C3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +64.0703927791 +11 +23.815103493 +21 +64.0703927791 +0 +LINE +5 +C4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +23.815103493 +20 +57.0703927791 +11 +23.815103493 +21 +53.0703927791 +0 +LINE +5 +C5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +23.815103493 +20 +53.0703927791 +11 +22.515103493 +21 +53.0703927791 +0 +LINE +5 +C6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +57.0703927791 +11 +23.815103493 +21 +57.0703927791 +0 +LINE +5 +C7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +14.315103493 +20 +76.0703927791 +11 +14.315103493 +21 +72.0703927791 +0 +LINE +5 +C8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +14.315103493 +20 +72.0703927791 +11 +13.015103493 +21 +72.0703927791 +0 +LINE +5 +C9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +76.0703927791 +11 +14.315103493 +21 +76.0703927791 +0 +LINE +5 +CA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +14.315103493 +20 +83.5703927791 +11 +14.315103493 +21 +79.5703927791 +0 +LINE +5 +CB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +14.315103493 +20 +79.5703927791 +11 +13.015103493 +21 +79.5703927791 +0 +LINE +5 +CC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +83.5703927791 +11 +14.315103493 +21 +83.5703927791 +0 +LINE +5 +CD +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +9.31510349296 +20 +95.0703927791 +11 +9.31510349296 +21 +91.0703927791 +0 +LINE +5 +CE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +9.31510349296 +20 +91.0703927791 +11 +8.01510349296 +21 +91.0703927791 +0 +LINE +5 +CF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +95.0703927791 +11 +9.31510349296 +21 +95.0703927791 +0 +LINE +5 +D0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +9.31510349296 +20 +102.070392779 +11 +9.31510349296 +21 +98.0703927791 +0 +LINE +5 +D1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +9.31510349296 +20 +98.0703927791 +11 +8.01510349296 +21 +98.0703927791 +0 +LINE +5 +D2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +102.070392779 +11 +9.31510349296 +21 +102.070392779 +0 +LINE +5 +D3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +15.615103493 +20 +28.5703927791 +11 +26.115103493 +21 +28.5703927791 +0 +LINE +5 +D4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +46.315103493 +20 +28.5703927791 +11 +160.415103493 +21 +28.5703927791 +0 +LINE +5 +D5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +21.115103493 +20 +126.570392779 +11 +21.115103493 +21 +123.270392779 +0 +LINE +5 +D6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +21.115103493 +20 +123.270392779 +11 +17.915103493 +21 +123.270392779 +0 +LINE +5 +D7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +50.7387765123 +20 +126.570392779 +11 +21.115103493 +21 +126.570392779 +0 +LINE +5 +D8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +31.215103493 +20 +103.570392779 +11 +8.01510349296 +21 +103.570392779 +0 +LINE +5 +D9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +108.570392779 +11 +31.215103493 +21 +108.570392779 +0 +LINE +5 +DA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +31.215103493 +20 +108.570392779 +11 +31.215103493 +21 +103.570392779 +0 +LINE +5 +DB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +160.415103493 +20 +126.070392779 +11 +80.1469013801 +21 +126.070392779 +0 +LINE +5 +DC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +31.215103493 +20 +84.5703927791 +11 +13.015103493 +21 +84.5703927791 +0 +LINE +5 +DD +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +31.215103493 +20 +89.5703927791 +11 +31.215103493 +21 +84.5703927791 +0 +LINE +5 +DE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +89.5703927791 +11 +31.215103493 +21 +89.5703927791 +0 +LINE +5 +DF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +41.715103493 +20 +46.5703927791 +11 +15.615103493 +21 +46.5703927791 +0 +LINE +5 +E0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +41.715103493 +20 +51.5703927791 +11 +41.715103493 +21 +46.5703927791 +0 +LINE +5 +E1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +51.5703927791 +11 +41.715103493 +21 +51.5703927791 +0 +LINE +5 +E2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +41.715103493 +20 +65.5703927791 +11 +22.515103493 +21 +65.5703927791 +0 +LINE +5 +E3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +41.715103493 +20 +70.5703927791 +11 +41.715103493 +21 +65.5703927791 +0 +LINE +5 +E4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +70.5703927791 +11 +41.715103493 +21 +70.5703927791 +0 +LINE +5 +E5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +122.070392779 +11 +17.915103493 +21 +123.270392779 +0 +LINE +5 +E6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +108.570392779 +11 +17.915103493 +21 +110.070392779 +0 +LINE +5 +E7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +102.070392779 +11 +8.01510349296 +21 +103.570392779 +0 +LINE +5 +E8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +89.5703927791 +11 +8.01510349296 +21 +91.0703927791 +0 +LINE +5 +E9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +15.615103493 +20 +28.5703927791 +11 +15.615103493 +21 +46.5703927791 +0 +LINE +5 +EA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +17.915103493 +20 +114.070392779 +11 +17.915103493 +21 +118.070392779 +0 +LINE +5 +EB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +8.01510349296 +20 +95.0703927791 +11 +8.01510349296 +21 +98.0703927791 +0 +LINE +5 +EC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +83.5703927791 +11 +13.015103493 +21 +84.5703927791 +0 +LINE +5 +ED +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +76.0703927791 +11 +13.015103493 +21 +79.5703927791 +0 +LINE +5 +EE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +13.015103493 +20 +70.5703927791 +11 +13.015103493 +21 +72.0703927791 +0 +LINE +5 +EF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +64.0703927791 +11 +22.515103493 +21 +65.5703927791 +0 +LINE +5 +F0 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +57.0703927791 +11 +22.515103493 +21 +60.0703927791 +0 +LINE +5 +F1 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +22.515103493 +20 +51.5703927791 +11 +22.515103493 +21 +53.0703927791 +0 +LINE +5 +F2 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +160.415103493 +20 +126.070392779 +11 +160.415103493 +21 +28.5703927791 +0 +CIRCLE +5 +F3 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +44.915103493 +20 +105.570392779 +40 +2.1 +0 +CIRCLE +5 +F4 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +59.715103493 +20 +67.5703927791 +40 +2.1 +0 +CIRCLE +5 +F5 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +98.1151034931 +20 +67.5703927791 +40 +2.1 +0 +CIRCLE +5 +F6 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +155.415103493 +20 +77.5703927791 +40 +2.1 +0 +CIRCLE +5 +F7 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +111.915103493 +20 +106.570392779 +40 +2.1 +0 +CIRCLE +5 +F8 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +140.415103493 +20 +39.5703927791 +40 +2 +0 +CIRCLE +5 +F9 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +110.265103493 +20 +39.5703927791 +40 +2 +0 +CIRCLE +5 +FA +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +107.715103493 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +FB +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +138.165103493 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +FC +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +142.665103493 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +FD +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +116.825103493 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +FE +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +149.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +FF +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +144.915103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +100 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +121.415103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +101 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +102.415103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +102 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +83.4151034931 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +103 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +64.415103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +104 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +45.415103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +105 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +26.415103493 +20 +115.570392779 +40 +2 +0 +CIRCLE +5 +106 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +130.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +107 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +111.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +108 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +92.4151034931 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +109 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +73.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +10A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +54.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +10B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +35.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +10C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +16.415103493 +20 +96.5703927791 +40 +2 +0 +CIRCLE +5 +10D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +97.7351034931 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +10E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +78.6451034931 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +10F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +59.555103493 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +110 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +40.465103493 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +111 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +21.375103493 +20 +77.5703927791 +40 +2 +0 +CIRCLE +5 +112 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +88.5151034931 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +113 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +69.315103493 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +114 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +50.115103493 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +115 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +30.915103493 +20 +58.5703927791 +40 +2 +0 +CIRCLE +5 +116 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +34.115103493 +20 +39.5703927791 +40 +2 +0 +CIRCLE +5 +117 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +86.8651034931 +20 +39.5703927791 +40 +2 +0 +CIRCLE +5 +118 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +63.465103493 +20 +39.5703927791 +40 +2 +0 +ARC +5 +119 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +124.426298668 +20 +54.6043548311 +40 +2.37170824513 +100 +AcDbArc +50 +341.565051177 +51 +198.434948823 +0 +LINE +5 +11A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +122.176298668 +20 +53.8543548311 +11 +123.676298668 +21 +50.8543548311 +0 +LINE +5 +11B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +126.676298668 +20 +53.8543548311 +11 +125.176298668 +21 +50.8543548311 +0 +ARC +5 +11C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +124.426298668 +20 +51.2543548311 +40 +0.85 +100 +AcDbArc +50 +208.072486936 +51 +331.927513064 +0 +ARC +5 +11D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +124.380058759 +20 +41.2642934551 +40 +2.37170824513 +100 +AcDbArc +50 +161.565051177 +51 +18.4349488229 +0 +LINE +5 +11E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +126.630058759 +20 +42.0142934551 +11 +125.130058759 +21 +45.0142934551 +0 +LINE +5 +11F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +122.130058759 +20 +42.0142934551 +11 +123.630058759 +21 +45.0142934551 +0 +ARC +5 +120 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +124.380058759 +20 +44.6142934551 +40 +0.85 +100 +AcDbArc +50 +28.0724869359 +51 +151.927513064 +0 +ARC +5 +121 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +117.630058759 +20 +48.1425852101 +40 +2.37170824513 +100 +AcDbArc +50 +71.5650511771 +51 +288.434948823 +0 +LINE +5 +122 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +118.380058759 +20 +45.8925852101 +11 +121.380058759 +21 +47.3925852101 +0 +LINE +5 +123 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +118.380058759 +20 +50.3925852101 +11 +121.380058759 +21 +48.8925852101 +0 +ARC +5 +124 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +120.980058759 +20 +48.1425852101 +40 +0.85 +100 +AcDbArc +50 +298.072486936 +51 +61.9275130641 +0 +ARC +5 +125 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +131.130058759 +20 +48.1425852101 +40 +2.37170824513 +100 +AcDbArc +50 +251.565051177 +51 +108.434948823 +0 +LINE +5 +126 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +130.380058759 +20 +50.3925852101 +11 +127.380058759 +21 +48.8925852101 +0 +LINE +5 +127 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +130.380058759 +20 +45.8925852101 +11 +127.380058759 +21 +47.3925852101 +0 +ARC +5 +128 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +127.780058759 +20 +48.1425852101 +40 +0.85 +100 +AcDbArc +50 +118.072486936 +51 +241.927513064 +0 +ARC +5 +129 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +53.1318556378 +20 +37.0101551811 +40 +0.85 +100 +AcDbArc +50 +73.0724869359 +51 +196.927513064 +0 +ARC +5 +12A +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +45.8640058389 +20 +34.7320634341 +40 +2.37170824513 +100 +AcDbArc +50 +116.565051177 +51 +333.434948823 +0 +LINE +5 +12B +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +47.9853261825 +20 +33.6714032621 +11 +49.0459863543 +21 +36.8533837771 +0 +LINE +5 +12C +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +44.8033456672 +20 +36.8533837771 +11 +47.9853261825 +21 +37.9140439491 +0 +ARC +5 +12D +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +48.2328135559 +20 +37.1008711511 +40 +0.85 +100 +AcDbArc +50 +343.072486936 +51 +106.927513064 +0 +ARC +5 +12E +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +55.4099473849 +20 +44.2780049801 +40 +2.37170824513 +100 +AcDbArc +50 +296.565051177 +51 +153.434948823 +0 +LINE +5 +12F +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +53.2886270414 +20 +45.3386651511 +11 +52.2279668696 +21 +42.1566846361 +0 +LINE +5 +130 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +56.4706075567 +20 +42.1566846361 +11 +53.2886270414 +21 +41.0960244641 +0 +ARC +5 +131 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +53.041139668 +20 +41.9091972631 +40 +0.85 +100 +AcDbArc +50 +163.072486936 +51 +286.927513064 +0 +ARC +5 +132 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +46.1005120477 +20 +44.1068918771 +40 +2.37170824513 +100 +AcDbArc +50 +26.5650511771 +51 +243.434948823 +0 +LINE +5 +133 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +45.0398518759 +20 +41.9855715331 +11 +48.2218323913 +21 +40.9249113611 +0 +LINE +5 +134 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +48.2218323913 +20 +45.1675520491 +11 +49.282492563 +21 +41.9855715331 +0 +ARC +5 +135 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +48.4693197647 +20 +41.7380841601 +40 +0.85 +100 +AcDbArc +50 +253.072486936 +51 +16.9275130641 +0 +ARC +5 +136 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +55.5006633548 +20 +34.6413474641 +40 +2.37170824513 +100 +AcDbArc +50 +206.565051177 +51 +63.4349488229 +0 +LINE +5 +137 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +56.5613235266 +20 +36.7626678071 +11 +53.3793430112 +21 +37.8233279791 +0 +LINE +5 +138 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbLine +10 +53.3793430112 +20 +33.5806872921 +11 +52.3186828395 +21 +36.7626678071 +0 +CIRCLE +5 +139 +100 +AcDbEntity +8 +Visible Edges(PEC) +6 +ByLayer +62 +256 +370 +-1 +100 +AcDbCircle +10 +156.82650113 +20 +63.0606544828 +40 +0.75 +0 +ENDSEC +0 +SECTION +2 +OBJECTS +0 +DICTIONARY +5 +C +330 +0 +100 +AcDbDictionary +281 +1 +3 +ACAD_GROUP +350 +D +0 +DICTIONARY +5 +D +330 +C +100 +AcDbDictionary +281 +1 +0 +ENDSEC +0 +EOF diff --git a/pcbnew/import_gfx/graphics_import_mgr.cpp b/pcbnew/import_gfx/graphics_import_mgr.cpp new file mode 100644 index 0000000000..c988584e82 --- /dev/null +++ b/pcbnew/import_gfx/graphics_import_mgr.cpp @@ -0,0 +1,73 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "graphics_import_mgr.h" + +#include "dxf_import_plugin.h" +#include "svg_import_plugin.h" + +using namespace std; + +unique_ptr GRAPHICS_IMPORT_MGR::GetPlugin( GFX_FILE_T aType ) +{ + unique_ptr ret; + + switch( aType ) + { + case DXF: + ret.reset( new DXF_IMPORT_PLUGIN() ); + break; + + case SVG: + ret.reset( new SVG_IMPORT_PLUGIN() ); + break; + + default: + throw std::runtime_error( "Unhandled graphics format" ); + break; + } + + return ret; +} + + +unique_ptr GRAPHICS_IMPORT_MGR::GetPluginByExt( const wxString& aExtension ) +{ + for( auto fileType : GFX_FILE_TYPES ) + { + auto plugin = GetPlugin( fileType ); + auto fileExtensions = plugin->GetFileExtensions(); + + for( const auto& fileExt : fileExtensions ) + { + if( aExtension.IsSameAs( fileExt, false ) ) + return plugin; + } + } + + return unique_ptr(); +} + + +const vector GRAPHICS_IMPORT_MGR::GFX_FILE_TYPES = { DXF, SVG }; diff --git a/pcbnew/import_gfx/graphics_import_mgr.h b/pcbnew/import_gfx/graphics_import_mgr.h new file mode 100644 index 0000000000..bd8a6e3cb8 --- /dev/null +++ b/pcbnew/import_gfx/graphics_import_mgr.h @@ -0,0 +1,61 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GRAPHICS_IMPORT_MGR_H +#define GRAPHICS_IMPORT_MGR_H + +#include +#include + +class GRAPHICS_IMPORT_PLUGIN; +class wxString; + +/** + * @brief Class to manage vector graphics importers. + */ +class GRAPHICS_IMPORT_MGR +{ +public: + ///> List of handled file types. + enum GFX_FILE_T { + DXF, + SVG + }; + + ///> Vector containing all GFX_FILE_T values. + static const std::vector GFX_FILE_TYPES; + + ///> Returns a plugin that handles a specific file extension. + static std::unique_ptr GetPluginByExt( const wxString& aExtension ); + + ///> Returns a plugin instance for a specific file type. + static std::unique_ptr GetPlugin( GFX_FILE_T aType ); + +private: + GRAPHICS_IMPORT_MGR() + { + } +}; + +#endif /* GRAPHICS_IMPORT_MGR_H */ diff --git a/pcbnew/import_gfx/graphics_import_plugin.h b/pcbnew/import_gfx/graphics_import_plugin.h new file mode 100644 index 0000000000..b56163648d --- /dev/null +++ b/pcbnew/import_gfx/graphics_import_plugin.h @@ -0,0 +1,120 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GRAPHICS_IMPORT_PLUGIN_H +#define GRAPHICS_IMPORT_PLUGIN_H + +#include + +class GRAPHICS_IMPORTER; + +/** + * @brief Interface for vector graphics import plugins. + */ +class GRAPHICS_IMPORT_PLUGIN +{ +public: + virtual ~GRAPHICS_IMPORT_PLUGIN() + { + } + + /** + * @brief Sets the receiver of the imported shapes. + */ + void SetImporter( GRAPHICS_IMPORTER* aImporter ) + { + m_importer = aImporter; + } + + /** + * @breif Returns the plugin name. + * + * This string will be used as the description in the file dialog. + */ + virtual const wxString GetName() const = 0; + + /** + * @brief Returns a string array of the file extensions handled by this plugin. + */ + virtual const wxArrayString GetFileExtensions() const = 0; + + /** + * @brief Returns a list of wildcards that contains the file extensions + * handled by this plugin, separated with a coma. + */ + wxString GetWildcards() const + { + wxString ret; + bool first = true; + + for( const auto& extension : GetFileExtensions() ) + { + if( first ) + first = false; + else + ret += ", "; + + ret += "*." + extension; + } + + return ret; + } + + /** + * @brief Loads file for import. + * + * It is necessary to have the GRAPHICS_IMPORTER object set before. + */ + virtual bool Load( const wxString& aFileName ) = 0; + + /** + * @brief Return image height from original imported file. + * + * @return Original Image height in internal units. + */ + virtual unsigned int GetImageHeight() const = 0; + + /** + * @brief Return image width from original imported file. + * + * @return Original Image width in internal units. + */ + virtual unsigned int GetImageWidth() const = 0; + + /** + * @brief Actually imports the file. + * + * It is necessary to have loaded the file beforehand. + */ + virtual bool Import( float aXScale, float aYScale ) = 0; + + +protected: + ///> Importer used to create objects representing the imported shapes. + GRAPHICS_IMPORTER* m_importer; +}; + + +#endif /* GRAPHICS_IMPORT_PLUGIN_H */ + diff --git a/pcbnew/import_gfx/graphics_importer.cpp b/pcbnew/import_gfx/graphics_importer.cpp new file mode 100644 index 0000000000..fe2f4b6c4d --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer.cpp @@ -0,0 +1,60 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "graphics_importer.h" +#include "graphics_import_plugin.h" + +GRAPHICS_IMPORTER::GRAPHICS_IMPORTER() : + m_lineWidth( DEFAULT_LINE_WIDTH_DFX ), m_scale( 1.0 ) +{ +} + + +bool GRAPHICS_IMPORTER::Load( const wxString &aFileName ) +{ + m_items.clear(); + + if( !m_plugin ) + { + wxASSERT_MSG( false, "Plugin has to be set before load." ); + return false; + } + + m_plugin->SetImporter( this ); + + return m_plugin->Load( aFileName ); +} + +bool GRAPHICS_IMPORTER::Import( float aXScale, float aYScale) +{ + if( !m_plugin ) + { + wxASSERT_MSG( false, "Plugin has to be set before import." ); + return false; + } + + m_plugin->SetImporter( this ); + + return m_plugin->Import( aXScale, aYScale ); +} diff --git a/pcbnew/import_gfx/graphics_importer.h b/pcbnew/import_gfx/graphics_importer.h new file mode 100644 index 0000000000..e46a213749 --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer.h @@ -0,0 +1,235 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GRAPHICS_IMPORTER_H +#define GRAPHICS_IMPORTER_H + +#include "graphics_import_mgr.h" +#include "graphics_import_plugin.h" + +#include +#include + +#include +#include +#include + +class EDA_ITEM; + +/** + * @brief Interface that creates objects representing shapes for a given data model. + */ +class GRAPHICS_IMPORTER +{ +public: + GRAPHICS_IMPORTER(); + + virtual ~GRAPHICS_IMPORTER() + { + } + + /** + * @brief Sets the import plugin used to obtain shapes from a file. + */ + void SetPlugin( std::unique_ptr aPlugin ) + { + m_plugin = std::move( aPlugin ); + } + + /** + * @brief Load file and get its basic data + * + */ + bool Load( const wxString &aFileName ); + + + /** + * @brief Imports shapes from loaded file. + * + * It is important to have the file loaded before importing. + */ + bool Import( float aXScale, float aYScale); + + + /** + * @brief Get original image Wigth. + * + * @return Width of the loaded image in internal units. + */ + unsigned int GetImageWidth() const + { + return m_originalWidth; + } + + /** + * @brief Get original image Height + * + * @return Height of the loaded image in internal units. + */ + unsigned int GetImageHeight() const + { + return m_originalHeight; + } + + + /** + * @brief Sets the line width for the imported outlines. + */ + void SetLineWidth( double aWidth ) + { + m_lineWidth = (unsigned int)( aWidth * m_scale ); + } + + /** + * @brief Returns the line width used for importing the outlines. + */ + unsigned int GetLineWidth() const + { + return m_lineWidth; + } + + + /** + * @brief Sets scale affecting the imported shapes, for the X direction. + */ + void SetXScale( double aScale ) + { + m_xScale = aScale; + } + + + /** + * @brief Sets scale affecting the imported shapes, for the Y direction. + */ + void SetYScale( double aScale ) + { + m_yScale = aScale; + } + + /** + * @brief Returns the scale factor affecting the imported shapes. + */ + double GetScale() const + { + return m_scale; + } + + /** + * @brief set the scale factor affecting the imported shapes. + * it allows conversion between imported shapes units and internal units + */ + void SetScale( double aScale ) + { + m_scale = aScale; + } + + /** + * @breif Returns the list of objects representing the imported shapes. + */ + std::list>& GetItems() + { + return m_items; + } + + ///> Default line thickness (in internal units) + static constexpr unsigned int DEFAULT_LINE_WIDTH_DFX = 1; + + // Methods to be implemented by derived graphics importers + + /** + * @brief Creates an object representing a line segment. + * @param aOrigin is the segment origin point expressed in internal units. + * @param aEnd is the segment end point expressed in internal units. + */ + virtual void AddLine( const VECTOR2D& aOrigin, const VECTOR2D& aEnd ) = 0; + + /** + * @brief Creates an object representing a circle. + * @param aCenter is the circle center point expressed in internal units. + * @param aRadius is the circle radius expressed in internal units. + */ + virtual void AddCircle( const VECTOR2D& aCenter, unsigned int aRadius ) = 0; + + /** + * @brief Creates an object representing an arc. + * @param aCenter is the arc center point expressed in internal units. + * @param aStart is the arc arm end point expressed in internal units. + * Its length is the arc radius. + * @param aAgnle is the arc angle expressed in decidegrees. + */ + virtual void AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, double aAngle ) = 0; + + virtual void AddPolygon( const std::vector< VECTOR2D >& aVertices ) = 0; + + //virtual void AddArc( const VECTOR2D& aOrigin, double aStartAngle, double aEndAngle ) = 0; + // + /** + * @brief Creates an object representing a text. + * @param aOrigin is the text position. + * @param aText is the displayed text. + * @param aHeight is the text height expressed in internal units. + * @param aWidth is the text width expressed in internal units. + * @param aOrientation is the text orientation angle expressed in decidegrees. + * @param aHJustify is the text horizontal justification. + * @param aVJustify is the text vertical justification. + */ + virtual void AddText( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) = 0; + +protected: + ///> Adds an item to the imported shapes list. + void addItem( std::unique_ptr aItem ) + { + m_items.emplace_back( std::move( aItem ) ); + } + +private: + ///> List of imported items + std::list> m_items; + + ///> Plugin used to load a file + std::unique_ptr m_plugin; + + ///> Total image width + unsigned int m_originalWidth; + + ///> Total image Height; + unsigned int m_originalHeight; + + ///> Default line thickness for the imported graphics + unsigned int m_lineWidth; + + ///> Scale factor applied to the imported graphics, X direction + double m_xScale; + + ///> Scale factor applied to the imported graphics, Y direction + double m_yScale; + + ///> Scale factor applied to the imported graphics + double m_scale; + +}; + +#endif /* GRAPHICS_IMPORTER_H */ diff --git a/pcbnew/import_gfx/graphics_importer_buffer.cpp b/pcbnew/import_gfx/graphics_importer_buffer.cpp new file mode 100644 index 0000000000..a78d084a6a --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer_buffer.cpp @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * @author Janito Vaqueiro Ferreira Filho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "graphics_importer_buffer.h" + +using namespace std; + +template +static std::unique_ptr make_shape( const Args&... aArguments ) +{ + return std::unique_ptr( new T( aArguments... ) ); +} + +void GRAPHICS_IMPORTER_BUFFER::AddLine( const VECTOR2D& aStart, const VECTOR2D& aEnd ) +{ + m_shapes.push_back( make_shape< IMPORTED_LINE >( aStart, aEnd ) ); +} + + +void GRAPHICS_IMPORTER_BUFFER::AddCircle( const VECTOR2D& aCenter, unsigned int aRadius ) +{ + m_shapes.push_back( make_shape< IMPORTED_CIRCLE >( aCenter, aRadius ) ); +} + + +void GRAPHICS_IMPORTER_BUFFER::AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, + double aAngle ) +{ + m_shapes.push_back( make_shape< IMPORTED_ARC >( aCenter, aStart, aAngle ) ); +} + + +void GRAPHICS_IMPORTER_BUFFER::AddPolygon( const std::vector< VECTOR2D >& aVertices ) +{ + m_shapes.push_back( make_shape< IMPORTED_POLYGON >( aVertices ) ); +} + + +void GRAPHICS_IMPORTER_BUFFER::AddText( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) +{ + m_shapes.push_back( make_shape< IMPORTED_TEXT >( aOrigin, aText, aHeight, aWidth, aOrientation, + aHJustify, aVJustify ) ); +} + +void GRAPHICS_IMPORTER_BUFFER::ImportTo( GRAPHICS_IMPORTER& aImporter ) +{ + for( auto& shape : m_shapes ) + shape->ImportTo( aImporter ); +} diff --git a/pcbnew/import_gfx/graphics_importer_buffer.h b/pcbnew/import_gfx/graphics_importer_buffer.h new file mode 100644 index 0000000000..e7e22c2228 --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer_buffer.h @@ -0,0 +1,167 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2017 CERN + * @author Janito Vaqueiro Ferreira Filho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GRAPHICS_IMPORTER_BUFFER_H +#define GRAPHICS_IMPORTER_BUFFER_H + +#include "graphics_importer.h" + +#include + +class IMPORTED_SHAPE +{ +public: + virtual void ImportTo( GRAPHICS_IMPORTER& aImporter ) const = 0; +}; + + +class IMPORTED_LINE : public IMPORTED_SHAPE +{ +public: + IMPORTED_LINE( const VECTOR2D& aStart, const VECTOR2D& aEnd ) + : m_start( aStart ), m_end( aEnd ) + { + } + + void ImportTo( GRAPHICS_IMPORTER& aImporter ) const override + { + aImporter.AddLine( m_start, m_end ); + } + +private: + const VECTOR2D m_start; + const VECTOR2D m_end; +}; + + +class IMPORTED_CIRCLE : public IMPORTED_SHAPE +{ +public: + IMPORTED_CIRCLE( const VECTOR2D& aCenter, double aRadius ) + : m_center( aCenter ), m_radius( aRadius ) + { + } + + void ImportTo( GRAPHICS_IMPORTER& aImporter ) const override + { + aImporter.AddCircle( m_center, m_radius ); + } + +private: + const VECTOR2D m_center; + double m_radius; +}; + + +class IMPORTED_ARC : public IMPORTED_SHAPE +{ +public: + IMPORTED_ARC( const VECTOR2D& aCenter, const VECTOR2D& aStart, double aAngle ) + : m_center( aCenter ), m_start( aStart ), m_angle( aAngle ) + { + } + + void ImportTo( GRAPHICS_IMPORTER& aImporter ) const override + { + aImporter.AddArc( m_center, m_start, m_angle ); + } + +private: + const VECTOR2D m_center; + const VECTOR2D m_start; + double m_angle; +}; + + +class IMPORTED_POLYGON : public IMPORTED_SHAPE +{ +public: + IMPORTED_POLYGON( const std::vector< VECTOR2D >& aVertices ) + : m_vertices( aVertices ) + { + } + + void ImportTo( GRAPHICS_IMPORTER& aImporter ) const override + { + aImporter.AddPolygon( m_vertices ); + } + +private: + const std::vector< VECTOR2D > m_vertices; +}; + + +class IMPORTED_TEXT : public IMPORTED_SHAPE +{ +public: + IMPORTED_TEXT( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) + : m_origin( aOrigin ), m_text( aText ), + m_height( aHeight ), m_width( aWidth ), + m_orientation( aOrientation ), + m_hJustify( aHJustify ), m_vJustify( aVJustify ) + { + } + + void ImportTo( GRAPHICS_IMPORTER& aImporter ) const override + { + aImporter.AddText( m_origin, m_text, m_height, m_width, m_orientation, + m_hJustify, m_vJustify ); + } + +private: + const VECTOR2D& m_origin; + const wxString& m_text; + unsigned int m_height; + unsigned int m_width; + double m_orientation; + EDA_TEXT_HJUSTIFY_T m_hJustify; + EDA_TEXT_VJUSTIFY_T m_vJustify; +}; + + +class GRAPHICS_IMPORTER_BUFFER : public GRAPHICS_IMPORTER +{ +public: + void AddLine( const VECTOR2D& aStart, const VECTOR2D& aEnd ) override; + + void AddCircle( const VECTOR2D& aCenter, unsigned int aRadius ) override; + + void AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, double aAngle ) override; + + void AddPolygon( const std::vector< VECTOR2D >& aVertices ) override; + + void AddText( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) override; + + void ImportTo( GRAPHICS_IMPORTER& aImporter ); + +protected: + ///> List of imported shapes + std::list< std::unique_ptr< IMPORTED_SHAPE > > m_shapes; +}; + +#endif /* GRAPHICS_IMPORTER_BUFFER */ diff --git a/pcbnew/import_gfx/graphics_importer_pcbnew.cpp b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp new file mode 100644 index 0000000000..6c06aa6e06 --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer_pcbnew.cpp @@ -0,0 +1,154 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "graphics_importer_pcbnew.h" + +#include +#include +#include +#include +#include + +using namespace std; + +static std::vector convertPoints( const std::vector& aPoints, + double aScaleFactor ); + + +static wxPoint Round( const VECTOR2D& aVec ) +{ + return wxPoint( (int) aVec.x, (int) aVec.y ); +} + + +void GRAPHICS_IMPORTER_PCBNEW::AddLine( const VECTOR2D& aOrigin, const VECTOR2D& aEnd ) +{ + unique_ptr line( createDrawing() ); + line->SetShape( S_SEGMENT ); + line->SetLayer( GetLayer() ); + line->SetWidth( GetLineWidth() ); + line->SetStart( Round ( aOrigin * GetScale() ) ); + line->SetEnd( Round ( aEnd * GetScale() ) ); + addItem( std::move( line ) ); +} + + +void GRAPHICS_IMPORTER_PCBNEW::AddCircle( const VECTOR2D& aCenter, unsigned int aRadius ) +{ + unique_ptr circle( createDrawing() ); + circle->SetShape( S_CIRCLE ); + circle->SetLayer( GetLayer() ); + circle->SetWidth( GetLineWidth() ); + circle->SetCenter( Round ( aCenter * GetScale() ) ); + circle->SetArcStart( Round ( VECTOR2D( aCenter.x + aRadius, aCenter.y ) * GetScale() ) ); + addItem( std::move( circle ) ); +} + + +void GRAPHICS_IMPORTER_PCBNEW::AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, double aAngle ) +{ + unique_ptr arc( createDrawing() ); + arc->SetShape( S_ARC ); + arc->SetLayer( GetLayer() ); + arc->SetWidth( GetLineWidth() ); + arc->SetCenter( Round ( aCenter * GetScale() ) ); + arc->SetArcStart( Round ( aStart * GetScale() ) ); + arc->SetAngle( aAngle ); + addItem( std::move( arc ) ); +} + + +void GRAPHICS_IMPORTER_PCBNEW::AddPolygon( const std::vector< VECTOR2D >& aVertices ) +{ + std::vector< wxPoint > convertedVertices = convertPoints( aVertices, GetScale() ); + unique_ptr polygon( createDrawing() ); + polygon->SetShape( S_POLYGON ); + polygon->SetLayer( GetLayer() ); + polygon->SetPolyPoints( convertedVertices ); + addItem( std::move( polygon ) ); +} + + +void GRAPHICS_IMPORTER_PCBNEW::AddText( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) +{ + unique_ptr boardItem; + EDA_TEXT* textItem; + tie( boardItem, textItem ) = createText(); + boardItem->SetLayer( GetLayer() ); + textItem->SetThickness( GetLineWidth() ); + textItem->SetTextPos( Round ( aOrigin * GetScale() ) ); + textItem->SetTextAngle( aOrientation ); + textItem->SetTextWidth( (double)aWidth * GetScale() ); + textItem->SetTextHeight( (double)aHeight * GetScale() ); + textItem->SetVertJustify( aVJustify ); + textItem->SetHorizJustify( aHJustify ); + textItem->SetText( aText ); + addItem( std::move( boardItem ) ); +} + + +unique_ptr GRAPHICS_IMPORTER_BOARD::createDrawing() +{ + return unique_ptr( new DRAWSEGMENT( m_board ) ); +} + + +pair, EDA_TEXT*> GRAPHICS_IMPORTER_BOARD::createText() +{ + TEXTE_PCB* text = new TEXTE_PCB( m_board ); + return make_pair( unique_ptr( text ), static_cast( text ) ); +} + + +unique_ptr GRAPHICS_IMPORTER_MODULE::createDrawing() +{ + return unique_ptr( new EDGE_MODULE( m_module ) ); +} + + +pair, EDA_TEXT*> GRAPHICS_IMPORTER_MODULE::createText() +{ + TEXTE_MODULE* text = new TEXTE_MODULE( m_module ); + return make_pair( unique_ptr( text ), static_cast( text ) ); +} + + +static std::vector< wxPoint > convertPoints( const std::vector& aPoints, + double aScaleFactor ) +{ + std::vector convertedPoints; + convertedPoints.reserve( aPoints.size() ); + + for( const VECTOR2D& precisePoint : aPoints ) + { + auto scaledX = precisePoint.x * aScaleFactor; + auto scaledY = precisePoint.y * aScaleFactor; + + convertedPoints.emplace_back( scaledX, scaledY ); + } + + return convertedPoints; +} diff --git a/pcbnew/import_gfx/graphics_importer_pcbnew.h b/pcbnew/import_gfx/graphics_importer_pcbnew.h new file mode 100644 index 0000000000..671f54763a --- /dev/null +++ b/pcbnew/import_gfx/graphics_importer_pcbnew.h @@ -0,0 +1,119 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef GRAPHICS_IMPORTER_PCBNEW_H +#define GRAPHICS_IMPORTER_PCBNEW_H + +#include "graphics_importer.h" + +#include + +class BOARD_ITEM; +class BOARD; +class MODULE; +class DRAWSEGMENT; +class EDA_TEXT; + +class GRAPHICS_IMPORTER_PCBNEW : public GRAPHICS_IMPORTER +{ +public: + GRAPHICS_IMPORTER_PCBNEW() + : m_layer( Dwgs_User ) + { + } + + /** + * @brief Sets the target layer for the imported shapes. + * @param aLayer is the layer to be used by the imported shapes. + */ + void SetLayer( PCB_LAYER_ID aLayer ) + { + m_layer = aLayer; + } + + /** + * @brief Returns the target layer for the imported shapes. + */ + PCB_LAYER_ID GetLayer() const + { + return m_layer; + } + + void AddLine( const VECTOR2D& aOrigin, const VECTOR2D& aEnd ) override; + + void AddCircle( const VECTOR2D& aOrigin, unsigned int aRadius ) override; + + void AddArc( const VECTOR2D& aCenter, const VECTOR2D& aStart, double aAngle ) override; + + void AddPolygon( const std::vector< VECTOR2D >& aVertices ) override; + + void AddText( const VECTOR2D& aOrigin, const wxString& aText, + unsigned int aHeight, unsigned aWidth, double aOrientation, + EDA_TEXT_HJUSTIFY_T aHJustify, EDA_TEXT_VJUSTIFY_T aVJustify ) override; + +protected: + ///> Create an object representing a graphical shape. + virtual std::unique_ptr createDrawing() = 0; + + ///> Create an object representing a text. Both pointers point to different parts of the + ///> same object, the EDA_TEXT pointer is simply for convenience. + virtual std::pair, EDA_TEXT*> createText() = 0; + + ///> Target layer for the imported shapes. + PCB_LAYER_ID m_layer; +}; + + +class GRAPHICS_IMPORTER_BOARD : public GRAPHICS_IMPORTER_PCBNEW +{ +public: + GRAPHICS_IMPORTER_BOARD( BOARD* aBoard ) + : m_board( aBoard ) + { + } + +protected: + std::unique_ptr createDrawing() override; + std::pair, EDA_TEXT*> createText() override; + + BOARD* m_board; +}; + + +class GRAPHICS_IMPORTER_MODULE : public GRAPHICS_IMPORTER_PCBNEW +{ +public: + GRAPHICS_IMPORTER_MODULE( MODULE* aModule ) + : m_module( aModule ) + { + } + +protected: + std::unique_ptr createDrawing() override; + std::pair, EDA_TEXT*> createText() override; + + MODULE* m_module; +}; + +#endif /* GRAPHICS_IMPORTER_PCBNEW */ diff --git a/pcbnew/import_gfx/nanosvg.cpp b/pcbnew/import_gfx/nanosvg.cpp new file mode 100644 index 0000000000..8ffad72b4b --- /dev/null +++ b/pcbnew/import_gfx/nanosvg.cpp @@ -0,0 +1,6 @@ +#include +#include +#include + +#define NANOSVG_IMPLEMENTATION +#include "nanosvg.h" diff --git a/pcbnew/import_gfx/nanosvg.h b/pcbnew/import_gfx/nanosvg.h new file mode 100644 index 0000000000..a6c60155ea --- /dev/null +++ b/pcbnew/import_gfx/nanosvg.h @@ -0,0 +1,2849 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example + * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) + * + * Arc calculation code based on canvg (https://code.google.com/p/canvg/) + * + * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html + * + */ + +#ifndef NANOSVG_H +#define NANOSVG_H + +#ifdef __cplusplus +extern "C" { +#endif + +// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. +// +// The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. +// +// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! +// +// The shapes in the SVG images are transformed by the viewBox and converted to specified units. +// That is, you should get the same looking data as your designed in your favorite app. +// +// NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose +// to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. +// +// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// DPI (dots-per-inch) controls how the unit conversion is done. +// +// If you don't know or care about the units stuff, "px" and 96 should get you going. + + +/* Example Usage: + // Load + NSVGImage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + printf("size: %f x %f\n", image->width, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3, +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2, +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2, +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2, +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1, +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Deletes list of paths. +void nsvgDelete(NSVGimage* image); + +#ifdef __cplusplus +}; +#endif + +#endif // NANOSVG_H + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return strchr("0123456789", c) != 0; +} + +static int nsvg__isnum(char c) +{ + return strchr("0123456789+-.eE", c) != 0; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + attr[nattr++] = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + attr[nattr++] = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1, +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX, +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static NSVGparser* nsvg__createParser() +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + while (grad) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + while (ref != NULL) { + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + ref = nsvg__findGradientData(p, ref->ref); + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape *shape, *cur, *prev; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + prev = NULL; + cur = p->image->shapes; + while (cur != NULL) { + prev = cur; + cur = cur->next; + } + if (prev == NULL) + p->image->shapes = shape; + else + prev->next = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if (*s == 'e' || *s == 'E') { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int c = 0, r = 0, g = 0, b = 0; + int n = 0; + str++; // skip # + // Calculate number of characters. + while(str[n] && !nsvg__isspace(str[n])) + n++; + if (n == 6) { + sscanf(str, "%x", &c); + } else if (n == 3) { + sscanf(str, "%x", &c); + c = (c&0xf) | ((c&0xf0) << 4) | ((c&0xf00) << 8); + c |= c<<4; + } + r = (c >> 16) & 0xff; + g = (c >> 8) & 0xff; + b = c & 0xff; + return NSVG_RGB(r,g,b); +} + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int r = -1, g = -1, b = -1; + char s1[32]="", s2[32]=""; + sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b); + if (strchr(s1, '%')) { + return NSVG_RGB((r*255)/100,(g*255)/100,(b*255)/100); + } else { + return NSVG_RGB(r,g,b); + } +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = 0; + sscanf(str, "%f", &val); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char units[32]=""; + sscanf(str, "%f%s", &coord.value, units); + coord.units = nsvg__parseUnits(units); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + str += nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + str += nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + str += nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + str += nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + str += nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + str += nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str == '#') + str++; + while (i < 63 && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + } + return 0; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fa) { + // Choose large arc + if (da > 0.0f) + da = da - 2*NSVG_PI; + else + da = 2*NSVG_PI + da; + } + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + kappa = fabsf(4.0f / 3.0f * (1.0f - cosf(hda)) / sinf(hda)); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * (i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (nsvg__isnum(item[0])) { + if (nargs < 10) + args[nargs++] = (float)atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + rargs = nsvg__getArgsPerElement(cmd); + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + grad->xform[0] *= sx; + grad->xform[1] *= sx; + grad->xform[2] *= sy; + grad->xform[3] *= sy; + grad->xform[4] += tx*sx; + grad->xform[5] += ty*sx; +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif diff --git a/pcbnew/import_gfx/svg_import_plugin.cpp b/pcbnew/import_gfx/svg_import_plugin.cpp new file mode 100644 index 0000000000..4281fef3a1 --- /dev/null +++ b/pcbnew/import_gfx/svg_import_plugin.cpp @@ -0,0 +1,245 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Janito V. Ferreira Filho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "svg_import_plugin.h" + +#include +#include + +#include +#include + +#include "convert_to_biu.h" +#include "graphics_importer.h" + +static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints, + std::function< const float&( const float&, const float& ) > comparator ); +static float calculateBezierSegmentationThreshold( const float* aCurvePoints ); +static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset, + float aStep, const float* aCurvePoints, float aSegmentationThreshold, + std::vector< VECTOR2D >& aGeneratedPoints ); +static void createNewBezierCurveSegments( const VECTOR2D& aStart, const VECTOR2D& aMiddle, + const VECTOR2D& aEnd, float aOffset, float aStep, const float* aCurvePoints, + float aSegmentationThreshold, std::vector< VECTOR2D >& aGeneratedPoints ); +static VECTOR2D getBezierPoint( const float* aCurvePoints, float aStep ); +static VECTOR2D getPoint( const float* aPointCoordinates ); +static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd, + float aDistance ); +static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart, + const VECTOR2D& aLineEnd ); + +bool SVG_IMPORT_PLUGIN::Load( const wxString& aFileName ) +{ + wxCHECK( m_importer, false ); + + m_parsedImage = nsvgParseFromFile( aFileName.c_str(), "mm", 96 ); + + wxCHECK( m_parsedImage, false ); + + return true; +} + +bool SVG_IMPORT_PLUGIN::Import(float aXScale, float aYScale) +{ + for( NSVGshape* shape = m_parsedImage->shapes; shape != NULL; shape = shape->next ) + { + m_importer->SetLineWidth( shape->strokeWidth ); + + for( NSVGpath* path = shape->paths; path != NULL; path = path->next ) + DrawPath( path->pts, path->npts, path->closed ); + } + + return true; +} + + +void SVG_IMPORT_PLUGIN::DrawPath( const float* aPoints, int aNumPoints, bool aClosedPath ) +{ + std::vector< VECTOR2D > collectedPathPoints; + + if( aNumPoints > 0 ) + DrawCubicBezierPath( aPoints, aNumPoints, collectedPathPoints ); + + if( aClosedPath ) + DrawPolygon( collectedPathPoints ); + else + DrawLineSegments( collectedPathPoints ); +} + + +void SVG_IMPORT_PLUGIN::DrawCubicBezierPath( const float* aPoints, int aNumPoints, + std::vector< VECTOR2D >& aGeneratedPoints ) +{ + const int pointsPerSegment = 4; + const int curveSpecificPointsPerSegment = 3; + const int curveSpecificCoordinatesPerSegment = 2 * curveSpecificPointsPerSegment; + const float* currentPoints = aPoints; + int remainingPoints = aNumPoints; + + while( remainingPoints >= pointsPerSegment ) + { + DrawCubicBezierCurve( currentPoints, aGeneratedPoints ); + currentPoints += curveSpecificCoordinatesPerSegment; + remainingPoints -= curveSpecificPointsPerSegment; + } +} + + +void SVG_IMPORT_PLUGIN::DrawCubicBezierCurve( const float* aPoints, + std::vector< VECTOR2D >& aGeneratedPoints ) +{ + auto start = getBezierPoint( aPoints, 0.f ); + auto end = getBezierPoint( aPoints, 1.f ); + auto segmentationThreshold = calculateBezierSegmentationThreshold( aPoints ); + + aGeneratedPoints.push_back( start ); + segmentBezierCurve( start, end, 0.f, 0.5f, aPoints, segmentationThreshold, aGeneratedPoints ); + aGeneratedPoints.push_back( end ); +} + + +void SVG_IMPORT_PLUGIN::DrawPolygon( const std::vector< VECTOR2D >& aPoints ) +{ + m_importer->AddPolygon( aPoints ); +} + + +void SVG_IMPORT_PLUGIN::DrawLineSegments( const std::vector< VECTOR2D >& aPoints ) +{ + unsigned int numLineStartPoints = aPoints.size() - 1; + + for( unsigned int pointIndex = 0; pointIndex < numLineStartPoints; ++pointIndex ) + m_importer->AddLine( aPoints[ pointIndex ], aPoints[ pointIndex + 1 ] ); +} + + +static VECTOR2D getPoint( const float* aPointCoordinates ) +{ + return VECTOR2D( aPointCoordinates[0], aPointCoordinates[1] ); +} + + +static VECTOR2D getBezierPoint( const float* aPoints, float aStep ) +{ + const int coordinatesPerPoint = 2; + + auto firstCubicPoint = getPoint( aPoints ); + auto secondCubicPoint = getPoint( aPoints + 1 * coordinatesPerPoint ); + auto thirdCubicPoint = getPoint( aPoints + 2 * coordinatesPerPoint ); + auto fourthCubicPoint = getPoint( aPoints + 3 * coordinatesPerPoint ); + + auto firstQuadraticPoint = getPointInLine( firstCubicPoint, secondCubicPoint, aStep ); + auto secondQuadraticPoint = getPointInLine( secondCubicPoint, thirdCubicPoint, aStep ); + auto thirdQuadraticPoint = getPointInLine( thirdCubicPoint, fourthCubicPoint, aStep ); + + auto firstLinearPoint = getPointInLine( firstQuadraticPoint, secondQuadraticPoint, aStep ); + auto secondLinearPoint = getPointInLine( secondQuadraticPoint, thirdQuadraticPoint, aStep ); + + return getPointInLine( firstLinearPoint, secondLinearPoint, aStep ); +} + + +static VECTOR2D getPointInLine( const VECTOR2D& aLineStart, const VECTOR2D& aLineEnd, + float aDistance ) +{ + return aLineStart + ( aLineEnd - aLineStart ) * aDistance; +} + + +static float calculateBezierSegmentationThreshold( const float* aCurvePoints ) +{ + using comparatorFunction = const float&(*)( const float&, const float& ); + + auto minimumComparator = static_cast< comparatorFunction >( &std::min ); + auto maximumComparator = static_cast< comparatorFunction >( &std::max ); + + VECTOR2D minimum = calculateBezierBoundingBoxExtremity( aCurvePoints, minimumComparator ); + VECTOR2D maximum = calculateBezierBoundingBoxExtremity( aCurvePoints, maximumComparator ); + VECTOR2D boundingBoxDimensions = maximum - minimum; + + return 0.001 * std::max( boundingBoxDimensions.x, boundingBoxDimensions.y ); +} + + +static VECTOR2D calculateBezierBoundingBoxExtremity( const float* aCurvePoints, + std::function< const float&( const float&, const float& ) > comparator ) +{ + float x, y; + + x = aCurvePoints[0]; + y = aCurvePoints[1]; + + for( int pointIndex = 1; pointIndex < 3; ++pointIndex ) + { + x = comparator( x, aCurvePoints[ 2 * pointIndex ] ); + y = comparator( y, aCurvePoints[ 2 * pointIndex + 1 ] ); + } + + return VECTOR2D( x, y ); +} + + +static void segmentBezierCurve( const VECTOR2D& aStart, const VECTOR2D& aEnd, float aOffset, + float aStep, const float* aCurvePoints, float aSegmentationThreshold, + std::vector< VECTOR2D >& aGeneratedPoints ) +{ + VECTOR2D middle = getBezierPoint( aCurvePoints, aOffset + aStep ); + float distanceToPreviousSegment = distanceFromPointToLine( middle, aStart, aEnd ); + + if( distanceToPreviousSegment > aSegmentationThreshold ) + { + createNewBezierCurveSegments( aStart, middle, aEnd, aOffset, aStep, aCurvePoints, + aSegmentationThreshold, aGeneratedPoints ); + } +} + + +static void createNewBezierCurveSegments( const VECTOR2D& aStart, const VECTOR2D& aMiddle, + const VECTOR2D& aEnd, float aOffset, float aStep, const float* aCurvePoints, + float aSegmentationThreshold, std::vector< VECTOR2D >& aGeneratedPoints ) +{ + float newStep = aStep / 2.f; + float offsetAfterMiddle = aOffset + aStep; + + segmentBezierCurve( aStart, aMiddle, aOffset, newStep, aCurvePoints, aSegmentationThreshold, + aGeneratedPoints ); + + aGeneratedPoints.push_back( aMiddle ); + + segmentBezierCurve( aMiddle, aEnd, offsetAfterMiddle, newStep, aCurvePoints, + aSegmentationThreshold, aGeneratedPoints ); +} + + +static float distanceFromPointToLine( const VECTOR2D& aPoint, const VECTOR2D& aLineStart, + const VECTOR2D& aLineEnd ) +{ + auto lineDirection = aLineEnd - aLineStart; + auto lineNormal = lineDirection.Perpendicular().Resize( 1.f ); + auto lineStartToPoint = aPoint - aLineStart; + + auto distance = lineNormal.Dot( lineStartToPoint ); + + return fabs( distance ); +} diff --git a/pcbnew/import_gfx/svg_import_plugin.h b/pcbnew/import_gfx/svg_import_plugin.h new file mode 100644 index 0000000000..e43bb575e0 --- /dev/null +++ b/pcbnew/import_gfx/svg_import_plugin.h @@ -0,0 +1,90 @@ +/* + * This program source code file is part of KICAD, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Janito V. Ferreira Filho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SVG_IMPORT_PLUGIN_H +#define SVG_IMPORT_PLUGIN_H + +#include "nanosvg.h" + +#include "graphics_import_plugin.h" +//#include "drw_interface.h" +#include "convert_to_biu.h" +#include + + +class SVG_IMPORT_PLUGIN : public GRAPHICS_IMPORT_PLUGIN +{ +public: + const wxString GetName() const override + { + return "Scalable Vector Graphics"; + } + + const wxArrayString GetFileExtensions() const override + { + return wxArrayString( 1, { "svg" } ); + } + + bool Import(float aXScale, float aYScale) override; + bool Load( const wxString& aFileName ) override; + + virtual unsigned int GetImageHeight() const override + { + if( !m_parsedImage ) + { + wxASSERT_MSG(false, "Image must have been loaded before checking height"); + return false; + } + + return Millimeter2iu( m_parsedImage->height ); + } + + virtual unsigned int GetImageWidth() const override + { + if( !m_parsedImage ) + { + wxASSERT_MSG(false, "Image must have been loaded before checking width"); + return false; + } + + return Millimeter2iu( m_parsedImage->width ); + } + + +private: + void DrawPath( const float* aPoints, int aNumPoints, bool aClosedPath ); + + void DrawCubicBezierPath( const float* aPoints, int aNumPoints, + std::vector< VECTOR2D >& aGeneratedPoints ); + + void DrawCubicBezierCurve( const float* aPoints, + std::vector< VECTOR2D >& aGeneratedPoints ); + + void DrawPolygon( const std::vector< VECTOR2D >& aPoints ); + void DrawLineSegments( const std::vector< VECTOR2D >& aPoints ); + + struct NSVGimage* m_parsedImage; +}; + +#endif /* SVG_IMPORT_PLUGIN_H */ diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 03e10ce5af..a7f3af2db5 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -738,26 +739,29 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent ) if( !m_frame->GetModel() ) return 0; - DIALOG_DXF_IMPORT dlg( m_frame ); + //DIALOG_DXF_IMPORT dlg( m_frame ); + DIALOG_IMPORT_GFX dlg( m_frame, m_editModules ); int dlgResult = dlg.ShowModal(); - const std::list& list = dlg.GetImportedItems(); + /*const std::list*/auto& list = dlg.GetImportedItems(); if( dlgResult != wxID_OK || list.empty() ) return 0; - VECTOR2I cursorPos = m_controls->GetCursorPosition(); - VECTOR2I delta = cursorPos - list.front()->GetPosition(); - // Add a VIEW_GROUP that serves as a preview for the new item SELECTION preview; BOARD_COMMIT commit( m_frame ); // Build the undo list & add items to the current view - for( auto item : list ) + //for( auto item : list ) + for( auto it = list.begin(), itEnd = list.end(); it != itEnd; ++it ) { - assert( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T ); + EDA_ITEM* item = it->get(); + + wxASSERT( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T ); + preview.Add( item ); + it->release(); } BOARD_ITEM* firstItem = static_cast( preview.Front() ); @@ -771,8 +775,8 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent ) SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF ); // Now move the new items to the current cursor position: - cursorPos = m_controls->GetCursorPosition(); - delta = cursorPos - firstItem->GetPosition(); + VECTOR2I cursorPos = m_controls->GetCursorPosition(); + VECTOR2I delta = cursorPos - firstItem->GetPosition(); for( auto item : preview ) static_cast( item )->Move( wxPoint( delta.x, delta.y ) ); @@ -895,7 +899,8 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent ) } default: - assert( false ); + wxASSERT_MSG( false, + wxString::Format( "item type %d not allowed", item->Type() ) ); break; }