From 3dd2ae762d58847050696ec92726bfafb2623473 Mon Sep 17 00:00:00 2001 From: Marek Roszko Date: Sat, 8 Oct 2022 18:00:05 -0400 Subject: [PATCH] Refactor step export to use our normal board processing routines --- common/jobs/job_export_pcb_step.h | 6 +- kicad/cli/command_export_pcb_step.cpp | 9 +- libs/kimath/include/math/vector3.h | 25 + pcbnew/CMakeLists.txt | 6 +- pcbnew/dialogs/dialog_export_step.cpp | 3 - pcbnew/exporters/step/CMakeLists.txt | 44 - pcbnew/exporters/step/exporter_step.cpp | 361 +++++++ pcbnew/exporters/step/exporter_step.h | 103 ++ pcbnew/exporters/step/kicad2step.cpp | 290 ------ pcbnew/exporters/step/kicad2step.h | 76 -- pcbnew/exporters/step/pcb/base.cpp | 299 ------ pcbnew/exporters/step/pcb/base.h | 135 --- pcbnew/exporters/step/pcb/kicadcurve.cpp | 295 ------ pcbnew/exporters/step/pcb/kicadcurve.h | 71 -- pcbnew/exporters/step/pcb/kicadfootprint.cpp | 474 --------- pcbnew/exporters/step/pcb/kicadfootprint.h | 81 -- pcbnew/exporters/step/pcb/kicadmodel.cpp | 124 --- pcbnew/exporters/step/pcb/kicadmodel.h | 50 - pcbnew/exporters/step/pcb/kicadpad.cpp | 192 ---- pcbnew/exporters/step/pcb/kicadpad.h | 70 -- pcbnew/exporters/step/pcb/kicadpcb.cpp | 662 ------------- pcbnew/exporters/step/pcb/kicadpcb.h | 135 --- .../{pcb/oce_utils.cpp => step_pcb_model.cpp} | 928 +++--------------- .../{pcb/oce_utils.h => step_pcb_model.h} | 88 +- pcbnew/footprint.h | 6 +- pcbnew/pcbnew_jobs_handler.cpp | 44 +- pcbnew/plugins/altium/altium_parser_pcb.h | 6 +- qa/unittests/CMakeLists.txt | 3 +- .../libs/kimath/math/test_vector3.cpp | 28 + qa/unittests/utils/kicad2step/CMakeLists.txt | 63 -- .../utils/kicad2step/pcb/test_base.cpp | 202 ---- qa/unittests/utils/kicad2step/test_module.cpp | 48 - 32 files changed, 741 insertions(+), 4186 deletions(-) delete mode 100644 pcbnew/exporters/step/CMakeLists.txt create mode 100644 pcbnew/exporters/step/exporter_step.cpp create mode 100644 pcbnew/exporters/step/exporter_step.h delete mode 100644 pcbnew/exporters/step/kicad2step.cpp delete mode 100644 pcbnew/exporters/step/kicad2step.h delete mode 100644 pcbnew/exporters/step/pcb/base.cpp delete mode 100644 pcbnew/exporters/step/pcb/base.h delete mode 100644 pcbnew/exporters/step/pcb/kicadcurve.cpp delete mode 100644 pcbnew/exporters/step/pcb/kicadcurve.h delete mode 100644 pcbnew/exporters/step/pcb/kicadfootprint.cpp delete mode 100644 pcbnew/exporters/step/pcb/kicadfootprint.h delete mode 100644 pcbnew/exporters/step/pcb/kicadmodel.cpp delete mode 100644 pcbnew/exporters/step/pcb/kicadmodel.h delete mode 100644 pcbnew/exporters/step/pcb/kicadpad.cpp delete mode 100644 pcbnew/exporters/step/pcb/kicadpad.h delete mode 100644 pcbnew/exporters/step/pcb/kicadpcb.cpp delete mode 100644 pcbnew/exporters/step/pcb/kicadpcb.h rename pcbnew/exporters/step/{pcb/oce_utils.cpp => step_pcb_model.cpp} (58%) rename pcbnew/exporters/step/{pcb/oce_utils.h => step_pcb_model.h} (71%) delete mode 100644 qa/unittests/utils/kicad2step/CMakeLists.txt delete mode 100644 qa/unittests/utils/kicad2step/pcb/test_base.cpp delete mode 100644 qa/unittests/utils/kicad2step/test_module.cpp diff --git a/common/jobs/job_export_pcb_step.h b/common/jobs/job_export_pcb_step.h index e6e97fb9dc..8ce7bd9037 100644 --- a/common/jobs/job_export_pcb_step.h +++ b/common/jobs/job_export_pcb_step.h @@ -32,7 +32,8 @@ public: m_overwrite( false ), m_useGridOrigin( false ), m_useDrillOrigin( false ), - m_includeVirtual( false ), + m_boardOnly( false ), + m_includeExcludedBom( false ), m_substModels( false ), m_filename(), m_outputFile(), @@ -45,7 +46,8 @@ public: bool m_overwrite; bool m_useGridOrigin; bool m_useDrillOrigin; - bool m_includeVirtual; + bool m_boardOnly; + bool m_includeExcludedBom; bool m_substModels; wxString m_filename; wxString m_outputFile; diff --git a/kicad/cli/command_export_pcb_step.cpp b/kicad/cli/command_export_pcb_step.cpp index 2ca9b90ce0..6fc6f37e0f 100644 --- a/kicad/cli/command_export_pcb_step.cpp +++ b/kicad/cli/command_export_pcb_step.cpp @@ -35,6 +35,7 @@ #define ARG_INPUT "input" #define ARG_MIN_DISTANCE "--min-distance" #define ARG_USER_ORIGIN "--user-origin" +#define ARG_BOARD_ONLY "--board-only" #define REGEX_QUANTITY "([\\s]*[+-]?[\\d]*[.]?[\\d]*)" #define REGEX_DELIMITER "(?:[\\s]*x)" @@ -67,6 +68,11 @@ CLI::EXPORT_PCB_STEP_COMMAND::EXPORT_PCB_STEP_COMMAND() : COMMAND( "step" ) .implicit_value( true ) .default_value( false ); + m_argParser.add_argument( ARG_BOARD_ONLY ) + .help( "only generate a board with no components" ) + .implicit_value( true ) + .default_value( false ); + m_argParser.add_argument( ARG_MIN_DISTANCE ) .default_value( std::string() ) .help( "Minimum distance between points to treat them as separate ones (default 0.01mm)" ); @@ -88,11 +94,12 @@ int CLI::EXPORT_PCB_STEP_COMMAND::Perform( KIWAY& aKiway ) step->m_useDrillOrigin = m_argParser.get( ARG_DRILL_ORIGIN ); step->m_useGridOrigin = m_argParser.get( ARG_GRID_ORIGIN ); - step->m_includeVirtual = !m_argParser.get( ARG_NO_VIRTUAL ); + step->m_includeExcludedBom = !m_argParser.get( ARG_NO_VIRTUAL ); step->m_substModels = m_argParser.get( ARG_SUBST_MODELS ); step->m_overwrite = m_argParser.get( ARG_FORCE ); step->m_filename = FROM_UTF8( m_argParser.get( ARG_INPUT ).c_str() ); step->m_outputFile = FROM_UTF8( m_argParser.get( ARG_OUTPUT ).c_str() ); + step->m_boardOnly = m_argParser.get( ARG_BOARD_ONLY ); wxString userOrigin = FROM_UTF8( m_argParser.get( ARG_USER_ORIGIN ).c_str() ); if( !userOrigin.IsEmpty() ) diff --git a/libs/kimath/include/math/vector3.h b/libs/kimath/include/math/vector3.h index 099e1a1df1..45be677b2f 100644 --- a/libs/kimath/include/math/vector3.h +++ b/libs/kimath/include/math/vector3.h @@ -102,6 +102,9 @@ public: ///< Not equality operator bool operator!=( const VECTOR3& aVector ) const; + + VECTOR3& operator*=( T val ); + VECTOR3& operator/=( T val ); }; @@ -175,6 +178,28 @@ bool VECTOR3::operator!=( VECTOR3 const& aVector ) const } +template +VECTOR3& VECTOR3::operator*=( T aScalar ) +{ + x = x * aScalar; + y = y * aScalar; + z = z * aScalar; + + return *this; +} + + +template +VECTOR3& VECTOR3::operator/=( T aScalar ) +{ + x = x / aScalar; + y = y / aScalar; + z = z / aScalar; + + return *this; +} + + /* Default specializations */ typedef VECTOR3 VECTOR3D; typedef VECTOR3 VECTOR3I; diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index a069305cfa..b75f8dd62d 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -13,7 +13,6 @@ add_definitions( -DPCBNEW ) add_subdirectory(connectivity) add_subdirectory(router) -add_subdirectory(exporters/step) # psnrouter depends on make_lexer outputs in common (bug #1285878 ) add_dependencies( pnsrouter pcbcommon ) @@ -223,6 +222,8 @@ set( PCBNEW_EXPORTERS exporters/export_footprint_associations.cpp exporters/export_gencad.cpp exporters/export_idf.cpp + exporters/step/exporter_step.cpp + exporters/step/step_pcb_model.cpp exporters/exporter_vrml.cpp exporters/place_file_exporter.cpp exporters/gen_drill_report_files.cpp @@ -680,7 +681,8 @@ target_link_libraries( pcbnew_kiface_objects tinyspline_lib nlohmann_json rectpack2d - kicad2step_lib + gzip-hpp + ${OCC_LIBRARIES} ) target_include_directories( pcbnew_kiface_objects PRIVATE diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index ab33453962..73d1b25614 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -47,9 +47,6 @@ #include -#include - - class DIALOG_EXPORT_STEP : public DIALOG_EXPORT_STEP_BASE { public: diff --git a/pcbnew/exporters/step/CMakeLists.txt b/pcbnew/exporters/step/CMakeLists.txt deleted file mode 100644 index 8d01797291..0000000000 --- a/pcbnew/exporters/step/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -include_directories( SYSTEM - ${OCE_INCLUDE_DIRS} - ${OCC_INCLUDE_DIR} -) - -set( KS2_LIB_FILES - kicad2step.cpp - - pcb/base.cpp - pcb/kicadmodel.cpp - pcb/kicadfootprint.cpp - pcb/kicadpad.cpp - pcb/kicadpcb.cpp - pcb/kicadcurve.cpp - pcb/oce_utils.cpp -) - -# Break the library out for re-use by both kicad2step and any qa that needs it -# In future, this could move for re-use by other programs needing s-expr support (?) -add_library( kicad2step_lib STATIC - ${KS2_LIB_FILES} -) - -target_include_directories( kicad2step_lib PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/include # for core - ${Boost_INCLUDE_DIR} # see commit 03bce554 - ${CMAKE_SOURCE_DIR}/libs/kimath/include - ${INC_AFTER} -) - -target_link_libraries( kicad2step_lib - sexpr - common - ${wxWidgets_LIBRARIES} - ${OCC_LIBRARIES} - ${ZLIB_LIBRARIES} - kimath -) - - -target_include_directories( kicad2step_lib PRIVATE - $ - ) \ No newline at end of file diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp new file mode 100644 index 0000000000..0cc372f955 --- /dev/null +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -0,0 +1,361 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 2016 Cirilo Bernardo + * Copyright (C) 2016-2021 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 "exporter_step.h" +#include +#include +#include +#include + +#include +#include +#include + +#include // OpenCascade messenger +#include // OpenCascade output messenger +#include // In open cascade + +#include + +#include + +#define DEFAULT_BOARD_THICKNESS 1.6 + +void ReportMessage( const wxString& aMessage ) +{ + wxPrintf( aMessage ); + fflush( stdout ); // Force immediate printing (needed on mingw) +} + +class KiCadPrinter : public Message_Printer +{ +public: + KiCadPrinter( EXPORTER_STEP* aConverter ) : m_converter( aConverter ) {} + +protected: +#if OCC_VERSION_HEX < OCC_VERSION_MIN + virtual void Send( const TCollection_ExtendedString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEol ) const override + { + Send( TCollection_AsciiString( theString ), theGravity, theToPutEol ); + } + + virtual void Send( const TCollection_AsciiString& theString, + const Message_Gravity theGravity, + const Standard_Boolean theToPutEol ) const override +#else + virtual void send( const TCollection_AsciiString& theString, + const Message_Gravity theGravity ) const override +#endif + { + if( theGravity >= Message_Info ) + { + ReportMessage( theString.ToCString() ); + +#if OCC_VERSION_HEX < OCC_VERSION_MIN + if( theToPutEol ) + ReportMessage( wxT( "\n" ) ); +#else + ReportMessage( wxT( "\n" ) ); +#endif + } + + if( theGravity >= Message_Alarm ) + m_converter->SetError(); + + if( theGravity == Message_Fail ) + m_converter->SetFail(); + } + +private: + EXPORTER_STEP* m_converter; +}; + + +EXPORTER_STEP::EXPORTER_STEP( BOARD* aBoard, const EXPORTER_STEP_PARAMS& aParams ) : + m_params( aParams ), + m_error( false ), + m_fail( false ), + m_hasDrillOrigin( false ), + m_hasGridOrigin( false ), + m_board( aBoard ), + m_pcbModel( nullptr ), + m_pcbName(), + m_minDistance( STEPEXPORT_MIN_DISTANCE ), + m_boardThickness( DEFAULT_BOARD_THICKNESS ) +{ + m_solderMaskColor = COLOR4D( 0.08, 0.20, 0.14, 0.83 ); + + m_resolver = std::make_unique(); + m_resolver->Set3DConfigDir( wxT( "" ) ); + m_resolver->SetProgramBase( &Pgm() ); +} + + +EXPORTER_STEP::~EXPORTER_STEP() +{ +} + + +bool EXPORTER_STEP::composePCB( FOOTPRINT* aFootprint, VECTOR2D aOrigin ) +{ + bool hasdata = false; + + if( ( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) && !m_params.m_includeExcludedBom ) + { + return hasdata; + } + + // Prefetch the library for this footprint + // In case we need to resolve relative footprint paths + wxString libraryName = aFootprint->GetFPID().GetLibNickname(); + wxString footprintBasePath = wxEmptyString; + + double posX = aFootprint->GetPosition().x - aOrigin.x; + double posY = (aFootprint->GetPosition().y) - aOrigin.y; + + if( m_board->GetProject() ) + { + try + { + // FindRow() can throw an exception + const FP_LIB_TABLE_ROW* fpRow = + m_board->GetProject()->PcbFootprintLibs()->FindRow( + libraryName, false ); + + if( fpRow ) + footprintBasePath = fpRow->GetFullURI( true ); + } + catch( ... ) + { + // Do nothing if the libraryName is not found in lib table + } + } + + // Dump the pad holes into the PCB + for( PAD* pad : aFootprint->Pads() ) + { + if( m_pcbModel->AddPadHole( pad ) ) + hasdata = true; + } + + // Exit early if we don't want to include footprint models + if( m_params.m_boardOnly ) + { + return hasdata; + } + + VECTOR2D newpos( pcbIUScale.IUTomm( posX ), pcbIUScale.IUTomm( posY ) ); + + for( const FP_3DMODEL& fp_model : aFootprint->Models() ) + { + if( !fp_model.m_Show || fp_model.m_Filename.empty() ) + + continue; + + std::vector searchedPaths; + wxString mname = m_resolver->ResolvePath( fp_model.m_Filename, wxEmptyString ); + + + if( !wxFileName::FileExists( mname ) ) + { + ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n" + "File not found: %s\n" ), + aFootprint->GetReference(), mname ) ); + continue; + } + + std::string fname( mname.ToUTF8() ); + std::string refName( aFootprint->GetReference().ToUTF8() ); + try + { + bool bottomSide = aFootprint->GetLayer() == B_Cu; + + // the rotation is stored in degrees but opencascade wants radians + VECTOR3D modelRot = fp_model.m_Rotation; + modelRot *= M_PI; + modelRot /= 180.0; + + if( m_pcbModel->AddComponent( fname, refName, bottomSide, + newpos, + aFootprint->GetOrientation().AsRadians(), + fp_model.m_Offset, modelRot, + fp_model.m_Scale, m_params.m_substModels ) ) + { + hasdata = true; + } + } + catch( const Standard_Failure& e ) + { + ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n" + "OpenCASCADE error: %s\n" ), + aFootprint->GetReference(), e.GetMessageString() ) ); + } + + } + + return hasdata; +} + + +bool EXPORTER_STEP::composePCB() +{ + if( m_pcbModel ) + return true; + + SHAPE_POLY_SET pcbOutlines; // stores the board main outlines + + if( !m_board->GetBoardPolygonOutlines( pcbOutlines ) ) + { + wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) ); + } + + VECTOR2D origin; + + // Determine the coordinate system reference: + // Precedence of reference point is Drill Origin > Grid Origin > User Offset + if( m_params.m_useDrillOrigin ) + origin = m_board->GetDesignSettings().GetAuxOrigin(); + else if( m_params.m_useGridOrigin ) + origin = m_board->GetDesignSettings().GetGridOrigin(); + else + origin = m_params.m_origin; + + m_pcbModel = std::make_unique( m_pcbName ); + + // TODO: Handle when top & bottom soldermask colours are different... + m_pcbModel->SetBoardColor( m_solderMaskColor.r, m_solderMaskColor.g, m_solderMaskColor.b ); + + m_pcbModel->SetPCBThickness( m_boardThickness ); + m_pcbModel->SetMinDistance( + std::max( m_params.m_minDistance, STEPEXPORT_MIN_ACCEPTABLE_DISTANCE ) ); + + m_pcbModel->SetMaxError( m_board->GetDesignSettings().m_MaxError ); + + for( FOOTPRINT* i : m_board->Footprints() ) + composePCB( i, origin ); + + ReportMessage( wxT( "Create PCB solid model\n" ) ); + + if( !m_pcbModel->CreatePCB( pcbOutlines ) ) + { + ReportMessage( wxT( "could not create PCB solid model\n" ) ); + return false; + } + + return true; +} + + +void EXPORTER_STEP::determinePcbThickness() +{ + m_boardThickness = DEFAULT_BOARD_THICKNESS; + + const BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + if( bds.GetStackupDescriptor().GetCount() ) + { + int thickness = 0; + + for( BOARD_STACKUP_ITEM* item : bds.GetStackupDescriptor().GetList() ) + { + switch( item->GetType() ) + { + case BS_ITEM_TYPE_DIELECTRIC: thickness += item->GetThickness(); break; + + case BS_ITEM_TYPE_COPPER: + if( item->IsEnabled() ) + thickness += item->GetThickness(); + + default: break; + } + } + + m_boardThickness = pcbIUScale.IUTomm( thickness ); + } +} + + +bool EXPORTER_STEP::Export() +{ + // setup opencascade message log + Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) ); + Message::DefaultMessenger()->AddPrinter( new KiCadPrinter( this ) ); + + ReportMessage( _( "Determining PCB data\n" ) ); + determinePcbThickness(); + + try + { + ReportMessage( _( "Build STEP data\n" ) ); + if( !composePCB() ) + { + ReportMessage( _( "\n** Error building STEP board model. Export aborted. **\n" ) ); + return false; + } + + ReportMessage( _( "Writing STEP file\n" ) ); + if( !m_pcbModel->WriteSTEP( m_outputFile ) ) + { + ReportMessage( _( "\n** Error writing STEP file. **\n" ) ); + return false; + } + else + { + ReportMessage( wxString::Format( _( "\nSTEP file '%s' created.\n" ), m_outputFile ) ); + } + } + catch( const Standard_Failure& e ) + { + ReportMessage( e.GetMessageString() ); + ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) ); + return false; + } + catch( ... ) + { + ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) ); + return false; + } + + if( m_fail || m_error ) + { + wxString msg; + + if( m_fail ) + { + msg = _( "Unable to create STEP file.\n" + "Check that the board has a valid outline and models." ); + } + else if( m_error ) + { + msg = _( "STEP file has been created, but there are warnings." ); + } + + ReportMessage( msg ); + } + + return true; +} \ No newline at end of file diff --git a/pcbnew/exporters/step/exporter_step.h b/pcbnew/exporters/step/exporter_step.h new file mode 100644 index 0000000000..0047d68abf --- /dev/null +++ b/pcbnew/exporters/step/exporter_step.h @@ -0,0 +1,103 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mark Roszko + * Copyright (C) 2016 Cirilo Bernardo + * Copyright (C) 2016-2021 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 EXPORTER_STEP_H +#define EXPORTER_STEP_H + +#include "step_pcb_model.h" +#include +#include + +class PCBMODEL; +class BOARD; +class FOOTPRINT; +class FILENAME_RESOLVER; + +class EXPORTER_STEP_PARAMS +{ +public: + EXPORTER_STEP_PARAMS() : + m_origin(), + m_overwrite( false ), + m_useGridOrigin( false ), + m_useDrillOrigin( false ), + m_includeExcludedBom( true ), + m_substModels( true ), + m_boardOnly( false ), + m_minDistance( 0.00001 ) {}; + + wxString m_outputFile; + + VECTOR2D m_origin; + + bool m_overwrite; + bool m_useGridOrigin; + bool m_useDrillOrigin; + bool m_includeExcludedBom; + bool m_substModels; + double m_minDistance; + + bool m_boardOnly; +}; + +class EXPORTER_STEP +{ +public: + EXPORTER_STEP( BOARD* aBoard, const EXPORTER_STEP_PARAMS& aParams ); + ~EXPORTER_STEP(); + + bool Export(); + + wxString m_outputFile; + + void SetError() { m_error = true; } + void SetFail() { m_error = true; } + +private: + bool composePCB(); + bool composePCB( FOOTPRINT* aFootprint, VECTOR2D aOrigin ); + void determinePcbThickness(); + + EXPORTER_STEP_PARAMS m_params; + std::unique_ptr m_resolver; + + bool m_error; + bool m_fail; + + bool m_hasDrillOrigin; + bool m_hasGridOrigin; + BOARD* m_board; + std::unique_ptr m_pcbModel; + wxString m_pcbName; + + // minimum distance between points to treat them as separate entities (mm) + double m_minDistance; + + double m_boardThickness; + + KIGFX::COLOR4D m_solderMaskColor; +}; + +#endif \ No newline at end of file diff --git a/pcbnew/exporters/step/kicad2step.cpp b/pcbnew/exporters/step/kicad2step.cpp deleted file mode 100644 index 2520f38436..0000000000 --- a/pcbnew/exporters/step/kicad2step.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/* - * This program source code file is part of kicad2mcad - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2016-2022 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "kicad2step.h" -#include "pcb/kicadpcb.h" -#include // OpenCascade messenger -#include // OpenCascade output messenger -#include // In open cascade - -#include - -#include - -#define OCC_VERSION_MIN 0x070500 - -#if OCC_VERSION_HEX < OCC_VERSION_MIN -#include -#endif - - - -// Horrible hack until we decouple things more -static KICAD2STEP* k2sInstance = nullptr; - - -void ReportMessage( const wxString& aMessage ) -{ - if( k2sInstance != nullptr ) - k2sInstance->ReportMessage( aMessage ); -} - - -class KiCadPrinter : public Message_Printer -{ -public: - KiCadPrinter( KICAD2STEP* aConverter ) : m_converter( aConverter ) {} - -protected: -#if OCC_VERSION_HEX < OCC_VERSION_MIN - virtual void Send( const TCollection_ExtendedString& theString, - const Message_Gravity theGravity, - const Standard_Boolean theToPutEol ) const override - { - Send( TCollection_AsciiString( theString ), theGravity, theToPutEol ); - } - - virtual void Send( const TCollection_AsciiString& theString, - const Message_Gravity theGravity, - const Standard_Boolean theToPutEol ) const override -#else - virtual void send( const TCollection_AsciiString& theString, - const Message_Gravity theGravity ) const override -#endif - { - if( theGravity >= Message_Info ) - { - m_converter->ReportMessage( theString.ToCString() ); - -#if OCC_VERSION_HEX < OCC_VERSION_MIN - if( theToPutEol ) - ReportMessage( wxT( "\n" ) ); -#else - m_converter->ReportMessage( wxT( "\n" ) ); -#endif - } - - if( theGravity >= Message_Alarm ) - m_converter->SetError(); - - if( theGravity == Message_Fail ) - m_converter->SetFail(); - } - -private: - KICAD2STEP* m_converter; -}; - - -KICAD2MCAD_PRMS::KICAD2MCAD_PRMS() -{ -#ifdef SUPPORTS_IGES - m_fmtIGES = false; -#endif - m_overwrite = false; - m_useGridOrigin = false; - m_useDrillOrigin = false; - m_includeVirtual = true; - m_substModels = false; - m_xOrigin = 0.0; - m_yOrigin = 0.0; - m_minDistance = MIN_DISTANCE; - -} - - -wxString KICAD2MCAD_PRMS::getOutputExt() const -{ -#ifdef SUPPORTS_IGES - if( m_fmtIGES ) - return wxT( "igs" ); - else -#endif - return wxT( "step" ); -} - - -KICAD2STEP::KICAD2STEP( KICAD2MCAD_PRMS aParams ) : - m_params( aParams ), m_error( false ), m_fail( false ) -{ -} - - -int KICAD2STEP::DoRun() -{ - wxFileName fname( m_params.m_filename ); - - if( !fname.FileExists() ) - { - ReportMessage( wxString::Format( _( "No such file: %s" ), m_params.m_filename ) ); - - return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; - } - - wxFileName out_fname; - - if( m_params.m_outputFile.empty() ) - { - out_fname.Assign( fname.GetFullPath() ); - out_fname.SetExt( m_params.getOutputExt() ); - } - else - { - out_fname.Assign( m_params.m_outputFile ); - - // Set the file extension if the user's requested file name does not have an extension. - if( !out_fname.HasExt() ) - out_fname.SetExt( m_params.getOutputExt() ); - } - - if( out_fname.FileExists() && !m_params.m_overwrite ) - { - ReportMessage( _( "** Output already exists. Export aborted. **\n" - "Enable the force overwrite flag to overwrite it." ) ); - - return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT; - } - - LOCALE_IO dummy; - - wxString outfile = out_fname.GetFullPath(); - KICADPCB pcb( fname.GetName() ); - - pcb.SetOrigin( m_params.m_xOrigin, m_params.m_yOrigin ); - // Set the min dist in mm to consider 2 points at the same place - // This is also the tolerance to consider 2 lines or arcs are connected - // A min value (0.001mm) is needed to have closed board outlines - // 0.01 mm is a good value - pcb.SetMinDistance( std::max( m_params.m_minDistance, MIN_ACCEPTABLE_DISTANCE ) ); - ReportMessage( wxString::Format( _( "Read file: '%s'\n" ), m_params.m_filename ) ); - - Message::DefaultMessenger()->RemovePrinters( STANDARD_TYPE( Message_PrinterOStream ) ); - Message::DefaultMessenger()->AddPrinter( new KiCadPrinter( this ) ); - - if( pcb.ReadFile( m_params.m_filename ) ) - { - if( m_params.m_useDrillOrigin ) - pcb.UseDrillOrigin( true ); - - if( m_params.m_useGridOrigin ) - pcb.UseGridOrigin( true ); - - bool res; - - try - { - ReportMessage( _( "Build STEP data\n" ) ); - - res = pcb.ComposePCB( m_params.m_includeVirtual, m_params.m_substModels ); - - if( !res ) - { - ReportMessage( _( "\n** Error building STEP board model. Export aborted. **\n" ) ); - - return CLI::EXIT_CODES::ERR_UNKNOWN; - } - - ReportMessage( _( "Write STEP file\n" ) ); - -#ifdef SUPPORTS_IGES - if( m_fmtIGES ) - res = pcb.WriteIGES( outfile ); - else -#endif - res = pcb.WriteSTEP( outfile ); - - if( !res ) - { - ReportMessage( _( "\n** Error writing STEP file. **\n" ) ); - return CLI::EXIT_CODES::ERR_UNKNOWN; - } - else - { - ReportMessage( wxString::Format( _( "\nSTEP file '%s' created.\n" ), outfile ) ); - } - } - catch( const Standard_Failure& e ) - { - ReportMessage( e.GetMessageString() ); - ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) ); - return CLI::EXIT_CODES::ERR_UNKNOWN; - } - catch( ... ) - { - ReportMessage( _( "\n** Error exporting STEP file. Export aborted. **\n" ) ); - return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; - } - } - else - { - ReportMessage( _( "\n** Error reading kicad_pcb file. **\n" ) ); - return CLI::EXIT_CODES::ERR_UNKNOWN; - } - - wxString msg; - - if( m_fail ) - { - msg = _( "Unable to create STEP file.\n" - "Check that the board has a valid outline and models." ); - } - else if( m_error ) - { - msg = _( "STEP file has been created, but there are warnings." ); - } - - ReportMessage( msg ); - - return CLI::EXIT_CODES::SUCCESS; -} - - -int KICAD2STEP::Run() -{ - k2sInstance = this; - - int diag = DoRun(); - return diag; -} - - -void KICAD2STEP::ReportMessage( const wxString& aMessage ) -{ - wxPrintf( aMessage ); - fflush( stdout ); // Force immediate printing (needed on mingw) -} diff --git a/pcbnew/exporters/step/kicad2step.h b/pcbnew/exporters/step/kicad2step.h deleted file mode 100644 index cca9d2aba8..0000000000 --- a/pcbnew/exporters/step/kicad2step.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This program source code file is part of kicad2mcad - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2016-2021 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 KICAD2STEP_H -#define KICAD2STEP_H - -#include -#include - -class APIEXPORT KICAD2MCAD_PRMS // A small class to handle parameters of conversion -{ -public: - KICAD2MCAD_PRMS(); - - ///< Return file extension for the selected output format - wxString getOutputExt() const; - -#ifdef SUPPORTS_IGES - bool m_fmtIGES; -#endif - bool m_overwrite; - bool m_useGridOrigin; - bool m_useDrillOrigin; - bool m_includeVirtual; - bool m_substModels; - wxString m_filename; - wxString m_outputFile; - double m_xOrigin; - double m_yOrigin; - double m_minDistance; -}; - -class APIEXPORT KICAD2STEP -{ -public: - KICAD2STEP( KICAD2MCAD_PRMS aParams ); - - int Run(); - void ReportMessage( const wxString& aMessage ); - - void SetError() { m_error = true; } - void SetFail() { m_error = true; } - -private: - int DoRun(); - - KICAD2MCAD_PRMS m_params; - - bool m_error; - bool m_fail; - - friend class KICAD2STEP_FRAME; -}; - -#endif \ No newline at end of file diff --git a/pcbnew/exporters/step/pcb/base.cpp b/pcbnew/exporters/step/pcb/base.cpp deleted file mode 100644 index 71267a68d5..0000000000 --- a/pcbnew/exporters/step/pcb/base.cpp +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 -#include -#include -#include -#include "sexpr/sexpr.h" -#include "base.h" - -static const char bad_position[] = "* corrupt module in PCB file; invalid position"; - - -std::ostream& operator<<( std::ostream& aStream, const DOUBLET& aDoublet ) -{ - aStream << aDoublet.x << "," << aDoublet.y; - return aStream; -} - - -std::ostream& operator<<( std::ostream& aStream, const TRIPLET& aTriplet ) -{ - aStream << aTriplet.x << "," << aTriplet.y << "," << aTriplet.z; - return aStream; -} - - -bool Get2DPositionAndRotation( const SEXPR::SEXPR* data, DOUBLET& aPosition, double& aRotation ) -{ - // form: (at X Y {rot}) - int nchild = data->GetNumberOfChildren(); - - if( nchild < 3 ) - { - std::ostringstream ostr; - ostr << bad_position << " (line " << data->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - if( data->GetChild( 0 )->GetSymbol() != "at" ) - { - std::ostringstream ostr; - ostr << "* SEXPR item is not a position string (line "; - ostr << data->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child = data->GetChild( 1 ); - double x; - - if( child->IsDouble() ) - { - x = child->GetDouble(); - } - else if( child->IsInteger() ) - { - x = (double) child->GetInteger(); - } - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - child = data->GetChild( 2 ); - double y; - - if( child->IsDouble() ) - { - y = child->GetDouble(); - } - else if( child->IsInteger() ) - { - y = (double) child->GetInteger(); - } - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - aPosition.x = x; - aPosition.y = y; - - if( nchild == 3 ) - return true; - - child = data->GetChild( 3 ); - double angle = 0.0; - - if( child->IsDouble() ) - { - angle = child->GetDouble(); - } - else if( child->IsInteger() ) - { - angle = (double) child->GetInteger(); - } - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - while( angle >= 360.0 ) - angle -= 360.0; - - while( angle <= -360.0 ) - angle += 360.0; - - aRotation = (angle / 180.0) * M_PI; - - return true; -} - - -bool Get2DCoordinate( const SEXPR::SEXPR* data, DOUBLET& aCoordinate ) -{ - // form: (at X Y {rot}) - int nchild = data->GetNumberOfChildren(); - - if( nchild < 3 ) - { - std::ostringstream ostr; - ostr << bad_position << " (line " << data->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child = data->GetChild( 1 ); - double x; - - if( child->IsDouble() ) - { - x = child->GetDouble(); - } - else if( child->IsInteger() ) - { - x = (double) child->GetInteger(); - } - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - child = data->GetChild( 2 ); - double y; - - if( child->IsDouble() ) - { - y = child->GetDouble(); - } - else if( child->IsInteger() ) - { - y = (double) child->GetInteger(); - } - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - aCoordinate.x = x; - aCoordinate.y = y; - - return true; -} - - -bool Get3DCoordinate( const SEXPR::SEXPR* data, TRIPLET& aCoordinate ) -{ - // form: (at X Y Z) - int nchild = data->GetNumberOfChildren(); - - if( nchild < 4 ) - { - std::ostringstream ostr; - ostr << bad_position << " (line " << data->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child; - double val[3]; - - for( int i = 1; i < 4; ++i ) - { - child = data->GetChild( i ); - - if( child->IsDouble() ) - val[i -1] = child->GetDouble(); - else if( child->IsInteger() ) - val[i -1] = (double) child->GetInteger(); - else - { - std::ostringstream ostr; - ostr << bad_position << " (line " << child->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - } - - aCoordinate.x = val[0]; - aCoordinate.y = val[1]; - aCoordinate.z = val[2]; - - return true; -} - - -bool GetXYZRotation( const SEXPR::SEXPR* data, TRIPLET& aRotation ) -{ - const char bad_rotation[] = "* invalid 3D rotation"; - - if( !Get3DCoordinate( data, aRotation ) ) - { - std::ostringstream ostr; - ostr << bad_rotation << " (line " << data->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - if( aRotation.x > 360.0 || aRotation.x < -360.0 ) - { - int nr = (int) aRotation.x / 360; - aRotation.x -= ( 360.0 * nr ); - } - - if( aRotation.y > 360.0 || aRotation.y < -360.0 ) - { - int nr = (int) aRotation.y / 360; - aRotation.y -= ( 360.0 * nr ); - } - - if( aRotation.z > 360.0 || aRotation.z < -360.0 ) - { - int nr = (int) aRotation.z / 360; - aRotation.z -= ( 360.0 * nr ); - } - - aRotation.x *= M_PI / 180.0; - aRotation.y *= M_PI / 180.0; - aRotation.z *= M_PI / 180.0; - - return true; -} - - -std::optional GetLayerName( const SEXPR::SEXPR& aLayerElem ) -{ - std::optional layer; - - if( aLayerElem.GetNumberOfChildren() == 2 ) - { - const SEXPR::SEXPR& layerChild = *aLayerElem.GetChild( 1 ); - - // The layer child can be quoted (string) or unquoted (symbol) depending on PCB version. - if( layerChild.IsString() ) - layer = layerChild.GetString(); - else if( layerChild.IsSymbol() ) - layer = layerChild.GetSymbol(); - } - - return layer; -} \ No newline at end of file diff --git a/pcbnew/exporters/step/pcb/base.h b/pcbnew/exporters/step/pcb/base.h deleted file mode 100644 index fd20c64436..0000000000 --- a/pcbnew/exporters/step/pcb/base.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 - */ - -/** - * @file base.h - * Provide declarations of items which are basic to all kicad2mcad code. - */ - -#ifndef KICADBASE_H -#define KICADBASE_H - -#include - -#include - -///< Default minimum distance between points to treat them as separate ones (mm) -static constexpr double MIN_DISTANCE = 0.01; -static constexpr double MIN_ACCEPTABLE_DISTANCE = 0.001; - -namespace SEXPR -{ - class SEXPR; -} - -enum CURVE_TYPE -{ - CURVE_NONE = 0, // invalid curve - CURVE_LINE, - CURVE_POLYGON, - CURVE_ARC, - CURVE_CIRCLE, - CURVE_BEZIER -}; - - -/* - * Layers of importance to MCAD export: - * - LAYER_TOP: specifies that a footprint is on the top of the PCB - * - LAYER_BOTTOM: specifies that a footprint is on the bottom of the PCB - * - LAYER_EDGE: specifies that a Curve is associated with the PCB edge - */ -enum LAYERS -{ - LAYER_NONE = 0, // no layer specified (bad object) - LAYER_TOP, // top side - LAYER_BOTTOM, // bottom side - LAYER_EDGE // edge data -}; - - -struct DOUBLET -{ - double x; - double y; - - DOUBLET() : - x( 0.0 ), - y( 0.0 ) - { } - - DOUBLET( double aX, double aY ) : - x( aX ), - y( aY ) - { } -}; - - -std::ostream& operator<<( std::ostream& aStream, const DOUBLET& aDoublet ); - - -struct TRIPLET -{ - double x; - double y; - - union - { - double z; - double angle; - }; - - TRIPLET() : - x( 0.0 ), - y( 0.0 ), - z( 0.0 ) - { } - - TRIPLET( double aX, double aY, double aZ ) : - x( aX ), - y( aY ), - z( aZ ) - { } -}; - - -std::ostream& operator<<( std::ostream& aStream, const TRIPLET& aTriplet ); - - -bool Get2DPositionAndRotation( const SEXPR::SEXPR* data, DOUBLET& aPosition, double& aRotation ); -bool Get2DCoordinate( const SEXPR::SEXPR* data, DOUBLET& aCoordinate ); -bool Get3DCoordinate( const SEXPR::SEXPR* data, TRIPLET& aCoordinate ); -bool GetXYZRotation( const SEXPR::SEXPR* data, TRIPLET& aRotation ); - -/** - * Get the layer name from a layer element, if the layer is syntactically valid. - * - * E.g. (layer "Edge.Cuts") -> "Edge.Cuts" - * - * @param aLayerElem the s-expr element to get the name from. - * @return the layer name if valid, else empty. - */ -std::optional GetLayerName( const SEXPR::SEXPR& aLayerElem ); - -#endif // KICADBASE_H diff --git a/pcbnew/exporters/step/pcb/kicadcurve.cpp b/pcbnew/exporters/step/pcb/kicadcurve.cpp deleted file mode 100644 index 9eb4ed1e68..0000000000 --- a/pcbnew/exporters/step/pcb/kicadcurve.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2022 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 "kicadcurve.h" - -#include - -#include - -#include -#include -#include -#include <../../../libs/kimath/include/geometry/shape_arc.h> -#include -#include - - -KICADCURVE::KICADCURVE() -{ - m_form = CURVE_NONE; - m_angle = 0.0; - m_radius = 0.0; - m_layer = LAYER_NONE; - m_startangle = 0.0; - m_endangle = 0.0; - m_arcHasMiddlePoint = false; - - return; -} - - -KICADCURVE::~KICADCURVE() -{ - return; -} - - -bool KICADCURVE::Read( SEXPR::SEXPR* aEntry, CURVE_TYPE aCurveType ) -{ - if( CURVE_LINE != aCurveType && CURVE_ARC != aCurveType - && CURVE_CIRCLE != aCurveType && CURVE_BEZIER != aCurveType - && CURVE_POLYGON != aCurveType ) - { - wxLogMessage( wxT( "* Unsupported curve type: %d\n" ), aCurveType ); - return false; - } - - m_form = aCurveType; - - int nchild = aEntry->GetNumberOfChildren(); - - if( ( CURVE_CIRCLE == aCurveType && nchild < 5 ) - || ( CURVE_ARC == aCurveType && nchild < 6 ) - || ( CURVE_LINE == aCurveType && nchild < 5 ) - || ( CURVE_BEZIER == aCurveType && nchild < 5 ) - || ( CURVE_POLYGON == aCurveType && nchild < 5 ) ) - { - wxLogMessage( wxT( "* bad curve data; not enough parameters\n" ) ); - return false; - } - - SEXPR::SEXPR* child; - std::string text; - - for( int i = 1; i < nchild; ++i ) - { - child = aEntry->GetChild( i ); - - if( !child->IsList() ) - continue; - - text = child->GetChild( 0 )->GetSymbol(); - - if( text == "pts" ) - { - // Parse and extract the list of xy coordinates - SEXPR::PARSER parser; - std::unique_ptr prms = parser.Parse( child->AsString() ); - - // We need 4 XY parameters (and "pts" that is the first parameter) - if( ( aCurveType == CURVE_BEZIER && prms->GetNumberOfChildren() != 5 ) - || ( aCurveType == CURVE_POLYGON && prms->GetNumberOfChildren() < 4 ) ) - return false; - - // Extract xy coordinates from pts list - SEXPR::SEXPR_VECTOR const* list = prms->GetChildren(); - - // The first parameter is "pts", so skip it. - for( SEXPR::SEXPR_VECTOR::size_type ii = 1; ii < list->size(); ++ii ) - { - SEXPR::SEXPR* sub_child = ( *list )[ii]; - text = sub_child->GetChild( 0 )->GetSymbol(); - - // inside pts list, parameters are xy point coord - // or a arc (start, middle, end) points - if( text == "xy" ) - { - DOUBLET coord; - - if( !Get2DCoordinate( sub_child, coord ) ) - return false; - - if( aCurveType == CURVE_BEZIER ) - { - switch( ii ) - { - case 1: m_start = coord; break; - case 2: m_bezierctrl1 = coord; break; - case 3: m_bezierctrl2 = coord; break; - case 4: m_end = coord; break; - default: - break; - } - } - else - { - m_poly.push_back( coord ); - } - } - else if( text == "arc" ) - { - int arc_child = sub_child->GetNumberOfChildren(); - DOUBLET arc_start, arc_middle, arc_end; - - for( int jj = 1; jj < arc_child; jj++ ) - { - SEXPR::SEXPR* curr_child = sub_child->GetChild( jj ); - text = curr_child->GetChild( 0 )->GetSymbol(); - - if( text == "start" ) - { - if( !Get2DCoordinate( curr_child, arc_start ) ) - return false; - } - else if( text == "end" ) - { - if( !Get2DCoordinate( curr_child, arc_end ) ) - return false; - } - else if( text == "mid" ) - { - if( !Get2DCoordinate( curr_child, arc_middle ) ) - return false; - } - } - - // To convert arc edge to segments, we are using SHAPE_ARC, but SHAPE_ARC use - // integer coords. So to avoid truncation, use a scaling factor. - // 1e5 is enough. - const double scale = 1e5; - SHAPE_ARC new_arc( VECTOR2I( arc_start.x*scale, arc_start.y*scale ), - VECTOR2I( arc_middle.x*scale, arc_middle.y*scale ), - VECTOR2I( arc_end.x*scale, arc_end.y*scale ), 0 ); - - double accuracy = 0.005*scale; // Approx accuracy is 5 microns - SHAPE_LINE_CHAIN segs_from_arc = new_arc.ConvertToPolyline( accuracy ); - - // Add segments to STEP polygon - for( int ll = 0; ll < segs_from_arc.PointCount(); ll++ ) - m_poly.emplace_back( segs_from_arc.CPoint( ll ).x / scale, - segs_from_arc.CPoint( ll ).y / scale ); - } - } - } - else if( text == "start" || text == "center" ) - { - if( !Get2DCoordinate( child, m_start ) ) - return false; - } - else if( text == "end" ) - { - if( !Get2DCoordinate( child, m_end ) ) - return false; - } - else if( text == "mid" ) - { - if( !Get2DCoordinate( child, m_middle ) ) - return false; - - m_arcHasMiddlePoint = true; - } - else if( text == "angle" ) - { - if( child->GetNumberOfChildren() < 2 - || ( !child->GetChild( 1 )->IsDouble() && !child->GetChild( 1 )->IsInteger() ) ) - { - wxLogMessage( wxT( "* bad angle data\n" ) ); - return false; - } - - if( child->GetChild( 1 )->IsDouble() ) - m_angle = child->GetChild( 1 )->GetDouble(); - else - m_angle = child->GetChild( 1 )->GetInteger(); - - m_angle = m_angle / 180.0 * M_PI; - } - else if( text == "layer" ) - { - const std::optional layer = GetLayerName( *child ); - - if( !layer ) - { - std::ostringstream ostr; - ostr << "* bad layer data: " << child->AsString(); - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - // NOTE: for the moment we only process Edge.Cuts - if( *layer == "Edge.Cuts" ) - m_layer = LAYER_EDGE; - } - } - - // New arcs are defined by start middle and end points instead of center, - // start and arc angle - // So convert new params to old params - if( CURVE_ARC == aCurveType && m_arcHasMiddlePoint ) - { - // To calculate old params, we are using SHAPE_ARC, but SHAPE_ARC use - // integer coords. So to avoid truncation, use a scaling factor. - // 1e5 is enough. - const double scale = 1e5; - SHAPE_ARC new_arc( VECTOR2I( m_start.x*scale, m_start.y*scale ), - VECTOR2I( m_middle.x*scale, m_middle.y*scale ), - VECTOR2I( m_end.x*scale, m_end.y*scale ), 0 ); - - VECTOR2I center = new_arc.GetCenter(); - m_start.x = center.x/scale; - m_start.y = center.y/scale; - m_end.x = new_arc.GetP0().x / scale; - m_end.y = new_arc.GetP0().y / scale; - m_ep.x = new_arc.GetP1().x / scale; - m_ep.y = new_arc.GetP1().y / scale; - m_angle = new_arc.GetCentralAngle().AsRadians(); - } - - return true; -} - - -std::string KICADCURVE::Describe() const -{ - std::ostringstream desc; - - switch( m_form ) - { - case CURVE_LINE: - desc << "line start: " << m_start << " end: " << m_end; - break; - - case CURVE_ARC: - desc << "arc center: " << m_start << " radius: " << m_radius - << " angle: " << 180.0 * m_angle / M_PI - << " arc start: " << m_end << " arc end: " << m_ep; - break; - - case CURVE_CIRCLE: - desc << "circle center: " << m_start << " radius: " << m_radius; - break; - - case CURVE_BEZIER: - desc << "bezier start: " << m_start << " end: " << m_end - << " ctrl1: " << m_bezierctrl1 << " ctrl2: " << m_bezierctrl2 ; - break; - - default: - desc << ""; - break; - } - - return desc.str(); -} diff --git a/pcbnew/exporters/step/pcb/kicadcurve.h b/pcbnew/exporters/step/pcb/kicadcurve.h deleted file mode 100644 index e81aa431d1..0000000000 --- a/pcbnew/exporters/step/pcb/kicadcurve.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 - */ - -/** - * @file kicadcurve.h - * Declare the Curve (glyph) object. - */ - -#ifndef KICADCURVE_H -#define KICADCURVE_H - -#include -#include -#include "base.h" - - -class KICADCURVE -{ -public: - KICADCURVE(); - virtual ~KICADCURVE(); - - bool Read( SEXPR::SEXPR* aEntry, CURVE_TYPE aCurveType ); - - LAYERS GetLayer() - { - return m_layer; - } - - ///< Return human-readable description of the curve. - std::string Describe() const; - - CURVE_TYPE m_form; // form of curve: line, arc, circle - LAYERS m_layer; // layer of the glyph - DOUBLET m_start; // start point of line or center for arc and circle - DOUBLET m_end; // end point of line, first point on arc or circle - DOUBLET m_middle; // middle point on arc for recent files - DOUBLET m_ep; // actual endpoint, to be computed in the case of arcs - DOUBLET m_bezierctrl1; // for bezier curve only first control point - DOUBLET m_bezierctrl2; // for bezier curve only second control point - double m_radius; // radius; to be computed in the case of arcs and circles - double m_angle; // subtended angle of arc - double m_startangle; - double m_endangle; - bool m_arcHasMiddlePoint; // true if an arc id defined by 3 points - - std::vector m_poly; // vector of polygon points -}; - -#endif // KICADCURVE_H diff --git a/pcbnew/exporters/step/pcb/kicadfootprint.cpp b/pcbnew/exporters/step/pcb/kicadfootprint.cpp deleted file mode 100644 index ac596daf4a..0000000000 --- a/pcbnew/exporters/step/pcb/kicadfootprint.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright 2018-2022 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 "kicadfootprint.h" - -#include -#include "kicadcurve.h" -#include "kicadmodel.h" -#include "kicadpad.h" -#include "oce_utils.h" - -#include - -#include -#include - -#include -#include -#include -#include - -#include - - -KICADFOOTPRINT::KICADFOOTPRINT( KICADPCB* aParent ) -{ - m_parent = aParent; - m_side = LAYER_NONE; - m_rotation = 0.0; - m_smd = false; - m_tht = false; - m_virtual = false; - - return; -} - - -KICADFOOTPRINT::~KICADFOOTPRINT() -{ - for( KICADPAD* i : m_pads ) - delete i; - - for( KICADCURVE* i : m_curves ) - delete i; - - for( KICADMODEL* i : m_models ) - delete i; - - return; -} - - -bool KICADFOOTPRINT::Read( SEXPR::SEXPR* aEntry ) -{ - if( !aEntry ) - return false; - - if( aEntry->IsList() ) - { - size_t nc = aEntry->GetNumberOfChildren(); - SEXPR::SEXPR* child = aEntry->GetChild( 0 ); - std::string name = child->GetSymbol(); - - if( name != "module" && name != "footprint" ) - { - std::ostringstream ostr; - ostr << "* BUG: module parser invoked for type '" << name << "'\n"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - bool result = true; - - for( size_t i = 2; i < nc && result; ++i ) - { - child = aEntry->GetChild( i ); - std::string symname; - - // skip the optional locked and/or placed attributes; due to the vagaries of the - // KiCad version of sexpr, the attribute may be a Symbol or a String - if( child->IsSymbol() || child->IsString() ) - { - if( child->IsSymbol() ) - symname = child->GetSymbol(); - else if( child->IsString() ) - symname = child->GetString(); - - if( symname == "locked" || symname == "placed" ) - continue; - - wxLogMessage( wxT( "* module descr in PCB file at line %d: unexpected keyword " - "'%s'\n" ), - child->GetLineNumber(), - symname.c_str() ); - return false; - } - - if( !child->IsList() ) - { - wxLogMessage( wxT( "* corrupt module in PCB file at line %d\n" ), - child->GetLineNumber() ); - return false; - } - - symname = child->GetChild( 0 )->GetSymbol(); - - if( symname == "layer" ) - result = parseLayer( child ); - else if( symname == "at" ) - result = parsePosition( child ); - else if( symname == "attr" ) - result = parseAttribute( child ); - else if( symname == "fp_text" ) - result = parseText( child ); - else if( symname == "fp_arc" ) - result = parseCurve( child, CURVE_ARC ); - else if( symname == "fp_line" ) - result = parseCurve( child, CURVE_LINE ); - else if( symname == "fp_circle" ) - result = parseCurve( child, CURVE_CIRCLE ); - else if( symname == "fp_rect" ) - result = parseRect( child ); - else if( symname == "fp_curve" ) - result = parseCurve( child, CURVE_BEZIER ); - else if( symname == "pad" ) - result = parsePad( child ); - else if( symname == "model" ) - result = parseModel( child ); - } - - if( !m_smd && !m_tht ) - m_virtual = true; - - return result; - } - - std::ostringstream ostr; - ostr << "* data is not a valid PCB module\n"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - - return false; -} - - -bool KICADFOOTPRINT::parseRect( SEXPR::SEXPR* data ) -{ - std::unique_ptr rect = std::make_unique(); - - if( !rect->Read( data, CURVE_LINE ) ) - return false; - - // reject any curves not on the Edge.Cuts layer - if( rect->GetLayer() != LAYER_EDGE ) - return true; - - KICADCURVE* top = new KICADCURVE( *rect ); - KICADCURVE* right = new KICADCURVE( *rect ); - KICADCURVE* bottom = new KICADCURVE( *rect ); - KICADCURVE* left = new KICADCURVE( *rect ); - - top->m_end.y = right->m_start.y; - m_curves.push_back( top ); - - right->m_start.x = bottom->m_end.x; - m_curves.push_back( right ); - - bottom->m_start.y = left->m_end.y; - m_curves.push_back( bottom ); - - left->m_end.x = top->m_start.x; - m_curves.push_back( left ); - - return true; -} - - -bool KICADFOOTPRINT::parseModel( SEXPR::SEXPR* data ) -{ - KICADMODEL* mp = new KICADMODEL(); - - if( !mp->Read( data ) ) - { - delete mp; - return false; - } - - if( mp->Hide() ) - delete mp; - else - m_models.push_back( mp ); - - return true; -} - - -bool KICADFOOTPRINT::parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType ) -{ - KICADCURVE* mp = new KICADCURVE(); - - if( !mp->Read( data, aCurveType ) ) - { - delete mp; - return false; - } - - // NOTE: for now we are only interested in glyphs on the outline layer - if( LAYER_EDGE != mp->GetLayer() ) - { - delete mp; - return true; - } - - m_curves.push_back( mp ); - return true; -} - - -bool KICADFOOTPRINT::parseLayer( SEXPR::SEXPR* data ) -{ - SEXPR::SEXPR* val = data->GetChild( 1 ); - std::string layername; - - if( val->IsSymbol() ) - layername = val->GetSymbol(); - else if( val->IsString() ) - layername = val->GetString(); - else - { - std::ostringstream ostr; - ostr << "* corrupt module in PCB file (line "; - ostr << val->GetLineNumber() << "); layer cannot be parsed\n"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - int layerId = m_parent->GetLayerId( layername ); - - if( layerId == 31 ) - m_side = LAYER_BOTTOM; - else - m_side = LAYER_TOP; - - return true; -} - - -bool KICADFOOTPRINT::parsePosition( SEXPR::SEXPR* data ) -{ - return Get2DPositionAndRotation( data, m_position, m_rotation ); -} - - -bool KICADFOOTPRINT::parseAttribute( SEXPR::SEXPR* data ) -{ - if( data->GetNumberOfChildren() < 2 ) - { - std::ostringstream ostr; - ostr << "* corrupt module in PCB file (line "; - ostr << data->GetLineNumber() << "); attribute cannot be parsed\n"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child = data->GetChild( 1 ); - std::string text; - - if( child->IsSymbol() ) - text = child->GetSymbol(); - else if( child->IsString() ) - text = child->GetString(); - - // The "virtual" attribute token is obsolete but kicad2step can still be run against older - // board files. It has been replaced with "exclude_from_bom" so any footprint with this - // attribute will behave the same as the "virtual" attribute. - if( text == "smd" ) - m_smd = true; - else if( text == "through_hole" ) - m_tht = true; - else if( text == "virtual" ) - m_virtual = true; - else if( text == "exclude_from_bom" ) - m_virtual = true; - - return true; -} - - -bool KICADFOOTPRINT::parseText( SEXPR::SEXPR* data ) -{ - // we're only interested in the Reference Designator - if( data->GetNumberOfChildren() < 3 ) - return true; - - SEXPR::SEXPR* child = data->GetChild( 1 ); - std::string text; - - if( child->IsSymbol() ) - text = child->GetSymbol(); - else if( child->IsString() ) - text = child->GetString(); - - if( text != "reference" ) - return true; - - child = data->GetChild( 2 ); - - if( child->IsSymbol() ) - text = child->GetSymbol(); - else if( child->IsString() ) - text = child->GetString(); - - m_refdes = text; - return true; -} - - -bool KICADFOOTPRINT::parsePad( SEXPR::SEXPR* data ) -{ - KICADPAD* mp = new KICADPAD(); - - if( !mp->Read( data ) ) - { - delete mp; - return false; - } - - // NOTE: for now we only accept thru-hole pads - // for the MCAD description - if( !mp->IsThruHole() ) - { - delete mp; - return true; - } - - m_pads.push_back( mp ); - return true; -} - - -bool KICADFOOTPRINT::ComposePCB( class PCBMODEL* aPCB, FILENAME_RESOLVER* resolver, - DOUBLET aOrigin, bool aComposeVirtual, bool aSubstituteModels ) -{ - // translate pads and curves to final position and append to PCB. - double dlim = (double)std::numeric_limits< float >::epsilon(); - - double vsin = sin( m_rotation ); - double vcos = cos( m_rotation ); - bool hasdata = false; - - double posX = m_position.x - aOrigin.x; - double posY = m_position.y - aOrigin.y; - - for( KICADCURVE* i : m_curves ) - { - if( i->m_layer != LAYER_EDGE || CURVE_NONE == i->m_form ) - continue; - - KICADCURVE lcurve = *i; - - lcurve.m_start.y = -lcurve.m_start.y; - lcurve.m_end.y = -lcurve.m_end.y; - lcurve.m_angle = -lcurve.m_angle; - - if( m_rotation < -dlim || m_rotation > dlim ) - { - double x = lcurve.m_start.x * vcos - lcurve.m_start.y * vsin; - double y = lcurve.m_start.x * vsin + lcurve.m_start.y * vcos; - lcurve.m_start.x = x; - lcurve.m_start.y = y; - x = lcurve.m_end.x * vcos - lcurve.m_end.y * vsin; - y = lcurve.m_end.x * vsin + lcurve.m_end.y * vcos; - lcurve.m_end.x = x; - lcurve.m_end.y = y; - } - - lcurve.m_start.x += posX; - lcurve.m_start.y -= posY; - lcurve.m_end.x += posX; - lcurve.m_end.y -= posY; - - if( aPCB->AddOutlineSegment( &lcurve ) ) - hasdata = true; - } - - for( KICADPAD* i : m_pads ) - { - if( !i->IsThruHole() ) - continue; - - KICADPAD lpad = *i; - lpad.m_position.y = -lpad.m_position.y; - - if( m_rotation < -dlim || m_rotation > dlim ) - { - double x = lpad.m_position.x * vcos - lpad.m_position.y * vsin; - double y = lpad.m_position.x * vsin + lpad.m_position.y * vcos; - lpad.m_position.x = x; - lpad.m_position.y = y; - } - - lpad.m_position.x += posX; - lpad.m_position.y -= posY; - - if( aPCB->AddPadHole( &lpad ) ) - hasdata = true; - } - - if( m_virtual && !aComposeVirtual ) - return hasdata; - - DOUBLET newpos( posX, posY ); - - for( KICADMODEL* i : m_models ) - { - wxString mname = wxString::FromUTF8Unchecked( i->m_modelname.c_str() ); - - if( mname.empty() ) - continue; - - std::vector searchedPaths; - mname = resolver->ResolvePath( mname, wxEmptyString ); - - if( !wxFileName::FileExists( mname ) ) - { - ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n" - "File not found: %s\n" ), - m_refdes, - mname) ); - continue; - } - - std::string fname( mname.ToUTF8() ); - - try - { - if( aPCB->AddComponent( fname, m_refdes, LAYER_BOTTOM == m_side ? true : false, - newpos, m_rotation, i->m_offset, i->m_rotation, i->m_scale, - aSubstituteModels ) ) - { - hasdata = true; - } - } - catch( const Standard_Failure& e) - { - ReportMessage( wxString::Format( wxT( "Could not add 3D model to %s.\n" - "OpenCASCADE error: %s\n" ), - m_refdes, - e.GetMessageString() ) ); - } - } - - return hasdata; -} diff --git a/pcbnew/exporters/step/pcb/kicadfootprint.h b/pcbnew/exporters/step/pcb/kicadfootprint.h deleted file mode 100644 index c6618e68c1..0000000000 --- a/pcbnew/exporters/step/pcb/kicadfootprint.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 KICADFOOTPRINT_H -#define KICADFOOTPRINT_H - -#include "base.h" -#include "kicadpcb.h" - -#include -#include - -namespace SEXPR -{ - class SEXPR; -} - -class KICADPAD; -class KICADCURVE; -class KICADMODEL; -class PCBMODEL; -class FILENAME_RESOLVER; - -class KICADFOOTPRINT -{ -public: - KICADFOOTPRINT( KICADPCB* aParent ); - virtual ~KICADFOOTPRINT(); - - bool Read( SEXPR::SEXPR* aEntry ); - - bool ComposePCB( class PCBMODEL* aPCB, FILENAME_RESOLVER* resolver, - DOUBLET aOrigin, bool aComposeVirtual = true, bool aSubstituteModels = true ); - -private: - bool parseModel( SEXPR::SEXPR* data ); - bool parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType ); - bool parseLayer( SEXPR::SEXPR* data ); - bool parsePosition( SEXPR::SEXPR* data ); - bool parseAttribute( SEXPR::SEXPR* data ); - bool parseText( SEXPR::SEXPR* data ); - bool parsePad( SEXPR::SEXPR* data ); - bool parseRect( SEXPR::SEXPR* data ); - - KICADPCB* m_parent; // The parent KICADPCB, to know layer names - - LAYERS m_side; - std::string m_refdes; - DOUBLET m_position; - double m_rotation; // rotation (radians) - bool m_smd; // true for a SMD component - bool m_tht; // true for a through-hole component - bool m_virtual; // true for a virtual (usually mechanical) component - - std::vector< KICADPAD* > m_pads; - std::vector< KICADCURVE* > m_curves; - std::vector< KICADMODEL* > m_models; -}; - -#endif // KICADFOOTPRINT_H diff --git a/pcbnew/exporters/step/pcb/kicadmodel.cpp b/pcbnew/exporters/step/pcb/kicadmodel.cpp deleted file mode 100644 index d5f0f4914d..0000000000 --- a/pcbnew/exporters/step/pcb/kicadmodel.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2016-2022 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 "kicadmodel.h" - -#include - -#include -#include -#include - - -KICADMODEL::KICADMODEL() : - m_hide( false ), - m_scale( 1.0, 1.0, 1.0 ), - m_offset( 0.0, 0.0, 0.0 ), - m_rotation( 0.0, 0.0, 0.0 ) -{ -} - - -KICADMODEL::~KICADMODEL() -{ -} - - -bool KICADMODEL::Read( SEXPR::SEXPR* aEntry ) -{ - // form: ( pad N thru_hole shape (at x y {r}) (size x y) (drill {oval} x {y}) (layers X X X) ) - int nchild = aEntry->GetNumberOfChildren(); - - if( nchild < 2 ) - { - std::ostringstream ostr; - ostr << "* invalid model entry"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child = aEntry->GetChild( 1 ); - - if( child->IsSymbol() ) - { - m_modelname = child->GetSymbol(); - } - else if( child->IsString() ) - { - m_modelname = child->GetString(); - } - else - { - std::ostringstream ostr; - ostr << "* invalid model entry; invalid path"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - for( int i = 2; i < nchild; ++i ) - { - child = aEntry->GetChild( i ); - - if( child->IsSymbol() && child->GetSymbol() == "hide" ) - { - m_hide = true; - } - else if( child->IsList() ) - { - std::string name = child->GetChild( 0 )->GetSymbol(); - bool ret = true; - - // Version 4.x and prior used 'at' parameter, which was specified in inches. - if( name == "at" ) - { - ret = Get3DCoordinate( child->GetChild( 1 ), m_offset ); - - if( ret ) - { - m_offset.x *= 25.4f; - m_offset.y *= 25.4f; - m_offset.z *= 25.4f; - } - } - else if( name == "offset" ) - { - // From 5.x onwards, 3D model is provided in 'offset', which is in millimetres. - ret = Get3DCoordinate( child->GetChild( 1 ), m_offset ); - } - else if( name == "scale" ) - { - ret = Get3DCoordinate( child->GetChild( 1 ), m_scale ); - } - else if( name == "rotate" ) - { - ret = GetXYZRotation( child->GetChild( 1 ), m_rotation ); - } - - if( !ret ) - return false; - } - } - - return true; -} diff --git a/pcbnew/exporters/step/pcb/kicadmodel.h b/pcbnew/exporters/step/pcb/kicadmodel.h deleted file mode 100644 index 378f209547..0000000000 --- a/pcbnew/exporters/step/pcb/kicadmodel.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/** - * @file kicadmodel.h - * declares the 3D model object. - */ - -#ifndef KICADMODEL_H -#define KICADMODEL_H - -#include "base.h" - -class KICADMODEL -{ -public: - KICADMODEL(); - virtual ~KICADMODEL(); - - bool Read( SEXPR::SEXPR* aEntry ); - bool Hide() const { return m_hide; } - - std::string m_modelname; - bool m_hide; - TRIPLET m_scale; - TRIPLET m_offset; - TRIPLET m_rotation; -}; - -#endif // KICADMODEL_H diff --git a/pcbnew/exporters/step/pcb/kicadpad.cpp b/pcbnew/exporters/step/pcb/kicadpad.cpp deleted file mode 100644 index 8ecb925b18..0000000000 --- a/pcbnew/exporters/step/pcb/kicadpad.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 "kicadpad.h" - -#include - -#include - -#include -#include - - -static const char bad_pad[] = "* corrupt module in PCB file; bad pad"; - - -KICADPAD::KICADPAD() -{ - m_rotation = 0.0; - m_thruhole = false; - m_drill.oval = false; - return; -} - - -KICADPAD::~KICADPAD() -{ - return; -} - - -bool KICADPAD::Read( const SEXPR::SEXPR* aEntry ) -{ - // form: ( pad N thru_hole shape (at x y {r}) (size x y) (drill {oval} x {y}) (layers X X X) ) - int nchild = aEntry->GetNumberOfChildren(); - - if( nchild < 2 ) - { - std::ostringstream ostr; - ostr << bad_pad << " (line " << aEntry->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child; - - for( int i = 1; i < nchild; ++i ) - { - child = aEntry->GetChild( i ); - - if( child->IsSymbol() && - ( child->GetSymbol() == "thru_hole" || child->GetSymbol() == "np_thru_hole" ) ) - { - m_thruhole = true; - continue; - } - - if( child->IsList() ) - { - std::string name = child->GetChild( 0 )->GetSymbol(); - bool ret = true; - - if( name == "drill" ) - { - // ignore any drill info for SMD pads - if( m_thruhole ) - ret = parseDrill( child ); - } - else if( name == "at" ) - { - ret = Get2DPositionAndRotation( child, m_position, m_rotation ); - } - - if( !ret ) - return false; - } - } - - return true; -} - - -bool KICADPAD::parseDrill( const SEXPR::SEXPR* aDrill ) -{ - // form: (drill {oval} X {Y}) - const char bad_drill[] = "* corrupt pad in PCB file; bad drill"; - int nchild = aDrill->GetNumberOfChildren(); - - if( nchild < 2 ) - { - std::ostringstream ostr; - ostr << bad_drill << " (line " << aDrill->GetLineNumber() << ")"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - SEXPR::SEXPR* child = aDrill->GetChild( 1 ); - int idx = 1; - m_drill.oval = false; - - if( child->IsSymbol() ) - { - if( child->GetSymbol() == "oval" ) - { - m_drill.oval = true; - child = aDrill->GetChild( ++idx ); - } - else - { - std::ostringstream ostr; - ostr << bad_drill << " (line " << child->GetLineNumber(); - ostr << ") (unexpected symbol: "; - ostr << child->GetSymbol() << "), nchild = " << nchild; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - } - - double x; - - if( child->IsDouble() ) - x = child->GetDouble(); - else if( child->IsInteger() ) - x = (double) child->GetInteger(); - else - { - std::ostringstream ostr; - ostr << bad_drill << " (line " << child->GetLineNumber(); - ostr << ") (did not find X size)"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - m_drill.size.x = x; - m_drill.size.y = x; - - if( ++idx == nchild || !m_drill.oval ) - return true; - - for( int i = idx; i < nchild; ++i ) - { - child = aDrill->GetChild( i ); - - // NOTE: the Offset of the copper pad is stored in the drill string but since the copper - // is not needed in the MCAD model the offset is simply ignored. - if( !child->IsList() ) - { - double y; - - if( child->IsDouble() ) - y = child->GetDouble(); - else if( child->IsInteger() ) - y = (double) child->GetInteger(); - else - { - std::ostringstream ostr; - ostr << bad_drill << " (line " << child->GetLineNumber(); - ostr << ") (did not find Y size)"; - wxLogMessage( wxT( "%s\n" ), ostr.str().c_str() ); - return false; - } - - m_drill.size.y = y; - - if( m_drill.size.y != m_drill.size.x ) - m_drill.oval = true; - } - } - - return true; -} diff --git a/pcbnew/exporters/step/pcb/kicadpad.h b/pcbnew/exporters/step/pcb/kicadpad.h deleted file mode 100644 index 3ab4750a79..0000000000 --- a/pcbnew/exporters/step/pcb/kicadpad.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021 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 - */ - -/** - * @file kicadpad.h - * Declare the PAD description object. - */ - -#ifndef KICADPAD_H -#define KICADPAD_H - -#include -#include -#include "base.h" - - -struct KICADDRILL -{ - DOUBLET size; - bool oval; -}; - - -class KICADPAD -{ -public: - KICADPAD(); - virtual ~KICADPAD(); - - bool Read( const SEXPR::SEXPR* aEntry ); - - bool IsThruHole() const - { - return m_thruhole; - } - -private: - bool parseDrill( const SEXPR::SEXPR* aDrill ); - -public: - DOUBLET m_position; - double m_rotation; // rotation (radians) - KICADDRILL m_drill; - -private: - bool m_thruhole; -}; - -#endif // KICADPAD_H diff --git a/pcbnew/exporters/step/pcb/kicadpcb.cpp b/pcbnew/exporters/step/pcb/kicadpcb.cpp deleted file mode 100644 index b8671ca560..0000000000 --- a/pcbnew/exporters/step/pcb/kicadpcb.cpp +++ /dev/null @@ -1,662 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2020-2022 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 "kicadpcb.h" - -#include "kicadcurve.h" -#include "kicadfootprint.h" -#include "oce_utils.h" - -#include -#include - -#include - -#include -#include - -#include -#include - -KICADPCB::KICADPCB( const wxString& aPcbName ) -{ - m_resolver.Set3DConfigDir( wxT( "" ) ); - m_resolver.SetProgramBase( &Pgm() ); - - m_topSolderMask = wxColour( 15, 102, 15 ); - m_bottomSolderMask = wxColour( 15, 102, 15 ); - m_topSilk = wxColour( 240, 240, 240 ); - m_bottomSilk = wxColour( 240, 240, 240 ); - m_copperFinish = wxColour( 191, 155, 58 ); - - m_thickness = 1.6; - m_pcb_model = nullptr; - m_minDistance = MIN_DISTANCE; - m_useGridOrigin = false; - m_useDrillOrigin = false; - m_hasGridOrigin = false; - m_hasDrillOrigin = false; - m_pcbName = aPcbName; -} - - -KICADPCB::~KICADPCB() -{ - for( KICADFOOTPRINT* i : m_footprints ) - delete i; - - for( KICADCURVE* i : m_curves ) - delete i; - - delete m_pcb_model; - - return; -} - - -bool KICADPCB::ReadFile( const wxString& aFileName ) -{ - wxFileName fname( aFileName ); - - if( fname.GetExt() != "kicad_pcb" ) - { - ReportMessage( wxString::Format( wxT( "expecting extension kicad_pcb, got %s\n" ), - fname.GetExt() ) ); - return false; - } - - if( !fname.FileExists() ) - { - ReportMessage( wxString::Format( wxT( "No such file: %s\n" ), aFileName ) ); - return false; - } - - fname.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS ); - m_filename = fname.GetFullPath(); - - try - { - SEXPR::PARSER parser; - std::string infile( fname.GetFullPath().ToUTF8() ); - std::unique_ptr data( parser.ParseFromFile( infile ) ); - - if( !data ) - { - ReportMessage( wxString::Format( wxT( "No data in file: %s\n" ), aFileName ) ); - return false; - } - - if( !parsePCB( data.get() ) ) - return false; - } - catch( std::exception& e ) - { - ReportMessage( wxString::Format( wxT( "error reading file: %s\n%s\n" ), - aFileName, - e.what() ) ); - return false; - } - catch( ... ) - { - ReportMessage( wxString::Format( wxT( "unexpected exception while reading file: %s\n" ), - aFileName ) ); - return false; - } - - return true; -} - - -bool KICADPCB::WriteSTEP( const wxString& aFileName ) -{ - if( m_pcb_model ) - return m_pcb_model->WriteSTEP( aFileName ); - - return false; -} - - -#ifdef SUPPORTS_IGES -bool KICADPCB::WriteIGES( const wxString& aFileName ) -{ - if( m_pcb_model ) - return m_pcb_model->WriteIGES( aFileName ); - - return false; -} -#endif - - -bool KICADPCB::parsePCB( SEXPR::SEXPR* data ) -{ - if( nullptr == data ) - return false; - - if( data->IsList() ) - { - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = data->GetChild( 0 ); - std::string name = child->GetSymbol(); - - bool result = true; - - for( size_t i = 1; i < nc && result; ++i ) - { - child = data->GetChild( i ); - - if( !child->IsList() ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ), - child->GetLineNumber() ) ); - return false; - } - - std::string symname( child->GetChild( 0 )->GetSymbol() ); - - if( symname == "general" ) - result = result && parseGeneral( child ); - else if( symname == "setup" ) - result = result && parseSetup( child ); - else if( symname == "layers" ) - result = result && parseLayers( child ); - else if( symname == "module" ) - result = result && parseModule( child ); - else if( symname == "footprint" ) - result = result && parseModule( child ); - else if( symname == "gr_arc" ) - result = result && parseCurve( child, CURVE_ARC ); - else if( symname == "gr_line" ) - result = result && parseCurve( child, CURVE_LINE ); - else if( symname == "gr_rect" ) - result = result && parseRect( child ); - else if( symname == "gr_poly" ) - result = result && parsePolygon( child ); - else if( symname == "gr_circle" ) - result = result && parseCurve( child, CURVE_CIRCLE ); - else if( symname == "gr_curve" ) - result = result && parseCurve( child, CURVE_BEZIER ); - } - - return result; - } - - ReportMessage( wxString::Format( wxT( "data is not a valid PCB file: %s\n" ), m_filename ) ); - return false; -} - - -bool KICADPCB::parseGeneral( SEXPR::SEXPR* data ) -{ - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = nullptr; - - for( size_t i = 1; i < nc; ++i ) - { - child = data->GetChild( i ); - - if( !child->IsList() ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ), - child->GetLineNumber() ) ); - return false; - } - - // at the moment only the thickness is of interest in the general section - if( child->GetChild( 0 )->GetSymbol() != "thickness" ) - continue; - - m_thickness = child->GetChild( 1 )->GetDouble(); - return true; - } - - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" - "no PCB thickness specified in general section\n" ), - child->GetLineNumber() ) ); - return false; -} - - -bool KICADPCB::parseLayers( SEXPR::SEXPR* data ) -{ - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = nullptr; - - // Read the layername and the corresponding layer id list: - for( size_t i = 1; i < nc; ++i ) - { - child = data->GetChild( i ); - - if( !child->IsList() ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ), - child->GetLineNumber() ) ); - return false; - } - - std::string ref; - - if( child->GetChild( 1 )->IsSymbol() ) - ref = child->GetChild( 1 )->GetSymbol(); - else - ref = child->GetChild( 1 )->GetString(); - - m_layersNames[ref] = child->GetChild( 0 )->GetInteger(); - } - - return true; -} - - -bool KICADPCB::parseStackupLayer( SEXPR::SEXPR* data ) -{ - if( data->IsList() && data->GetNumberOfChildren() >= 3 ) - { - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = nullptr; - std::string ref; - std::string value; - - for( size_t i = 1; i < nc; ++i ) - { - child = data->GetChild( i ); - - if( child->IsList() && child->GetChild( 0 )->GetSymbol() == "type" ) - { - if( child->GetChild( 1 )->IsSymbol() ) - ref = child->GetChild( 1 )->GetSymbol(); - else - ref = child->GetChild( 1 )->GetString(); - } - else if( child->IsList() && child->GetChild( 0 )->GetSymbol() == "color" ) - { - if( child->GetChild( 1 )->IsSymbol() ) - value = child->GetChild( 1 )->GetSymbol(); - else - value = child->GetChild( 1 )->GetString(); - } - } - - if( !value.empty() ) - { - wxString colorName( value ); - wxColour color; - - if( colorName.StartsWith( wxT( "#" ) ) ) - color = wxColour( colorName.Left( 7 ) /* drop alpha component */ ); - else if( colorName == wxT( "Green" ) ) - color = wxColour( 20, 51, 36 ); - else if( colorName == wxT( "Light Green" ) ) - color = wxColour( 91, 168, 12); - else if( colorName == wxT( "Saturated Green" ) ) - color = wxColour( 13, 104, 11 ); - else if( colorName == wxT( "Red" ) ) - color = wxColour( 181, 19, 21 ); - else if( colorName == wxT( "Light Red" ) ) - color = wxColour( 210, 40, 14 ); - else if( colorName == wxT( "Red/Orange" ) ) - color = wxColour( 239, 53, 41 ); - else if( colorName == wxT( "Blue" ) ) - color = wxColour( 2, 59, 162 ); - else if( colorName == wxT( "Light Blue 1" ) ) - color = wxColour( 54, 79, 116 ); - else if( colorName == wxT( "Light Blue 2" ) ) - color = wxColour( 61, 85, 130 ); - else if( colorName == wxT( "Green/Blue" ) ) - color = wxColour( 21, 70, 80 ); - else if( colorName == wxT( "Black" ) ) - color = wxColour( 11, 11, 11 ); - else if( colorName == wxT( "White" ) ) - color = wxColour( 245, 245, 245 ); - else if( colorName == wxT( "Purple" ) ) - color = wxColour( 32, 2, 53 ); - else if( colorName == wxT( "Light Purple" ) ) - color = wxColour( 119, 31, 91 ); - else if( colorName == wxT( "Yellow" ) ) - color = wxColour( 194, 195, 0 ); - - if( ref == "Top Silk Screen" ) - m_topSilk = color; - else if( ref == "Top Solder Mask" ) - m_topSolderMask = color; - else if( ref == "Bottom Silk Screen" ) - m_bottomSilk = color; - else if( ref == "Bottom Solder Mask" ) - m_bottomSolderMask = color; - } - } - - return true; -} - - -bool KICADPCB::parseStackup( SEXPR::SEXPR* data ) -{ - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = nullptr; - - for( size_t i = 1; i < nc; ++i ) - { - child = data->GetChild( i ); - - if( !child->IsList() ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ), - child->GetLineNumber() ) ); - return false; - } - - std::string ref; - - if( child->GetChild( 0 )->GetSymbol() == "layer" ) - { - parseStackupLayer( child ); - } - else if( child->GetChild( 0 )->GetSymbol() == "copper_finish" ) - { - if( child->GetChild( 1 )->IsSymbol() ) - ref = child->GetChild( 1 )->GetSymbol(); - else - ref = child->GetChild( 1 )->GetString(); - - wxString finishName( ref ); - - if( finishName.EndsWith( wxT( "OSP" ) ) ) - { - m_copperFinish = wxColour( 184, 115, 50 ); - } - else if( finishName.EndsWith( wxT( "IG" ) ) - || finishName.EndsWith( wxT( "gold" ) ) ) - { - m_copperFinish = wxColour( 178, 156, 0 ); - } - else if( finishName.StartsWith( wxT( "HAL" ) ) - || finishName.StartsWith( wxT( "HASL" ) ) - || finishName.EndsWith( wxT( "tin" ) ) - || finishName.EndsWith( wxT( "nickel" ) ) ) - { - m_copperFinish = wxColour( 160, 160, 160 ); - } - else if( finishName.EndsWith( wxT( "silver" ) ) ) - { - m_copperFinish = wxColour( 213, 213, 213 ); - } - } - } - - return true; -} - - -int KICADPCB::GetLayerId( std::string& aLayerName ) -{ - int lid = -1; - auto item = m_layersNames.find( aLayerName ); - - if( item != m_layersNames.end() ) - lid = item->second; - - return lid; -} - - -bool KICADPCB::parseSetup( SEXPR::SEXPR* data ) -{ - size_t nc = data->GetNumberOfChildren(); - SEXPR::SEXPR* child = nullptr; - - for( size_t i = 1; i < nc; ++i ) - { - child = data->GetChild( i ); - - if( !child->IsList() ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ), - child->GetLineNumber() ) ); - return false; - } - - // at the moment only the Grid and Drill origins are of interest in the setup section - if( child->GetChild( 0 )->GetSymbol() == "grid_origin" ) - { - if( child->GetNumberOfChildren() != 3 ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d): grid_origin has " - "%d children (expected: 3)\n" ), - child->GetLineNumber(), - child->GetNumberOfChildren() ) ); - return false; - } - - m_gridOrigin.x = child->GetChild( 1 )->GetDouble(); - m_gridOrigin.y = child->GetChild( 2 )->GetDouble(); - m_hasGridOrigin = true; - } - else if( child->GetChild( 0 )->GetSymbol() == "aux_axis_origin" ) - { - if( child->GetNumberOfChildren() != 3 ) - { - ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d): aux_axis_origin " - "has %d children (expected: 3)\n" ), - child->GetLineNumber(), - child->GetNumberOfChildren() ) ); - return false; - } - - m_drillOrigin.x = child->GetChild( 1 )->GetDouble(); - m_drillOrigin.y = child->GetChild( 2 )->GetDouble(); - m_hasDrillOrigin = true; - } - else if( child->GetChild( 0 )->GetSymbol() == "stackup" ) - { - parseStackup( child ); - } - } - - return true; -} - - -bool KICADPCB::parseModule( SEXPR::SEXPR* data ) -{ - KICADFOOTPRINT* footprint = new KICADFOOTPRINT( this ); - - if( !footprint->Read( data ) ) - { - delete footprint; - return false; - } - - m_footprints.push_back( footprint ); - return true; -} - - -bool KICADPCB::parseRect( SEXPR::SEXPR* data ) -{ - KICADCURVE* rect = new KICADCURVE(); - - if( !rect->Read( data, CURVE_LINE ) ) - { - delete rect; - return false; - } - - // reject any curves not on the Edge.Cuts layer - if( rect->GetLayer() != LAYER_EDGE ) - { - delete rect; - return true; - } - - KICADCURVE* top = new KICADCURVE( *rect ); - KICADCURVE* right = new KICADCURVE( *rect ); - KICADCURVE* bottom = new KICADCURVE( *rect ); - KICADCURVE* left = new KICADCURVE( *rect ); - delete rect; - - top->m_end.y = right->m_start.y; - m_curves.push_back( top ); - - right->m_start.x = bottom->m_end.x; - m_curves.push_back( right ); - - bottom->m_start.y = left->m_end.y; - m_curves.push_back( bottom ); - - left->m_end.x = top->m_start.x; - m_curves.push_back( left ); - - return true; -} - - -bool KICADPCB::parsePolygon( SEXPR::SEXPR* data ) -{ - std::unique_ptr poly = std::make_unique(); - - if( !poly->Read( data, CURVE_POLYGON ) ) - return false; - - // reject any curves not on the Edge.Cuts layer - if( poly->GetLayer() != LAYER_EDGE ) - return true; - - auto pts = poly->m_poly; - - for( std::size_t ii = 1; ii < pts.size(); ++ii ) - { - KICADCURVE* seg = new KICADCURVE(); - seg->m_form = CURVE_LINE; - seg->m_layer = poly->GetLayer(); - seg->m_start = pts[ii - 1]; - seg->m_end = pts[ii]; - m_curves.push_back( seg ); - } - - KICADCURVE* seg = new KICADCURVE(); - seg->m_form = CURVE_LINE; - seg->m_layer = poly->GetLayer(); - seg->m_start = pts.back(); - seg->m_end = pts.front(); - m_curves.push_back( seg ); - - return true; -} - - -bool KICADPCB::parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType ) -{ - KICADCURVE* curve = new KICADCURVE(); - - if( !curve->Read( data, aCurveType ) ) - { - delete curve; - return false; - } - - // reject any curves not on the Edge.Cuts layer - if( curve->GetLayer() != LAYER_EDGE ) - { - delete curve; - return true; - } - - m_curves.push_back( curve ); - return true; -} - - -bool KICADPCB::ComposePCB( bool aComposeVirtual, bool aSubstituteModels ) -{ - if( m_pcb_model ) - return true; - - if( m_footprints.empty() && m_curves.empty() ) - { - ReportMessage( wxT( "Error: no PCB data (no footprint, no outline) to render\n" ) ); - return false; - } - - DOUBLET origin; - - // Determine the coordinate system reference: - // Precedence of reference point is Drill Origin > Grid Origin > User Offset - if( m_useDrillOrigin && m_hasDrillOrigin ) - origin = m_drillOrigin; - else if( m_useGridOrigin && m_hasDrillOrigin ) - origin = m_gridOrigin; - else - origin = m_origin; - - m_pcb_model = new PCBMODEL( m_pcbName ); - - // TODO: Handle when top & bottom soldermask colours are different... - m_pcb_model->SetBoardColor( m_topSolderMask.Red() / 255.0, - m_topSolderMask.Green() / 255.0, - m_topSolderMask.Blue() / 255.0 ); - - m_pcb_model->SetPCBThickness( m_thickness ); - m_pcb_model->SetMinDistance( m_minDistance ); - - for( KICADCURVE* i : m_curves ) - { - if( CURVE_NONE == i->m_form || LAYER_EDGE != i->m_layer ) - continue; - - // adjust the coordinate system - // Note: we negate the Y coordinates due to the fact in Pcbnew the Y axis - // is from top to bottom. - KICADCURVE lcurve = *i; - lcurve.m_start.y = -( lcurve.m_start.y - origin.y ); - lcurve.m_end.y = -( lcurve.m_end.y - origin.y ); - lcurve.m_start.x -= origin.x; - lcurve.m_end.x -= origin.x; - - // used in bezier curves: - lcurve.m_bezierctrl1.y = -( lcurve.m_bezierctrl1.y - origin.y ); - lcurve.m_bezierctrl1.x -= origin.x; - lcurve.m_bezierctrl2.y = -( lcurve.m_bezierctrl2.y - origin.y ); - lcurve.m_bezierctrl2.x -= origin.x; - - if( CURVE_ARC == lcurve.m_form ) - lcurve.m_angle = -lcurve.m_angle; - - m_pcb_model->AddOutlineSegment( &lcurve ); - } - - for( KICADFOOTPRINT* i : m_footprints ) - i->ComposePCB( m_pcb_model, &m_resolver, origin, aComposeVirtual, aSubstituteModels ); - - ReportMessage( wxT( "Create PCB solid model\n" ) ); - - if( !m_pcb_model->CreatePCB() ) - { - ReportMessage( wxT( "could not create PCB solid model\n" ) ); - delete m_pcb_model; - m_pcb_model = nullptr; - return false; - } - - return true; -} diff --git a/pcbnew/exporters/step/pcb/kicadpcb.h b/pcbnew/exporters/step/pcb/kicadpcb.h deleted file mode 100644 index 8f40237b97..0000000000 --- a/pcbnew/exporters/step/pcb/kicadpcb.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021-2022 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 - */ - -/** - * @file kicadpcb.h - * declares the main PCB object - */ - -#ifndef KICADPCB_H -#define KICADPCB_H - -#include -#include -#include -#include -#include -#include "base.h" - -#ifdef SUPPORTS_IGES -#undef SUPPORTS_IGES -#endif - -extern void ReportMessage( const wxString& aMessage ); - -namespace SEXPR -{ - class SEXPR; -} - -class KICADFOOTPRINT; -class KICADCURVE; -class PCBMODEL; - -class KICADPCB -{ -public: - KICADPCB( const wxString& aPcbName ); - virtual ~KICADPCB(); - - int GetLayerId( std::string& aLayerName ); - - void SetOrigin( double aXOrigin, double aYOrigin ) - { - m_origin.x = aXOrigin; - m_origin.y = aYOrigin; - } - - void UseGridOrigin( bool aUseOrigin ) - { - m_useGridOrigin = aUseOrigin; - } - - void UseDrillOrigin( bool aUseOrigin ) - { - m_useDrillOrigin = aUseOrigin; - } - - void SetMinDistance( double aDistance ) - { - m_minDistance = aDistance; - } - - bool ReadFile( const wxString& aFileName ); - bool ComposePCB( bool aComposeVirtual = true, bool aSubstituteModels = true ); - bool WriteSTEP( const wxString& aFileName ); - -#ifdef SUPPORTS_IGES - bool WriteIGES( const wxString& aFileName ); -#endif - -private: - bool parsePCB( SEXPR::SEXPR* data ); - bool parseGeneral( SEXPR::SEXPR* data ); - bool parseSetup( SEXPR::SEXPR* data ); - bool parseStackup( SEXPR::SEXPR* data ); - bool parseStackupLayer( SEXPR::SEXPR* data ); - bool parseLayers( SEXPR::SEXPR* data ); - bool parseModule( SEXPR::SEXPR* data ); - bool parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType ); - bool parseRect( SEXPR::SEXPR* data ); - bool parsePolygon( SEXPR::SEXPR* data ); - -private: - FILENAME_RESOLVER m_resolver; - wxString m_filename; - PCBMODEL* m_pcb_model; - DOUBLET m_origin; - DOUBLET m_gridOrigin; - DOUBLET m_drillOrigin; - bool m_useGridOrigin; - bool m_useDrillOrigin; - bool m_hasGridOrigin; // indicates origin found in source file - bool m_hasDrillOrigin; // indicates origin found in source file - - // minimum distance between points to treat them as separate entities (mm) - double m_minDistance; - - // the names of layers in use, and the internal layer ID - std::map m_layersNames; - - // PCB parameters/entities - double m_thickness; - wxColour m_topSolderMask; - wxColour m_bottomSolderMask; // Not currently used. - wxColour m_topSilk; // Not currently used. - wxColour m_bottomSilk; // Not currently used. - wxColour m_copperFinish; // Not currently used. - std::vector m_footprints; - std::vector m_curves; - wxString m_pcbName; -}; - - -#endif // KICADPCB_H diff --git a/pcbnew/exporters/step/pcb/oce_utils.cpp b/pcbnew/exporters/step/step_pcb_model.cpp similarity index 58% rename from pcbnew/exporters/step/pcb/oce_utils.cpp rename to pcbnew/exporters/step/step_pcb_model.cpp index 866b7b06b7..f3210ba176 100644 --- a/pcbnew/exporters/step/pcb/oce_utils.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -1,8 +1,9 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * + * Copyright (C) 2022 Mark Roszko * Copyright (C) 2016 Cirilo Bernardo - * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-2021 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 @@ -35,8 +36,10 @@ #include -#include "oce_utils.h" -#include "kicadpad.h" +#include +#include + +#include "step_pcb_model.h" #include "streamwrapper.h" #include @@ -100,68 +103,7 @@ static constexpr double THICKNESS_DEFAULT = 1.6; static constexpr double BOARD_OFFSET = 0.05; // min. length**2 below which 2 points are considered coincident -static constexpr double MIN_LENGTH2 = MIN_DISTANCE * MIN_DISTANCE; - -static void getEndPoints( const KICADCURVE& aCurve, double& spx0, double& spy0, - double& epx0, double& epy0 ) -{ - if( CURVE_ARC == aCurve.m_form ) - { - spx0 = aCurve.m_end.x; - spy0 = aCurve.m_end.y; - epx0 = aCurve.m_ep.x; - epy0 = aCurve.m_ep.y; - return; - } - - // assume a line - spx0 = aCurve.m_start.x; - spy0 = aCurve.m_start.y; - epx0 = aCurve.m_end.x; - epy0 = aCurve.m_end.y; -} - - -static void getCurveEndPoint( const KICADCURVE& aCurve, DOUBLET& aEndPoint ) -{ - if( CURVE_CIRCLE == aCurve.m_form ) - return; // circles are closed loops and have no end point - - if( CURVE_ARC == aCurve.m_form ) - { - aEndPoint.x = aCurve.m_ep.x; - aEndPoint.y = aCurve.m_ep.y; - return; - } - - // assume a line - aEndPoint.x = aCurve.m_end.x; - aEndPoint.y = aCurve.m_end.y; -} - - -static void reverseCurve( KICADCURVE& aCurve ) -{ - if( CURVE_NONE == aCurve.m_form || CURVE_CIRCLE == aCurve.m_form ) - return; - - if( CURVE_LINE == aCurve.m_form ) - { - std::swap( aCurve.m_start, aCurve.m_end ); - return; - } - - if( CURVE_BEZIER == aCurve.m_form ) - { - std::swap( aCurve.m_start, aCurve.m_end ); - std::swap( aCurve.m_bezierctrl1, aCurve.m_bezierctrl2 ); - return; - } - - std::swap( aCurve.m_end, aCurve.m_ep ); - std::swap( aCurve.m_endangle, aCurve.m_startangle ); - aCurve.m_angle = -aCurve.m_angle; -} +static constexpr double MIN_LENGTH2 = STEPEXPORT_MIN_DISTANCE * STEPEXPORT_MIN_DISTANCE; // supported file types @@ -242,7 +184,7 @@ FormatType fileType( const char* aFileName ) } -PCBMODEL::PCBMODEL( const wxString& aPcbName ) +STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName ) { m_app = XCAFApp_Application::GetApplication(); m_app->NewDocument( "MDTV-XCAF", m_doc ); @@ -255,222 +197,30 @@ PCBMODEL::PCBMODEL( const wxString& aPcbName ) m_thickness = THICKNESS_DEFAULT; m_minDistance2 = MIN_LENGTH2; m_minx = 1.0e10; // absurdly large number; any valid PCB X value will be smaller - m_mincurve = m_curves.end(); m_pcbName = aPcbName; - BRepBuilderAPI::Precision( MIN_DISTANCE ); + BRepBuilderAPI::Precision( STEPEXPORT_MIN_DISTANCE ); } -PCBMODEL::~PCBMODEL() +STEP_PCB_MODEL::~STEP_PCB_MODEL() { m_doc->Close(); } -bool PCBMODEL::AddOutlineSegment( KICADCURVE* aCurve ) +bool STEP_PCB_MODEL::AddPadHole( const PAD* aPad ) { - if( NULL == aCurve || LAYER_EDGE != aCurve->m_layer || CURVE_NONE == aCurve->m_form ) + if( NULL == aPad || !aPad->GetDrillSize().x ) return false; - if( CURVE_LINE == aCurve->m_form || CURVE_BEZIER == aCurve->m_form ) + VECTOR2I pos = aPad->GetPosition(); + + if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) { - // reject zero - length lines - double dx = aCurve->m_end.x - aCurve->m_start.x; - double dy = aCurve->m_end.y - aCurve->m_start.y; - double distance = dx * dx + dy * dy; - - if( distance < m_minDistance2 ) - { - wxString msg; - msg.Printf( wxT( " * AddOutlineSegment() rejected a zero-length %s\n" ), - aCurve->Describe() ); - ReportMessage( msg ); - return false; - } - } - else - { - // ensure that the start (center) and end (start of arc) are not the same point - double dx = aCurve->m_end.x - aCurve->m_start.x; - double dy = aCurve->m_end.y - aCurve->m_start.y; - double rad = dx * dx + dy * dy; - - if( rad < m_minDistance2 ) - { - wxString msg; - msg.Printf( wxT( " * AddOutlineSegment() rejected a zero-radius %s\n" ), - aCurve->Describe() ); - ReportMessage( msg ); - return false; - } - - // calculate the radius and, if applicable, end point - rad = sqrt( rad ); - aCurve->m_radius = rad; - - if( CURVE_ARC == aCurve->m_form ) - { - aCurve->m_startangle = atan2( dy, dx ); - - if( aCurve->m_startangle < 0.0 ) - aCurve->m_startangle += 2.0 * M_PI; - - double eang = aCurve->m_startangle + aCurve->m_angle; - - if( eang < 0.0 ) - eang += 2.0 * M_PI; - - if( aCurve->m_angle < 0.0 && eang > aCurve->m_startangle ) - aCurve->m_startangle += 2.0 * M_PI; - else if( aCurve->m_angle >= 0.0 && eang < aCurve->m_startangle ) - eang += 2.0 * M_PI; - - aCurve->m_endangle = eang; - aCurve->m_ep.x = aCurve->m_start.x + rad * cos( eang ); - aCurve->m_ep.y = aCurve->m_start.y + rad * sin( eang ); - - dx = aCurve->m_ep.x - aCurve->m_end.x; - dy = aCurve->m_ep.y - aCurve->m_end.y; - rad = dx * dx + dy * dy; - - if( rad < m_minDistance2 ) - { - ReportMessage( wxString::Format( wxT( " * AddOutlineSegment() rejected an arc " - "with equivalent end points, %s\n" ), - aCurve->Describe() ) ); - return false; - } - } - } - - m_curves.push_back( *aCurve ); - - // check if this curve has the current leftmost feature - switch( aCurve->m_form ) - { - case CURVE_LINE: - if( aCurve->m_start.x < m_minx ) - { - m_minx = aCurve->m_start.x; - m_mincurve = --( m_curves.end() ); - } - - if( aCurve->m_end.x < m_minx ) - { - m_minx = aCurve->m_end.x; - m_mincurve = --( m_curves.end() ); - } - - break; - - case CURVE_CIRCLE: - { - double dx = aCurve->m_start.x - aCurve->m_radius; - - if( dx < m_minx ) - { - m_minx = dx; - m_mincurve = --( m_curves.end() ); - } - - break; - } - - case CURVE_ARC: - { - double dx0 = aCurve->m_end.x - aCurve->m_start.x; - double dy0 = aCurve->m_end.y - aCurve->m_start.y; - int q0; // quadrant of start point - - if( dx0 > 0.0 && dy0 >= 0.0 ) - q0 = 1; - else if( dx0 <= 0.0 && dy0 > 0.0 ) - q0 = 2; - else if( dx0 < 0.0 && dy0 <= 0.0 ) - q0 = 3; - else - q0 = 4; - - double dx1 = aCurve->m_ep.x - aCurve->m_start.x; - double dy1 = aCurve->m_ep.y - aCurve->m_start.y; - int q1; // quadrant of end point - - if( dx1 > 0.0 && dy1 >= 0.0 ) - q1 = 1; - else if( dx1 <= 0.0 && dy1 > 0.0 ) - q1 = 2; - else if( dx1 < 0.0 && dy1 <= 0.0 ) - q1 = 3; - else - q1 = 4; - - // calculate x0, y0 for the start point on a CCW arc - double x0 = aCurve->m_end.x; - double x1 = aCurve->m_ep.x; - - if( aCurve->m_angle < 0.0 ) - { - std::swap( q0, q1 ); - std::swap( x0, x1 ); - } - - double minx; - - if( ( q0 <= 2 && q1 >= 3 ) || ( q0 >= 3 && x0 > x1 ) ) - minx = aCurve->m_start.x - aCurve->m_radius; - else - minx = std::min( x0, x1 ); - - if( minx < m_minx ) - { - m_minx = minx; - m_mincurve = --( m_curves.end() ); - } - - break; - } - - case CURVE_BEZIER: - if( aCurve->m_start.x < m_minx ) - { - m_minx = aCurve->m_start.x; - m_mincurve = --( m_curves.end() ); - } - - if( aCurve->m_end.x < m_minx ) - { - m_minx = aCurve->m_end.x; - m_mincurve = --( m_curves.end() ); - } - - break; - - default: // unexpected curve type - { - wxString msg; - msg.Printf( wxT( " * AddOutlineSegment() unsupported curve type: %d\n" ), - aCurve->m_form ); - ReportMessage( msg ); - } - - return false; - } - - return true; -} - - -bool PCBMODEL::AddPadHole( const KICADPAD* aPad ) -{ - if( NULL == aPad || !aPad->IsThruHole() ) - return false; - - if( !aPad->m_drill.oval ) - { - TopoDS_Shape s = BRepPrimAPI_MakeCylinder( aPad->m_drill.size.x * 0.5, - m_thickness * 2.0 ).Shape(); + TopoDS_Shape s = + BRepPrimAPI_MakeCylinder( pcbIUScale.IUTomm( aPad->GetDrillSize().x ) * 0.5, m_thickness * 2.0 ).Shape(); gp_Trsf shift; - shift.SetTranslation( gp_Vec( aPad->m_position.x, aPad->m_position.y, + shift.SetTranslation( gp_Vec( pcbIUScale.IUTomm( pos.x ), -pcbIUScale.IUTomm( pos.y ), -m_thickness * 0.5 ) ); BRepBuilderAPI_Transform hole( s, shift ); m_cutouts.push_back( hole.Shape() ); @@ -479,132 +229,48 @@ bool PCBMODEL::AddPadHole( const KICADPAD* aPad ) // slotted hole double angle_offset = 0.0; - double rad; // radius of the slot - double hlen; // half length of the slot + double rad; // radius of the slot + double hlen; // half length of the slot - if( aPad->m_drill.size.x < aPad->m_drill.size.y ) + if( aPad->GetDrillSize().x < aPad->GetDrillSize().y ) { angle_offset = M_PI_2; - rad = aPad->m_drill.size.x * 0.5; - hlen = aPad->m_drill.size.y * 0.5 - rad; + rad = aPad->GetDrillSize().x * 0.5; + hlen = aPad->GetDrillSize().y * 0.5 - rad; } else { - rad = aPad->m_drill.size.y * 0.5; - hlen = aPad->m_drill.size.x * 0.5 - rad; + rad = aPad->GetDrillSize().y * 0.5; + hlen = aPad->GetDrillSize().x * 0.5 - rad; } - DOUBLET c0( -hlen, 0.0 ); - DOUBLET c1( hlen, 0.0 ); - DOUBLET p0( -hlen, rad ); - DOUBLET p1( -hlen, -rad ); - DOUBLET p2( hlen, -rad ); - DOUBLET p3( hlen, rad ); - - angle_offset += aPad->m_rotation; - double dlim = (double)std::numeric_limits< float >::epsilon(); - - if( angle_offset < -dlim || angle_offset > dlim ) + SHAPE_POLY_SET holeOutlines; + if( !aPad->TransformHoleToPolygon( holeOutlines, 0, m_maxError, ERROR_INSIDE ) ) { - double vsin = sin( angle_offset ); - double vcos = cos( angle_offset ); - - double x = c0.x * vcos - c0.y * vsin; - double y = c0.x * vsin + c0.y * vcos; - c0.x = x; - c0.y = y; - - x = c1.x * vcos - c1.y * vsin; - y = c1.x * vsin + c1.y * vcos; - c1.x = x; - c1.y = y; - - x = p0.x * vcos - p0.y * vsin; - y = p0.x * vsin + p0.y * vcos; - p0.x = x; - p0.y = y; - - x = p1.x * vcos - p1.y * vsin; - y = p1.x * vsin + p1.y * vcos; - p1.x = x; - p1.y = y; - - x = p2.x * vcos - p2.y * vsin; - y = p2.x * vsin + p2.y * vcos; - p2.x = x; - p2.y = y; - - x = p3.x * vcos - p3.y * vsin; - y = p3.x * vsin + p3.y * vcos; - p3.x = x; - p3.y = y; + return false; } - c0.x += aPad->m_position.x; - c0.y += aPad->m_position.y; - c1.x += aPad->m_position.x; - c1.y += aPad->m_position.y; - p0.x += aPad->m_position.x; - p0.y += aPad->m_position.y; - p1.x += aPad->m_position.x; - p1.y += aPad->m_position.y; - p2.x += aPad->m_position.x; - p2.y += aPad->m_position.y; - p3.x += aPad->m_position.x; - p3.y += aPad->m_position.y; + TopoDS_Shape hole; - OUTLINE oln; - oln.SetMinSqDistance( m_minDistance2 ); - KICADCURVE crv0, crv1, crv2, crv3; - - // crv0 = arc - crv0.m_start = c0; - crv0.m_end = p0; - crv0.m_ep = p1; - crv0.m_angle = M_PI; - crv0.m_radius = rad; - crv0.m_form = CURVE_ARC; - - // crv1 = line - crv1.m_start = p1; - crv1.m_end = p2; - crv1.m_form = CURVE_LINE; - - // crv2 = arc - crv2.m_start = c1; - crv2.m_end = p2; - crv2.m_ep = p3; - crv2.m_angle = M_PI; - crv2.m_radius = rad; - crv2.m_form = CURVE_ARC; - - // crv3 = line - crv3.m_start = p3; - crv3.m_end = p0; - crv3.m_form = CURVE_LINE; - - oln.AddSegment( crv0 ); - oln.AddSegment( crv1 ); - oln.AddSegment( crv2 ); - oln.AddSegment( crv3 ); - TopoDS_Shape slot; - - if( oln.MakeShape( slot, m_thickness ) ) + if( holeOutlines.OutlineCount() > 0 ) { - if( !slot.IsNull() ) - m_cutouts.push_back( slot ); - - return true; + if( MakeShape( hole, holeOutlines.COutline( 0 ), m_thickness ) ) + { + m_cutouts.push_back( hole ); + } + } + else + { + return false; } - return false; + return true; } -bool PCBMODEL::AddComponent( const std::string& aFileNameUTF8, const std::string& aRefDes, - bool aBottom, DOUBLET aPosition, double aRotation, - TRIPLET aOffset, TRIPLET aOrientation, TRIPLET aScale, - bool aSubstituteModels ) +bool STEP_PCB_MODEL::AddComponent( const std::string& aFileNameUTF8, const std::string& aRefDes, + bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, + VECTOR3D aOrientation, VECTOR3D aScale, bool aSubstituteModels ) { if( aFileNameUTF8.empty() ) { @@ -617,7 +283,7 @@ bool PCBMODEL::AddComponent( const std::string& aFileNameUTF8, const std::string // first retrieve a label TDF_Label lmodel; - wxString errorMessage; + wxString errorMessage; if( !getModelLabel( aFileNameUTF8, aScale, lmodel, aSubstituteModels, &errorMessage ) ) { @@ -634,8 +300,8 @@ bool PCBMODEL::AddComponent( const std::string& aFileNameUTF8, const std::string if( !getModelLocation( aBottom, aPosition, aRotation, aOffset, aOrientation, toploc ) ) { - ReportMessage( wxString::Format( wxT( "No location data for filename '%s'.\n" ), - fileName ) ); + ReportMessage( + wxString::Format( wxT( "No location data for filename '%s'.\n" ), fileName ) ); return false; } @@ -657,7 +323,7 @@ bool PCBMODEL::AddComponent( const std::string& aFileNameUTF8, const std::string } -void PCBMODEL::SetPCBThickness( double aThickness ) +void STEP_PCB_MODEL::SetPCBThickness( double aThickness ) { if( aThickness < 0.0 ) m_thickness = THICKNESS_DEFAULT; @@ -668,7 +334,7 @@ void PCBMODEL::SetPCBThickness( double aThickness ) } -void PCBMODEL::SetBoardColor( double r, double g, double b ) +void STEP_PCB_MODEL::SetBoardColor( double r, double g, double b ) { m_boardColor[0] = r; m_boardColor[1] = g; @@ -676,10 +342,10 @@ void PCBMODEL::SetBoardColor( double r, double g, double b ) } -void PCBMODEL::SetMinDistance( double aDistance ) +void STEP_PCB_MODEL::SetMinDistance( double aDistance ) { // Ensure a minimal value (in mm) - aDistance = std::max( aDistance, MIN_ACCEPTABLE_DISTANCE ); + aDistance = std::max( aDistance, STEPEXPORT_MIN_ACCEPTABLE_DISTANCE ); // m_minDistance2 keeps a squared distance value m_minDistance2 = aDistance * aDistance; @@ -687,7 +353,73 @@ void PCBMODEL::SetMinDistance( double aDistance ) } -bool PCBMODEL::CreatePCB() +bool STEP_PCB_MODEL::MakeShape( TopoDS_Shape& aShape, const SHAPE_LINE_CHAIN& aChain, double aThickness ) +{ + if( !aShape.IsNull() ) + return false; // there is already data in the shape object + + if( !aChain.IsClosed() ) + return false; // the loop is not closed + + BRepBuilderAPI_MakeWire wire; + TopoDS_Edge edge; + bool success = true; + + for( int j = 0; j < aChain.PointCount(); j++ ) + { + gp_Pnt start = gp_Pnt( pcbIUScale.IUTomm(aChain.CPoint( j ).x ), -pcbIUScale.IUTomm( aChain.CPoint( j ).y ), 0.0 ); + + gp_Pnt end; + if( j >= aChain.PointCount() ) + { + end = gp_Pnt( pcbIUScale.IUTomm(aChain.CPoint( 0 ).x), -pcbIUScale.IUTomm(aChain.CPoint( 0 ).y ), 0.0 ); + } + else + { + end = gp_Pnt( pcbIUScale.IUTomm(aChain.CPoint( j + 1 ).x), -pcbIUScale.IUTomm(aChain.CPoint( j + 1 ).y ), 0.0 ); + } + + try + { + edge = BRepBuilderAPI_MakeEdge( start, end ); + + wire.Add( edge ); + + if( BRepBuilderAPI_DisconnectedWire == wire.Error() ) + { + ReportMessage( wxT( "failed to add curve\n" ) ); + return false; + } + } + catch( const Standard_Failure& e ) + { + ReportMessage( + wxString::Format( wxT( "Exception caught: %s\n" ), e.GetMessageString() ) ); + success = false; + } + + if( !success ) + { + ReportMessage( wxS( "failed to add edge\n" ) ); + return false; + } + } + + + TopoDS_Face face = BRepBuilderAPI_MakeFace( wire ); + aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) ); + + if( aShape.IsNull() ) + { + ReportMessage( wxT( "failed to create a prismatic shape\n" ) ); + return false; + } + + return true; +} + + +bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline ) { if( m_hasPCB ) { @@ -697,123 +429,30 @@ bool PCBMODEL::CreatePCB() return true; } - if( m_curves.empty() || m_mincurve == m_curves.end() ) - { - m_hasPCB = true; - ReportMessage( wxT( "No valid board outline.\n" ) ); - return false; - } - - m_hasPCB = true; // whether or not operations fail we note that CreatePCB has been invoked + m_hasPCB = true; // whether or not operations fail we note that CreatePCB has been invoked TopoDS_Shape board; - OUTLINE oln; // loop to assemble (represents PCB outline and cutouts) - oln.SetMinSqDistance( m_minDistance2 ); - oln.AddSegment( *m_mincurve ); - m_curves.erase( m_mincurve ); - ReportMessage( wxString::Format( wxT( "Build board outline (%d items).\n" ), - (int)m_curves.size() ) ); - - while( !m_curves.empty() ) + for( int cnt = 0; cnt < aOutline.OutlineCount(); cnt++ ) { - if( oln.IsClosed() ) + const SHAPE_LINE_CHAIN& outline = aOutline.COutline( cnt ); + + if( !MakeShape( board, outline, m_thickness ) ) { - if( board.IsNull() ) - { - if( !oln.MakeShape( board, m_thickness ) ) - { - ReportMessage( wxT( "Could not create board extrusion.\n" ) ); - return false; - } - } - else - { - TopoDS_Shape hole; - - if( oln.MakeShape( hole, m_thickness ) ) - { - m_cutouts.push_back( hole ); - } - else - { - ReportMessage( wxT( "Could not create board cutout.\n" ) ); - } - } - - oln.Clear(); - - if( !m_curves.empty() ) - { - oln.AddSegment( m_curves.front() ); - m_curves.pop_front(); - } - - continue; + // error } - std::list< KICADCURVE >::iterator sC = m_curves.begin(); - bool added = false; - - while( sC != m_curves.end() ) - { - if( oln.AddSegment( *sC ) ) - { - added = true; - m_curves.erase( sC ); - break; - } - - ++sC; - } - - if( !added && !oln.m_curves.empty() ) - { - wxString msg; - msg.Printf( wxT( "Could not close outline (dropping outline data with %d segments).\n" ), - static_cast( oln.m_curves.size() ) ); - - for( const auto& c : oln.m_curves ) - msg << " + " << c.Describe() << "\n"; - - ReportMessage( msg ); - oln.Clear(); - - if( !m_curves.empty() ) - { - oln.AddSegment( m_curves.front() ); - m_curves.pop_front(); - } - } - } - - if( oln.IsClosed() ) - { - if( board.IsNull() ) - { - if( !oln.MakeShape( board, m_thickness ) ) - { - ReportMessage( wxT( "Could not create board extrusion.\n" ) ); - return false; - } - } - else + // Generate board holes from outlines: + for( int ii = 0; ii < aOutline.HoleCount( cnt ); ii++ ) { + const SHAPE_LINE_CHAIN& holeOutline = aOutline.Hole( cnt, ii ); TopoDS_Shape hole; - if( oln.MakeShape( hole, m_thickness ) ) + if( MakeShape( hole, holeOutline, m_thickness ) ) { m_cutouts.push_back( hole ); } - else - { - ReportMessage( wxT( "Could not create board cutout.\n" ) ); - } } - } - else - { - ReportMessage( wxT( "Could not create closed board outlines.\n" ) ); - return false; + } // subtract cutouts (if any) @@ -821,37 +460,8 @@ bool PCBMODEL::CreatePCB() { ReportMessage( wxString::Format( wxT( "Build board cutouts and holes (%d holes).\n" ), (int) m_cutouts.size() ) ); - } -#if 0 - // First version for holes removing: very slow when having many (> 300) holes - // Substract holes (cutouts) can be time consuming, so display activity - // state to be sure there is no hang: - int char_count = 0; - int cur_count = 0; - int cntmax = m_cutouts.size(); - - for( auto hole : m_cutouts ) - { - board = BRepAlgoAPI_Cut( board, hole ); - - cur_count++; - char_count++; - - if( char_count < 80 ) - { - ReportMessage( wxT( "." ) ); - } - else - { - char_count = 0; - ReportMessage( wxString::Format( wxT( ". %d/%d\n" ), cur_count, cntmax ) ); - } - } -#else // Much faster than first version: group all holes and cut only once - if( m_cutouts.size() ) - { - BRepAlgoAPI_Cut Cut; + BRepAlgoAPI_Cut Cut; TopTools_ListOfShape mainbrd; mainbrd.Append( board ); @@ -859,13 +469,12 @@ bool PCBMODEL::CreatePCB() TopTools_ListOfShape holelist; for( auto hole : m_cutouts ) - holelist.Append( hole ); + holelist.Append( hole ); Cut.SetTools( holelist ); Cut.Build(); board = Cut.Shape(); } -#endif // push the board to the data structure ReportMessage( wxT( "\nGenerate board full shape.\n" ) ); @@ -898,7 +507,7 @@ bool PCBMODEL::CreatePCB() } // color the PCB - Handle( XCAFDoc_ColorTool ) colorTool = XCAFDoc_DocumentTool::ColorTool( m_doc->Main () ); + Handle( XCAFDoc_ColorTool ) colorTool = XCAFDoc_DocumentTool::ColorTool( m_doc->Main() ); Quantity_Color color( m_boardColor[0], m_boardColor[1], m_boardColor[2], Quantity_TOC_RGB ); colorTool->SetColor( m_pcb_label, color, XCAFDoc_ColorSurf ); @@ -912,7 +521,7 @@ bool PCBMODEL::CreatePCB() topex.Next(); } -#if ( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 ) +#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 ) m_assy->UpdateAssemblies(); #endif @@ -922,7 +531,7 @@ bool PCBMODEL::CreatePCB() #ifdef SUPPORTS_IGES // write the assembly model in IGES format -bool PCBMODEL::WriteIGES( const wxString& aFileName ) +bool STEP_PCB_MODEL::WriteIGES( const wxString& aFileName ) { if( m_pcb_label.IsNull() ) { @@ -954,7 +563,7 @@ bool PCBMODEL::WriteIGES( const wxString& aFileName ) #endif -bool PCBMODEL::WriteSTEP( const wxString& aFileName ) +bool STEP_PCB_MODEL::WriteSTEP( const wxString& aFileName ) { if( m_pcb_label.IsNull() ) { @@ -1024,7 +633,7 @@ bool PCBMODEL::WriteSTEP( const wxString& aFileName ) } -bool PCBMODEL::getModelLabel( const std::string& aFileNameUTF8, TRIPLET aScale, TDF_Label& aLabel, +bool STEP_PCB_MODEL::getModelLabel( const std::string& aFileNameUTF8, VECTOR3D aScale, TDF_Label& aLabel, bool aSubstituteModels, wxString* aErrorMessage ) { std::string model_key = aFileNameUTF8 + "_" + std::to_string( aScale.x ) @@ -1131,7 +740,8 @@ bool PCBMODEL::getModelLabel( const std::string& aFileNameUTF8, TRIPLET aScale, if( success ) { std::string altFileNameUTF8 = TO_UTF8( outFile.GetFullPath() ); - success = getModelLabel( altFileNameUTF8, TRIPLET( 1.0, 1.0, 1.0 ), aLabel, false ); + success = + getModelLabel( altFileNameUTF8, VECTOR3D( 1.0, 1.0, 1.0 ), aLabel, false ); } return success; @@ -1198,7 +808,7 @@ bool PCBMODEL::getModelLabel( const std::string& aFileNameUTF8, TRIPLET aScale, // named "model.wrl" and "model.stp" referring to different parts. // TODO: Fix model handling in v7. Default models should only be STP. // Have option to override this in DISPLAY. - if( getModelLabel( altFileNameUTF8, TRIPLET( 1.0, 1.0, 1.0 ), aLabel, false ) ) + if( getModelLabel( altFileNameUTF8, VECTOR3D( 1.0, 1.0, 1.0 ), aLabel, false ) ) { return true; } @@ -1245,8 +855,8 @@ bool PCBMODEL::getModelLabel( const std::string& aFileNameUTF8, TRIPLET aScale, } -bool PCBMODEL::getModelLocation( bool aBottom, DOUBLET aPosition, double aRotation, TRIPLET aOffset, - TRIPLET aOrientation, TopLoc_Location& aLocation ) +bool STEP_PCB_MODEL::getModelLocation( bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, VECTOR3D aOrientation, + TopLoc_Location& aLocation ) { // Order of operations: // a. aOrientation is applied -Z*-Y*-X @@ -1301,7 +911,7 @@ bool PCBMODEL::getModelLocation( bool aBottom, DOUBLET aPosition, double aRotati } -bool PCBMODEL::readIGES( Handle( TDocStd_Document )& doc, const char* fname ) +bool STEP_PCB_MODEL::readIGES( Handle( TDocStd_Document )& doc, const char* fname ) { IGESControl_Controller::Init(); IGESCAFControl_Reader reader; @@ -1340,7 +950,7 @@ bool PCBMODEL::readIGES( Handle( TDocStd_Document )& doc, const char* fname ) } -bool PCBMODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fname ) +bool STEP_PCB_MODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fname ) { STEPCAFControl_Reader reader; IFSelect_ReturnStatus stat = reader.ReadFile( fname ); @@ -1378,8 +988,8 @@ bool PCBMODEL::readSTEP( Handle( TDocStd_Document )& doc, const char* fname ) } -TDF_Label PCBMODEL::transferModel( Handle( TDocStd_Document )& source, - Handle( TDocStd_Document )& dest, TRIPLET aScale ) +TDF_Label STEP_PCB_MODEL::transferModel( Handle( TDocStd_Document )& source, + Handle( TDocStd_Document )& dest, VECTOR3D aScale ) { // transfer data from Source into a top level component of Dest gp_GTrsf scale_transform; @@ -1501,254 +1111,4 @@ TDF_Label PCBMODEL::transferModel( Handle( TDocStd_Document )& source, }; return component; -} - - -OUTLINE::OUTLINE() -{ - m_closed = false; - m_minDistance2 = MIN_LENGTH2; -} - - -OUTLINE::~OUTLINE() -{ -} - - -void OUTLINE::Clear() -{ - m_closed = false; - m_curves.clear(); -} - - -bool OUTLINE::AddSegment( const KICADCURVE& aCurve ) -{ - if( m_closed ) - return false; - - if( m_curves.empty() ) - { - m_curves.push_back( aCurve ); - - if( CURVE_CIRCLE == aCurve.m_form ) - m_closed = true; - - return true; - } - - if( CURVE_CIRCLE == aCurve.m_form ) - return false; - - // get the end points of the first curve - double spx0, spy0; - double epx0, epy0; - getEndPoints( m_curves.front(), spx0, spy0, epx0, epy0 ); - - // get the end points of the free curve - double spx1, spy1; - double epx1, epy1; - getEndPoints( aCurve, spx1, spy1, epx1, epy1 ); - - // check if the curve attaches to the front - double dx, dy; - dx = epx1 - spx0; - dy = epy1 - spy0; - - if( dx * dx + dy * dy < m_minDistance2 ) - { - m_curves.push_front( aCurve ); - m_closed = testClosed( m_curves.front(), m_curves.back() ); - return true; - } - else - { - dx = spx1 - spx0; - dy = spy1 - spy0; - - if( dx * dx + dy * dy < m_minDistance2 ) - { - KICADCURVE curve = aCurve; - reverseCurve( curve ); - m_curves.push_front( curve ); - m_closed = testClosed( m_curves.front(), m_curves.back() ); - return true; - } - } - - // check if the curve attaches to the back - getEndPoints( m_curves.back(), spx0, spy0, epx0, epy0 ); - dx = spx1 - epx0; - dy = spy1 - epy0; - - if( dx * dx + dy * dy < m_minDistance2 ) - { - m_curves.push_back( aCurve ); - m_closed = testClosed( m_curves.front(), m_curves.back() ); - return true; - } - else - { - dx = epx1 - epx0; - dy = epy1 - epy0; - - if( dx * dx + dy * dy < m_minDistance2 ) - { - KICADCURVE curve = aCurve; - reverseCurve( curve ); - m_curves.push_back( curve ); - m_closed = testClosed( m_curves.front(), m_curves.back() ); - return true; - } - } - - // this curve is not an end segment of the current loop - return false; -} - - -bool OUTLINE::MakeShape( TopoDS_Shape& aShape, double aThickness ) -{ - if( !aShape.IsNull() ) - return false; // there is already data in the shape object - - if( m_curves.empty() ) - return true; // succeeded in doing nothing - - if( !m_closed ) - return false; // the loop is not closed - - BRepBuilderAPI_MakeWire wire; - DOUBLET lastPoint; - getCurveEndPoint( m_curves.back(), lastPoint ); - - for( KICADCURVE& i : m_curves ) - { - bool success = false; - - try - { - success = addEdge( &wire, i, lastPoint ); - } - catch( const Standard_Failure& e ) - { - ReportMessage( wxString::Format( wxT( "Exception caught: %s\n" ), - e.GetMessageString() ) ); - success = false; - } - - if( !success ) - { - ReportMessage( wxString::Format( wxT( "failed to add edge: %s\n" - "last valid outline point: %f %f\n" ), - i.Describe().c_str(), - lastPoint.x, - lastPoint.y ) ); - return false; - } - } - - TopoDS_Face face = BRepBuilderAPI_MakeFace( wire ); - aShape = BRepPrimAPI_MakePrism( face, gp_Vec( 0, 0, aThickness ) ); - - if( aShape.IsNull() ) - { - ReportMessage( wxT( "failed to create a prismatic shape\n" ) ); - return false; - } - - return true; -} - - -bool OUTLINE::addEdge( BRepBuilderAPI_MakeWire* aWire, KICADCURVE& aCurve, DOUBLET& aLastPoint ) -{ - TopoDS_Edge edge; - DOUBLET endPoint; - getCurveEndPoint( aCurve, endPoint ); - - switch( aCurve.m_form ) - { - case CURVE_LINE: - edge = BRepBuilderAPI_MakeEdge( gp_Pnt( aLastPoint.x, aLastPoint.y, 0.0 ), - gp_Pnt( endPoint.x, endPoint.y, 0.0 ) ); - break; - - case CURVE_ARC: - { - gp_Circ arc( gp_Ax2( gp_Pnt( aCurve.m_start.x, aCurve.m_start.y, 0.0 ), - gp_Dir( 0.0, 0.0, 1.0 ) ), aCurve.m_radius ); - - gp_Pnt sa( aLastPoint.x, aLastPoint.y, 0.0 ); - gp_Pnt ea( endPoint.x, endPoint.y, 0.0 ); - - if( aCurve.m_angle < 0.0 ) - edge = BRepBuilderAPI_MakeEdge( arc, ea, sa ); - else - edge = BRepBuilderAPI_MakeEdge( arc, sa, ea ); - } - break; - - case CURVE_CIRCLE: - edge = BRepBuilderAPI_MakeEdge( - gp_Circ( gp_Ax2( gp_Pnt( aCurve.m_start.x, aCurve.m_start.y, 0.0 ), - gp_Dir( 0.0, 0.0, 1.0 ) ), aCurve.m_radius ) ); - break; - - case CURVE_BEZIER: - { - TColgp_Array1OfPnt poles( 0, 3 ); - gp_Pnt pt = gp_Pnt( aCurve.m_start.x, aCurve.m_start.y, 0.0 ); - - poles( 0 ) = pt; - pt = gp_Pnt( aCurve.m_bezierctrl1.x, aCurve.m_bezierctrl1.y, 0.0 ); - poles( 1 ) = pt; - pt = gp_Pnt( aCurve.m_bezierctrl2.x, aCurve.m_bezierctrl2.y, 0.0 ); - poles( 2 ) = pt; - pt = gp_Pnt( endPoint.x, endPoint.y, 0.0 ); - poles( 3 ) = pt; - - Geom_BezierCurve* bezier_curve = new Geom_BezierCurve( poles ); - edge = BRepBuilderAPI_MakeEdge( bezier_curve ); - } - break; - - default: - ReportMessage( wxString::Format( wxT( "unsupported curve type: %d\n" ), aCurve.m_form ) ); - return false; - } - - if( edge.IsNull() ) - return false; - - aLastPoint = endPoint; - aWire->Add( edge ); - - if( BRepBuilderAPI_DisconnectedWire == aWire->Error() ) - { - ReportMessage( wxT( "failed to add curve\n" ) ); - return false; - } - - return true; -} - - -bool OUTLINE::testClosed( const KICADCURVE& aFrontCurve, const KICADCURVE& aBackCurve ) -{ - double spx0, spy0, epx0, epy0; - getEndPoints( aFrontCurve, spx0, spy0, epx0, epy0 ); - - double spx1, spy1, epx1, epy1; - getEndPoints( aBackCurve, spx1, spy1, epx1, epy1 ); - - double dx = epx1 - spx0; - double dy = epy1 - spy0; - double r = dx * dx + dy * dy; - - if( r < m_minDistance2 ) - return true; - - return false; -} +} \ No newline at end of file diff --git a/pcbnew/exporters/step/pcb/oce_utils.h b/pcbnew/exporters/step/step_pcb_model.h similarity index 71% rename from pcbnew/exporters/step/pcb/oce_utils.h rename to pcbnew/exporters/step/step_pcb_model.h index 2980674a09..8928c311e7 100644 --- a/pcbnew/exporters/step/pcb/oce_utils.h +++ b/pcbnew/exporters/step/step_pcb_model.h @@ -30,9 +30,6 @@ #include #include #include -#include "base.h" -#include "kicadpcb.h" -#include "kicadcurve.h" #include #include @@ -41,65 +38,34 @@ #include #include +#include +#include +#include + +///< Default minimum distance between points to treat them as separate ones (mm) +static constexpr double STEPEXPORT_MIN_DISTANCE = 0.01; +static constexpr double STEPEXPORT_MIN_ACCEPTABLE_DISTANCE = 0.001; + +class PAD; typedef std::pair< std::string, TDF_Label > MODEL_DATUM; typedef std::map< std::string, TDF_Label > MODEL_MAP; -class KICADPAD; +extern void ReportMessage( const wxString& aMessage ); -class OUTLINE +class STEP_PCB_MODEL { public: - OUTLINE(); - virtual ~OUTLINE(); - - void Clear(); - - // attempt to add a curve to the outline; on success returns true - bool AddSegment( const KICADCURVE& aCurve ); - - bool IsClosed() - { - return m_closed; - } - - void SetMinSqDistance( double aDistance ) - { - m_minDistance2 = aDistance; - } - - bool MakeShape( TopoDS_Shape& aShape, double aThickness ); - -private: - bool addEdge( BRepBuilderAPI_MakeWire* aWire, KICADCURVE& aCurve, DOUBLET& aLastPoint ); - bool testClosed( const KICADCURVE& aFrontCurve, const KICADCURVE& aBackCurve ); - -public: - std::list< KICADCURVE > m_curves; // list of contiguous segments - -private: - bool m_closed; // set true if the loop is closed - double m_minDistance2; // min squared distance to treat points as separate entities (mm) -}; - - -class PCBMODEL -{ -public: - PCBMODEL( const wxString& aPcbName ); - virtual ~PCBMODEL(); - - // add an outline segment (must be in final position) - bool AddOutlineSegment( KICADCURVE* aCurve ); + STEP_PCB_MODEL( const wxString& aPcbName ); + virtual ~STEP_PCB_MODEL(); // add a pad hole or slot (must be in final position) - bool AddPadHole( const KICADPAD* aPad ); + bool AddPadHole( const PAD* aPad ); // add a component at the given position and orientation - bool AddComponent( const std::string& aFileName, const std::string& aRefDes, - bool aBottom, DOUBLET aPosition, double aRotation, - TRIPLET aOffset, TRIPLET aOrientation, TRIPLET aScale, - bool aSubstituteModels = true ); + bool AddComponent( const std::string& aFileName, const std::string& aRefDes, bool aBottom, + VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, + VECTOR3D aOrientation, VECTOR3D aScale, bool aSubstituteModels = true ); void SetBoardColor( double r, double g, double b ); @@ -112,8 +78,12 @@ public: // Set the minimum distance (in mm) to consider 2 points have the same coordinates void SetMinDistance( double aDistance ); + void SetMaxError( int aMaxError ) { m_maxError = aMaxError; } + // create the PCB model using the current outlines and drill holes - bool CreatePCB(); + bool CreatePCB( SHAPE_POLY_SET& aOutline ); + + bool MakeShape( TopoDS_Shape& aShape, const SHAPE_LINE_CHAIN& chain, double aThickness ); #ifdef SUPPORTS_IGES // write the assembly model in IGES format @@ -136,17 +106,17 @@ private: * @param aErrorMessage (can be nullptr) is an error message to be displayed on error. * @return true if successfully loaded, false on error. */ - bool getModelLabel( const std::string& aFileNameUTF8, TRIPLET aScale, TDF_Label& aLabel, + bool getModelLabel( const std::string& aFileNameUTF8, VECTOR3D aScale, TDF_Label& aLabel, bool aSubstituteModels, wxString* aErrorMessage = nullptr ); - bool getModelLocation( bool aBottom, DOUBLET aPosition, double aRotation, TRIPLET aOffset, - TRIPLET aOrientation, TopLoc_Location& aLocation ); + bool getModelLocation( bool aBottom, VECTOR2D aPosition, double aRotation, VECTOR3D aOffset, + VECTOR3D aOrientation, TopLoc_Location& aLocation ); bool readIGES( Handle( TDocStd_Document )& m_doc, const char* fname ); bool readSTEP( Handle( TDocStd_Document )& m_doc, const char* fname ); - TDF_Label transferModel( Handle( TDocStd_Document )& source, - Handle( TDocStd_Document )& dest, TRIPLET aScale ); + TDF_Label transferModel( Handle( TDocStd_Document )& source, Handle( TDocStd_Document ) & dest, + VECTOR3D aScale ); Handle( XCAFApp_Application ) m_app; Handle( TDocStd_Document ) m_doc; @@ -163,13 +133,13 @@ private: double m_minx; // leftmost curve point double m_minDistance2; // minimum squared distance between items (mm) - std::list::iterator m_mincurve; // iterator to the leftmost curve - std::list m_curves; std::vector m_cutouts; /// Name of the PCB, which will most likely be the file name of the path. wxString m_pcbName; + + int m_maxError; }; #endif // OCE_VIS_OCE_UTILS_H diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 96466f93d5..f222996b64 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -39,6 +39,7 @@ #include #include #include +#include class LINE_READER; class EDA_3D_CANVAS; @@ -87,11 +88,6 @@ public: { } - struct VECTOR3D - { - double x, y, z; - }; - VECTOR3D m_Scale; ///< 3D model scaling factor (dimensionless) VECTOR3D m_Rotation; ///< 3D model rotation (degrees) VECTOR3D m_Offset; ///< 3D model offset (mm) diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 41162650b2..21c1787f42 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -19,7 +19,6 @@ */ #include "pcbnew_jobs_handler.h" -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include "gerber_placefile_writer.h" #include #include @@ -65,23 +65,39 @@ int PCBNEW_JOBS_HANDLER::JobExportStep( JOB* aJob ) if( aStepJob == nullptr ) return CLI::EXIT_CODES::ERR_UNKNOWN; - KICAD2MCAD_PRMS params; + if( aJob->IsCli() ) + wxPrintf( _( "Loading board\n" ) ); + + BOARD* brd = LoadBoard( aStepJob->m_filename ); + + if( aStepJob->m_outputFile.IsEmpty() ) + { + wxFileName fn = brd->GetFileName(); + fn.SetName( fn.GetName() ); + fn.SetExt( wxS( "step" ) ); + + aStepJob->m_outputFile = fn.GetFullName(); + } + + EXPORTER_STEP_PARAMS params; + params.m_includeExcludedBom = aStepJob->m_includeExcludedBom; + params.m_minDistance = aStepJob->m_minDistance; + params.m_overwrite = aStepJob->m_overwrite; + params.m_substModels = aStepJob->m_substModels; + params.m_origin = VECTOR2D( aStepJob->m_xOrigin, aStepJob->m_yOrigin ); params.m_useDrillOrigin = aStepJob->m_useDrillOrigin; params.m_useGridOrigin = aStepJob->m_useGridOrigin; - params.m_overwrite = aStepJob->m_overwrite; - params.m_includeVirtual = aStepJob->m_includeVirtual; - params.m_filename = aStepJob->m_filename; - params.m_outputFile = aStepJob->m_outputFile; - params.m_xOrigin = aStepJob->m_xOrigin; - params.m_yOrigin = aStepJob->m_yOrigin; - params.m_minDistance = aStepJob->m_minDistance; - params.m_substModels = aStepJob->m_substModels; + params.m_boardOnly = aStepJob->m_boardOnly; - // we might need the lifetime of the converter to continue until frame destruction - // due to the gui parameter - KICAD2STEP* converter = new KICAD2STEP( params ); + EXPORTER_STEP stepExporter( brd, params ); + stepExporter.m_outputFile = aStepJob->m_outputFile; - return converter->Run(); + if( !stepExporter.Export() ) + { + return CLI::EXIT_CODES::ERR_UNKNOWN; + } + + return CLI::EXIT_CODES::OK; } diff --git a/pcbnew/plugins/altium/altium_parser_pcb.h b/pcbnew/plugins/altium/altium_parser_pcb.h index 59a06a214e..031c4a4b2d 100644 --- a/pcbnew/plugins/altium/altium_parser_pcb.h +++ b/pcbnew/plugins/altium/altium_parser_pcb.h @@ -452,7 +452,7 @@ struct AMODEL wxString id; bool isEmbedded; - FP_3DMODEL::VECTOR3D rotation; + VECTOR3D rotation; explicit AMODEL( ALTIUM_PARSER& aReader ); }; @@ -564,8 +564,8 @@ struct ACOMPONENTBODY6 wxString modelId; bool modelIsEmbedded; - FP_3DMODEL::VECTOR3D modelPosition; - FP_3DMODEL::VECTOR3D modelRotation; + VECTOR3D modelPosition; + VECTOR3D modelRotation; double rotation; double bodyOpacity; diff --git a/qa/unittests/CMakeLists.txt b/qa/unittests/CMakeLists.txt index 43ad49a71d..d74b7d57bb 100644 --- a/qa/unittests/CMakeLists.txt +++ b/qa/unittests/CMakeLists.txt @@ -24,5 +24,4 @@ add_subdirectory( common ) add_subdirectory( gerbview ) add_subdirectory( eeschema ) add_subdirectory( libs ) -add_subdirectory( pcbnew ) -add_subdirectory( utils/kicad2step ) +add_subdirectory( pcbnew ) \ No newline at end of file diff --git a/qa/unittests/libs/kimath/math/test_vector3.cpp b/qa/unittests/libs/kimath/math/test_vector3.cpp index 64c63b7a7e..5b03bae2e5 100644 --- a/qa/unittests/libs/kimath/math/test_vector3.cpp +++ b/qa/unittests/libs/kimath/math/test_vector3.cpp @@ -51,5 +51,33 @@ BOOST_AUTO_TEST_CASE( test_dot_product, *boost::unit_test::tolerance( 0.000001 ) BOOST_CHECK( v1.Dot( v2 ) == 1 ); } +BOOST_AUTO_TEST_CASE( test_equality_ops, *boost::unit_test::tolerance( 0.000001 ) ) +{ + VECTOR3I v1( 1, 1, 1 ); + VECTOR3I v2( 2, 2, 2 ); + VECTOR3I v3( 1, 1, 1 ); + + BOOST_CHECK( v1 == v3 ); + BOOST_CHECK( v1 != v2 ); +} + +BOOST_AUTO_TEST_CASE( test_scalar_multiply, *boost::unit_test::tolerance( 0.000001 ) ) +{ + VECTOR3I v1( 1, 1, 1 ); + + v1 *= 5; + + BOOST_CHECK( v1 == VECTOR3( 5, 5, 5 ) ); +} + +BOOST_AUTO_TEST_CASE( test_scalar_divide, *boost::unit_test::tolerance( 0.000001 ) ) +{ + VECTOR3I v1( 5, 5, 5 ); + + v1 /= 5; + + BOOST_CHECK( v1 == VECTOR3( 1, 1, 1 ) ); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/qa/unittests/utils/kicad2step/CMakeLists.txt b/qa/unittests/utils/kicad2step/CMakeLists.txt deleted file mode 100644 index abffb6dbfa..0000000000 --- a/qa/unittests/utils/kicad2step/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -# This program source code file is part of KiCad, a free EDA CAD application. -# -# Copyright (C) 2019 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 - -# kicad2step s-expr handling routines - -if( NOT TARGET kicad2step_lib ) - # Can't build this test without the underlying library - return() -endif() - -set( K2S_TEST_SRCS - test_module.cpp - pcb/test_base.cpp -) - -if( MINGW ) - list( APPEND K2S_TEST_SRCS ${CMAKE_SOURCE_DIR}/common/streamwrapper.cpp ) -endif( MINGW ) - -if( WIN32 ) - # We want to declare a resource manifest on Windows to enable UTF8 mode - # Without UTF8 mode, some random IO tests may fail, we set the active code page on normal kicad to UTF8 as well - if( MINGW ) - # QA_KICAD2STEP_RESOURCES variable is set by the macro. - mingw_resource_compiler( qa_kicad2step ) - else() - set( QA_KICAD2STEP_RESOURCES ${CMAKE_SOURCE_DIR}/resources/msw/qa_kicad2step.rc ) - endif() -endif() - -add_executable( qa_kicad2step - ${K2S_TEST_SRCS} - ${QA_KICAD2STEP_RESOURCES} ) - -target_link_libraries( qa_kicad2step - kicad2step_lib - qa_utils - ${wxWidgets_LIBRARIES} -) - -target_include_directories( qa_sexpr PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} -) - -kicad_add_boost_test( qa_kicad2step qa_kicad2step ) diff --git a/qa/unittests/utils/kicad2step/pcb/test_base.cpp b/qa/unittests/utils/kicad2step/pcb/test_base.cpp deleted file mode 100644 index 4a4f392973..0000000000 --- a/qa/unittests/utils/kicad2step/pcb/test_base.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2019 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 - */ - -/** - * @file - * Test suite for PCB "base" sexpr parsing - */ - -#include - -// Code under test -#include - -#include - -#include - - -namespace BOOST_TEST_PRINT_NAMESPACE_OPEN -{ -template <> -struct print_log_value -{ - inline void operator()( std::ostream& os, DOUBLET const& o ) - { - os << "DOUBLET[ " << o.x << ", " << o.y << " ]"; - } -}; -} -BOOST_TEST_PRINT_NAMESPACE_CLOSE - - -/** - * Radians from degrees - */ -constexpr double DegToRad( double aDeg ) -{ - return aDeg * M_PI / 180.0; -} - - -class TEST_PCB_BASE_FIXTURE -{ -public: - SEXPR::PARSER m_parser; -}; - - -/** - * Declare the test suite - */ -BOOST_FIXTURE_TEST_SUITE( PcbBase, TEST_PCB_BASE_FIXTURE ) - -struct TEST_2D_POS_ROT -{ - std::string m_sexp; - bool m_valid; - DOUBLET m_exp_pos; - double m_exp_rot; -}; - -/** - * Test the Get2DPositionAndRotation function - */ -BOOST_AUTO_TEST_CASE( SexprTo2DPosAndRot ) -{ - const std::vector cases = { - { - "(at 0 0 0)", - true, - { 0.0, 0.0 }, - 0.0, - }, - { - "(at 3.14 4.12 90.4)", - true, - { 3.14, 4.12 }, - DegToRad( 90.4 ), - }, - { - "(at 3.14)", // no enough params - false, - {}, - {}, - }, - { - "(att 3.14 4.12 90.4)", // bad symbol name - false, - {}, - {}, - }, - }; - - for( const auto& c : cases ) - { - BOOST_TEST_CONTEXT( c.m_sexp ) - { - DOUBLET gotPos; - double gotRot; - - std::unique_ptr sexpr( m_parser.Parse( c.m_sexp ) ); - - const bool ret = Get2DPositionAndRotation( sexpr.get(), gotPos, gotRot ); - - BOOST_CHECK_EQUAL( ret, c.m_valid ); - - if( !ret ) - continue; - - const double tolPercent = 0.00001; // seems small enough - - BOOST_CHECK_CLOSE( gotPos.x, c.m_exp_pos.x, tolPercent ); - BOOST_CHECK_CLOSE( gotPos.y, c.m_exp_pos.y, tolPercent ); - BOOST_CHECK_CLOSE( gotRot, c.m_exp_rot, tolPercent ); - } - } -} - -struct TEST_LAYER_NAME -{ - std::string m_sexp; - bool m_valid; - std::string m_layer; -}; - -/** - * Test the layer list parser. - */ -BOOST_AUTO_TEST_CASE( TestGetLayerName ) -{ - const std::vector cases = { - { - // Quoted string - OK - "(layer \"Edge.Cuts\")", - true, - "Edge.Cuts", - }, - { - // Old KiCads exported without quotes (so, as symbols) - "(layer Edge.Cuts)", - true, - "Edge.Cuts", - }, - { - // No atoms - "(layer)", - false, - {}, - }, - { - // Too many atoms in list - "(layer \"Edge.Cuts\" 2)", - false, - {}, - }, - { - // Wrong atom type - "(layer 2)", - false, - {}, - }, - }; - - for( const auto& c : cases ) - { - BOOST_TEST_CONTEXT( c.m_sexp ) - { - std::unique_ptr sexpr( m_parser.Parse( c.m_sexp ) ); - - const std::optional ret = GetLayerName( *sexpr ); - - BOOST_CHECK_EQUAL( !!ret, c.m_valid ); - - if( ret ) - { - BOOST_CHECK_EQUAL( *ret, c.m_layer ); - } - } - } -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/qa/unittests/utils/kicad2step/test_module.cpp b/qa/unittests/utils/kicad2step/test_module.cpp deleted file mode 100644 index de2d16561c..0000000000 --- a/qa/unittests/utils/kicad2step/test_module.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2019 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 - */ - -/** - * Main file for the Eeschema tests to be compiled - */ -#include - -#include - - -bool init_unit_test() -{ - boost::unit_test::framework::master_test_suite().p_name.value = "S-expression module tests"; - return wxInitialize(); -} - - -int main( int argc, char* argv[] ) -{ - int ret = boost::unit_test::unit_test_main( &init_unit_test, argc, argv ); - - // This causes some glib warnings on GTK3 (http://trac.wxwidgets.org/ticket/18274) - // but without it, Valgrind notices a lot of leaks from WX - wxUninitialize(); - - return ret; -} \ No newline at end of file