Refactor step export to use our normal board processing routines
This commit is contained in:
parent
e109b5883f
commit
3dd2ae762d
|
@ -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;
|
||||
|
|
|
@ -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<bool>( ARG_DRILL_ORIGIN );
|
||||
step->m_useGridOrigin = m_argParser.get<bool>( ARG_GRID_ORIGIN );
|
||||
step->m_includeVirtual = !m_argParser.get<bool>( ARG_NO_VIRTUAL );
|
||||
step->m_includeExcludedBom = !m_argParser.get<bool>( ARG_NO_VIRTUAL );
|
||||
step->m_substModels = m_argParser.get<bool>( ARG_SUBST_MODELS );
|
||||
step->m_overwrite = m_argParser.get<bool>( ARG_FORCE );
|
||||
step->m_filename = FROM_UTF8( m_argParser.get<std::string>( ARG_INPUT ).c_str() );
|
||||
step->m_outputFile = FROM_UTF8( m_argParser.get<std::string>( ARG_OUTPUT ).c_str() );
|
||||
step->m_boardOnly = m_argParser.get<bool>( ARG_BOARD_ONLY );
|
||||
|
||||
wxString userOrigin = FROM_UTF8( m_argParser.get<std::string>( ARG_USER_ORIGIN ).c_str() );
|
||||
if( !userOrigin.IsEmpty() )
|
||||
|
|
|
@ -102,6 +102,9 @@ public:
|
|||
|
||||
///< Not equality operator
|
||||
bool operator!=( const VECTOR3<T>& aVector ) const;
|
||||
|
||||
VECTOR3<T>& operator*=( T val );
|
||||
VECTOR3<T>& operator/=( T val );
|
||||
};
|
||||
|
||||
|
||||
|
@ -175,6 +178,28 @@ bool VECTOR3<T>::operator!=( VECTOR3<T> const& aVector ) const
|
|||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
VECTOR3<T>& VECTOR3<T>::operator*=( T aScalar )
|
||||
{
|
||||
x = x * aScalar;
|
||||
y = y * aScalar;
|
||||
z = z * aScalar;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
VECTOR3<T>& VECTOR3<T>::operator/=( T aScalar )
|
||||
{
|
||||
x = x / aScalar;
|
||||
y = y / aScalar;
|
||||
z = z / aScalar;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/* Default specializations */
|
||||
typedef VECTOR3<double> VECTOR3D;
|
||||
typedef VECTOR3<int> VECTOR3I;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -47,9 +47,6 @@
|
|||
#include <filename_resolver.h>
|
||||
|
||||
|
||||
#include <kicad2step.h>
|
||||
|
||||
|
||||
class DIALOG_EXPORT_STEP : public DIALOG_EXPORT_STEP_BASE
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -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
|
||||
$<TARGET_PROPERTY:gzip-hpp,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
)
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <footprint.h>
|
||||
#include <fp_lib_table.h>
|
||||
|
||||
#include <pgm_base.h>
|
||||
#include <base_units.h>
|
||||
#include <filename_resolver.h>
|
||||
|
||||
#include <Message.hxx> // OpenCascade messenger
|
||||
#include <Message_PrinterOStream.hxx> // OpenCascade output messenger
|
||||
#include <Standard_Failure.hxx> // In open cascade
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include <wx/crt.h>
|
||||
|
||||
#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<FILENAME_RESOLVER>();
|
||||
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<wxString> 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<STEP_PCB_MODEL>( 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;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <geometry/shape_poly_set.h>
|
||||
#include <gal/color4d.h>
|
||||
|
||||
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<FILENAME_RESOLVER> m_resolver;
|
||||
|
||||
bool m_error;
|
||||
bool m_fail;
|
||||
|
||||
bool m_hasDrillOrigin;
|
||||
bool m_hasGridOrigin;
|
||||
BOARD* m_board;
|
||||
std::unique_ptr<STEP_PCB_MODEL> 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
|
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of kicad2mcad
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <wx/app.h>
|
||||
#include <wx/cmdline.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/crt.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <cli/exit_codes.h>
|
||||
#include <pgm_base.h>
|
||||
#include "kicad2step.h"
|
||||
#include "pcb/kicadpcb.h"
|
||||
#include <Message.hxx> // OpenCascade messenger
|
||||
#include <Message_PrinterOStream.hxx> // OpenCascade output messenger
|
||||
#include <Standard_Failure.hxx> // In open cascade
|
||||
|
||||
#include <Standard_Version.hxx>
|
||||
|
||||
#include <locale_io.h>
|
||||
|
||||
#define OCC_VERSION_MIN 0x070500
|
||||
|
||||
#if OCC_VERSION_HEX < OCC_VERSION_MIN
|
||||
#include <Message_Messenger.hxx>
|
||||
#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)
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of kicad2mcad
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <wx/string.h>
|
||||
#include <import_export.h>
|
||||
|
||||
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
|
|
@ -1,299 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <wx/log.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#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<std::string> GetLayerName( const SEXPR::SEXPR& aLayerElem )
|
||||
{
|
||||
std::optional<std::string> 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;
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <optional>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
///< 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<std::string> GetLayerName( const SEXPR::SEXPR& aLayerElem );
|
||||
|
||||
#endif // KICADBASE_H
|
|
@ -1,295 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <sexpr/sexpr.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <../../../libs/kimath/include/geometry/shape_arc.h>
|
||||
#include <sexpr/sexpr_parser.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
|
||||
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<SEXPR::SEXPR> 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<std::string> 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 << "<invalid curve type>";
|
||||
break;
|
||||
}
|
||||
|
||||
return desc.str();
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
#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<DOUBLET> m_poly; // vector of polygon points
|
||||
};
|
||||
|
||||
#endif // KICADCURVE_H
|
|
@ -1,474 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <filename_resolver.h>
|
||||
#include "kicadcurve.h"
|
||||
#include "kicadmodel.h"
|
||||
#include "kicadpad.h"
|
||||
#include "oce_utils.h"
|
||||
|
||||
#include <sexpr/sexpr.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <wx/filename.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <Standard_Failure.hxx>
|
||||
|
||||
|
||||
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<KICADCURVE> rect = std::make_unique<KICADCURVE>();
|
||||
|
||||
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<wxString> 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;
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
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
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <sexpr/sexpr.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 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
|
|
@ -1,192 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <sexpr/sexpr.h>
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
#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
|
|
@ -1,662 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <sexpr/sexpr.h>
|
||||
#include <sexpr/sexpr_parser.h>
|
||||
|
||||
#include <wx/wxcrtvararg.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <wx_filename.h>
|
||||
#include <pgm_base.h>
|
||||
|
||||
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<SEXPR::SEXPR> 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<KICADCURVE> poly = std::make_unique<KICADCURVE>();
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
|
||||
* 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 <wx/string.h>
|
||||
#include <wx/colour.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filename_resolver.h>
|
||||
#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<std::string, int> 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<KICADFOOTPRINT*> m_footprints;
|
||||
std::vector<KICADCURVE*> m_curves;
|
||||
wxString m_pcbName;
|
||||
};
|
||||
|
||||
|
||||
#endif // KICADPCB_H
|
File diff suppressed because it is too large
Load Diff
|
@ -30,9 +30,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "base.h"
|
||||
#include "kicadpcb.h"
|
||||
#include "kicadcurve.h"
|
||||
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <TDocStd_Document.hxx>
|
||||
|
@ -41,65 +38,34 @@
|
|||
#include <TopoDS_Shape.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <math/vector3.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
///< 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<KICADCURVE>::iterator m_mincurve; // iterator to the leftmost curve
|
||||
|
||||
std::list<KICADCURVE> m_curves;
|
||||
std::vector<TopoDS_Shape> 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
|
|
@ -39,6 +39,7 @@
|
|||
#include <pcb_item_containers.h>
|
||||
#include <fp_text.h>
|
||||
#include <functional>
|
||||
#include <math/vector3.h>
|
||||
|
||||
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)
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
|
||||
#include "pcbnew_jobs_handler.h"
|
||||
#include <kicad2step.h>
|
||||
#include <jobs/job_export_pcb_gerber.h>
|
||||
#include <jobs/job_export_pcb_drill.h>
|
||||
#include <jobs/job_export_pcb_dxf.h>
|
||||
|
@ -32,6 +31,7 @@
|
|||
#include <plotters/plotter_gerber.h>
|
||||
#include <plotters/plotters_pslike.h>
|
||||
#include <exporters/place_file_exporter.h>
|
||||
#include <exporters/step/exporter_step.h>
|
||||
#include "gerber_placefile_writer.h"
|
||||
#include <pgm_base.h>
|
||||
#include <pcbplot.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 )
|
|
@ -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()
|
||||
|
|
|
@ -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 )
|
|
@ -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 <qa_utils/wx_utils/unit_test_utils.h>
|
||||
|
||||
// Code under test
|
||||
#include <pcb/base.h>
|
||||
|
||||
#include <sexpr/sexpr_parser.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace BOOST_TEST_PRINT_NAMESPACE_OPEN
|
||||
{
|
||||
template <>
|
||||
struct print_log_value<DOUBLET>
|
||||
{
|
||||
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<TEST_2D_POS_ROT> 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::SEXPR> 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<TEST_LAYER_NAME> 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::SEXPR> sexpr( m_parser.Parse( c.m_sexp ) );
|
||||
|
||||
const std::optional<std::string> ret = GetLayerName( *sexpr );
|
||||
|
||||
BOOST_CHECK_EQUAL( !!ret, c.m_valid );
|
||||
|
||||
if( ret )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( *ret, c.m_layer );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -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 <boost/test/unit_test.hpp>
|
||||
|
||||
#include <wx/init.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue