ADDED: Export inner PCB copper layers to STEP / BREP / GLTF.

Also adds options to exclude board body and components.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/16855
This commit is contained in:
Alex Shvartzkop 2024-04-22 03:39:43 +03:00
parent 6a73c7ffb3
commit c68e3ceb44
12 changed files with 575 additions and 274 deletions

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2023-2024 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
@ -38,9 +38,12 @@ JOB_EXPORT_PCB_3D::JOB_EXPORT_PCB_3D( bool aIsCli ) :
m_yOrigin( 0.0 ),
// max dist to chain 2 items (lines or curves) to build the board outlines
m_BoardOutlinesChainingEpsilon( 0.01 ), // 0.01 mm is a good value
m_exportTracks( false ), // Time consuming if true
m_exportZones( false ), // Time consuming if true
m_fuseShapes( false ), // Time consuming if true
m_exportBoardBody( true ),
m_exportComponents( true ),
m_exportTracks( false ),
m_exportZones( false ),
m_exportInnerCopper( false ),
m_fuseShapes( false ),
m_format( JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN ),
m_vrmlUnits( JOB_EXPORT_PCB_3D::VRML_UNITS::METERS ),
m_vrmlModelDir( wxEmptyString ),

View File

@ -2,7 +2,7 @@
* 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) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 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
@ -61,8 +61,11 @@ public:
double m_xOrigin;
double m_yOrigin;
double m_BoardOutlinesChainingEpsilon;
bool m_exportBoardBody;
bool m_exportComponents;
bool m_exportTracks;
bool m_exportZones;
bool m_exportInnerCopper;
bool m_fuseShapes;
JOB_EXPORT_PCB_3D::FORMAT m_format;

View File

@ -2,7 +2,7 @@
* 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) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2024 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
@ -37,8 +37,11 @@
#define ARG_MIN_DISTANCE "--min-distance"
#define ARG_USER_ORIGIN "--user-origin"
#define ARG_BOARD_ONLY "--board-only"
#define ARG_NO_BOARD_BODY "--no-board-body"
#define ARG_NO_COMPONENTS "--no-components"
#define ARG_INCLUDE_TRACKS "--include-tracks"
#define ARG_INCLUDE_ZONES "--include-zones"
#define ARG_INCLUDE_INNER_COPPER "--include-inner-copper"
#define ARG_FUSE_SHAPES "--fuse-shapes"
#define ARG_NO_OPTIMIZE_STEP "--no-optimize-step"
#define ARG_FORMAT "--format"
@ -103,15 +106,26 @@ CLI::PCB_EXPORT_3D_COMMAND::PCB_EXPORT_3D_COMMAND( const std::string& aNa
.help( UTF8STDSTR( _( "Only generate a board with no components" ) ) )
.flag();
m_argParser.add_argument( ARG_NO_BOARD_BODY )
.help( UTF8STDSTR( _( "Exclude board body" ) ) )
.flag();
m_argParser.add_argument( ARG_NO_COMPONENTS )
.help( UTF8STDSTR( _( "Exclude 3D models for components" ) ) )
.flag();
m_argParser.add_argument( ARG_INCLUDE_TRACKS )
.help( UTF8STDSTR( _( "Export tracks" ) ) )
.implicit_value( true )
.default_value( false );
.flag();
m_argParser.add_argument( ARG_INCLUDE_ZONES )
.help( UTF8STDSTR( _( "Export zones" ) ) )
.flag();
m_argParser.add_argument( ARG_INCLUDE_INNER_COPPER )
.help( UTF8STDSTR( _( "Export elements on inner copper layers" ) ) )
.flag();
m_argParser.add_argument( ARG_FUSE_SHAPES )
.help( UTF8STDSTR( _( "Fuse overlapping geometry together" ) ) )
.flag();
@ -166,8 +180,11 @@ int CLI::PCB_EXPORT_3D_COMMAND::doPerform( KIWAY& aKiway )
step->m_useDrillOrigin = m_argParser.get<bool>( ARG_DRILL_ORIGIN );
step->m_useGridOrigin = m_argParser.get<bool>( ARG_GRID_ORIGIN );
step->m_substModels = m_argParser.get<bool>( ARG_SUBST_MODELS );
step->m_exportBoardBody = !m_argParser.get<bool>( ARG_NO_BOARD_BODY );
step->m_exportComponents = !m_argParser.get<bool>( ARG_NO_COMPONENTS );
step->m_exportTracks = m_argParser.get<bool>( ARG_INCLUDE_TRACKS );
step->m_exportZones = m_argParser.get<bool>( ARG_INCLUDE_ZONES );
step->m_exportInnerCopper = m_argParser.get<bool>( ARG_INCLUDE_INNER_COPPER );
step->m_fuseShapes = m_argParser.get<bool>( ARG_FUSE_SHAPES );
step->m_boardOnly = m_argParser.get<bool>( ARG_BOARD_ONLY );
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 Cirilo Bernardo
* Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2024 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
@ -117,9 +117,12 @@ private:
bool m_noUnspecified; // remember last preference for No Unspecified Component
bool m_noDNP; // remember last preference for No DNP Component
static bool m_optimizeStep; // remember last preference for Optimize STEP file (stored only for the session)
static bool m_exportBoardBody; // remember last preference to export board body (stored only for the session)
static bool m_exportComponents; // remember last preference to export components (stored only for the session)
static bool m_exportTracks; // remember last preference to export tracks (stored only for the session)
static bool m_exportZones; // remember last preference to export tracks (stored only for the session)
static bool m_fuseShapes; // remember last preference to export tracks (stored only for the session)
static bool m_exportZones; // remember last preference to export zones (stored only for the session)
static bool m_fuseShapes; // remember last preference to fuse shapes (stored only for the session)
static bool m_exportInnerCopper; // remember last preference to export inner layers (stored only for the session)
wxString m_boardPath; // path to the exported board file
static int m_toleranceLastChoice; // Store m_tolerance option during a session
};
@ -127,8 +130,11 @@ private:
int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
bool DIALOG_EXPORT_STEP::m_optimizeStep = true;
bool DIALOG_EXPORT_STEP::m_exportBoardBody = true;
bool DIALOG_EXPORT_STEP::m_exportComponents = true;
bool DIALOG_EXPORT_STEP::m_exportTracks = false;
bool DIALOG_EXPORT_STEP::m_exportZones = false;
bool DIALOG_EXPORT_STEP::m_exportInnerCopper = false;
bool DIALOG_EXPORT_STEP::m_fuseShapes = false;
DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
@ -180,8 +186,11 @@ DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString&
m_noDNP = cfg->m_ExportStep.no_dnp;
m_cbOptimizeStep->SetValue( m_optimizeStep );
m_cbExportBody->SetValue( m_exportBoardBody );
m_cbExportComponents->SetValue( m_exportComponents );
m_cbExportTracks->SetValue( m_exportTracks );
m_cbExportZones->SetValue( m_exportZones );
m_cbExportInnerCopper->SetValue( m_exportInnerCopper );
m_cbFuseShapes->SetValue( m_fuseShapes );
m_cbRemoveUnspecified->SetValue( m_noUnspecified );
m_cbRemoveDNP->SetValue( m_noDNP );
@ -276,8 +285,11 @@ DIALOG_EXPORT_STEP::~DIALOG_EXPORT_STEP()
m_toleranceLastChoice = m_choiceTolerance->GetSelection();
m_optimizeStep = m_cbOptimizeStep->GetValue();
m_exportBoardBody = m_cbExportBody->GetValue();
m_exportComponents = m_cbExportComponents->GetValue();
m_exportTracks = m_cbExportTracks->GetValue();
m_exportZones = m_cbExportZones->GetValue();
m_exportInnerCopper = m_cbExportInnerCopper->GetValue();
m_fuseShapes = m_cbFuseShapes->GetValue();
}
@ -385,8 +397,11 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
double tolerance; // default value in mm
m_toleranceLastChoice = m_choiceTolerance->GetSelection();
m_optimizeStep = m_cbOptimizeStep->GetValue();
m_exportBoardBody = m_cbExportBody->GetValue();
m_exportComponents = m_cbExportComponents->GetValue();
m_exportTracks = m_cbExportTracks->GetValue();
m_exportZones = m_cbExportZones->GetValue();
m_exportInnerCopper = m_cbExportInnerCopper->GetValue();
m_fuseShapes = m_cbFuseShapes->GetValue();
switch( m_choiceTolerance->GetSelection() )
@ -475,12 +490,21 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
if( !m_optimizeStep )
cmdK2S.Append( wxT( " --no-optimize-step" ) );
if( !m_exportBoardBody )
cmdK2S.Append( wxT( " --no-board-body" ) );
if( !m_exportComponents )
cmdK2S.Append( wxT( " --no-components" ) );
if( m_exportTracks )
cmdK2S.Append( wxT( " --include-tracks" ) );
if( m_exportZones )
cmdK2S.Append( wxT( " --include-zones" ) );
if( m_exportInnerCopper )
cmdK2S.Append( wxT( " --include-inner-copper" ) );
if( m_fuseShapes )
cmdK2S.Append( wxT( " --fuse-shapes" ) );

View File

@ -151,16 +151,25 @@ DIALOG_EXPORT_STEP_BASE::DIALOG_EXPORT_STEP_BASE( wxWindow* parent, wxWindowID i
m_staticline1 = new wxStaticLine( sbOtherOptions->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
sbOtherOptions->Add( m_staticline1, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
m_cbExportBody = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export board body"), wxDefaultPosition, wxDefaultSize, 0 );
sbOtherOptions->Add( m_cbExportBody, 0, wxBOTTOM|wxRIGHT, 5 );
m_cbExportComponents = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export components"), wxDefaultPosition, wxDefaultSize, 0 );
sbOtherOptions->Add( m_cbExportComponents, 0, wxBOTTOM|wxRIGHT, 5 );
m_cbExportTracks = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export tracks, pads and vias"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbExportTracks->SetToolTip( _("Export tracks, pads and vias on external copper layers.") );
sbOtherOptions->Add( m_cbExportTracks, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
sbOtherOptions->Add( m_cbExportTracks, 0, wxBOTTOM|wxRIGHT, 5 );
m_cbExportZones = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export zones"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbExportZones->SetToolTip( _("Export zones on external copper layers.") );
sbOtherOptions->Add( m_cbExportZones, 0, wxBOTTOM|wxRIGHT, 5 );
m_cbExportInnerCopper = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export inner copper layers"), wxDefaultPosition, wxDefaultSize, 0 );
sbOtherOptions->Add( m_cbExportInnerCopper, 0, wxBOTTOM|wxEXPAND|wxRIGHT, 5 );
m_cbFuseShapes = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Fuse shapes (time consuming)"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbFuseShapes->SetToolTip( _("Combine intersecting geometry into one shape.") );

View File

@ -1496,7 +1496,137 @@
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxTOP|wxBOTTOM|wxRIGHT</property>
<property name="flag">wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Export board body</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbExportBody</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Export components</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbExportComponents</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
@ -1624,6 +1754,71 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxEXPAND|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Export inner copper layers</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbExportInnerCopper</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT</property>

View File

@ -68,8 +68,11 @@ class DIALOG_EXPORT_STEP_BASE : public DIALOG_SHIM
wxCheckBox* m_cbOptimizeStep;
wxCheckBox* m_cbExportCompound_hidden;
wxStaticLine* m_staticline1;
wxCheckBox* m_cbExportBody;
wxCheckBox* m_cbExportComponents;
wxCheckBox* m_cbExportTracks;
wxCheckBox* m_cbExportZones;
wxCheckBox* m_cbExportInnerCopper;
wxCheckBox* m_cbFuseShapes;
wxCheckBox* m_cbExportSilkscreen_hidden;
wxCheckBox* m_cbExportSoldermask_hidden;

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2022 Mark Roszko <mark.roszko@gmail.com>
* Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2016-2024 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
@ -143,8 +143,7 @@ EXPORTER_STEP::EXPORTER_STEP( BOARD* aBoard, const EXPORTER_STEP_PARAMS& aParams
m_fail( false ),
m_warn( false ),
m_board( aBoard ),
m_pcbModel( nullptr ),
m_boardThickness( DEFAULT_BOARD_THICKNESS_MM )
m_pcbModel( nullptr )
{
m_solderMaskColor = COLOR4D( 0.08, 0.20, 0.14, 0.83 );
m_copperColor = COLOR4D( 0.7, 0.61, 0.0, 1.0 );
@ -177,7 +176,7 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri
if( m_pcbModel->AddPadHole( pad, aOrigin ) )
hasdata = true;
if( ExportTracksAndVias() )
if( m_params.m_exportTracksVias )
{
if( m_pcbModel->AddPadShape( pad, aOrigin ) )
hasdata = true;
@ -185,17 +184,18 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri
}
// Build 3D shapes of the footprint graphic items on external layers:
if( ExportTracksAndVias() )
if( m_params.m_exportTracksVias )
{
int maxError = m_board->GetDesignSettings().m_MaxError;
aFootprint->TransformFPShapesToPolySet( m_top_copper_shapes, F_Cu, 0, maxError, ERROR_INSIDE,
false, /* include text */
true, /* include shapes */
false /* include private items */ );
aFootprint->TransformFPShapesToPolySet( m_bottom_copper_shapes, B_Cu, 0, maxError, ERROR_INSIDE,
false, /* include text */
true, /* include shapes */
false /* include private items */ );
for( PCB_LAYER_ID pcblayer : aFootprint->GetLayerSet().CuStack() )
{
aFootprint->TransformFPShapesToPolySet( m_poly_copper_shapes[pcblayer], pcblayer, 0,
maxError, ERROR_INSIDE,
false, /* include text */
true, /* include shapes */
false /* include private items */ );
}
}
if( ( !(aFootprint->GetAttributes() & (FP_THROUGH_HOLE|FP_SMD)) ) && !m_params.m_includeUnspecified )
@ -234,7 +234,7 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri
}
// Exit early if we don't want to include footprint models
if( m_params.m_boardOnly )
if( m_params.m_boardOnly || !m_params.m_exportComponents )
{
return hasdata;
}
@ -244,7 +244,6 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri
for( const FP_3DMODEL& fp_model : aFootprint->Models() )
{
if( !fp_model.m_Show || fp_model.m_Filename.empty() )
continue;
std::vector<wxString> searchedPaths;
@ -305,17 +304,15 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin )
PCB_LAYER_ID pcblayer = aTrack->GetLayer();
if( pcblayer != F_Cu && pcblayer != B_Cu )
if( !IsCopperLayer( pcblayer ) )
return false;
if( aTrack->Type() == PCB_ARC_T )
{
int maxError = m_board->GetDesignSettings().m_MaxError;
if( pcblayer == F_Cu )
aTrack->TransformShapeToPolygon( m_top_copper_shapes, pcblayer, 0, maxError, ERROR_INSIDE );
else
aTrack->TransformShapeToPolygon( m_bottom_copper_shapes, pcblayer, 0, maxError, ERROR_INSIDE );
aTrack->TransformShapeToPolygon( m_poly_copper_shapes[pcblayer], pcblayer, 0, maxError,
ERROR_INSIDE );
}
else
m_pcbModel->AddTrackSegment( aTrack, aOrigin );
@ -328,16 +325,13 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin )
{
for( ZONE* zone : m_board->Zones() )
{
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
for( PCB_LAYER_ID layer : zone->GetLayerSet().CuStack() )
{
if( layer == F_Cu || layer == B_Cu )
{
SHAPE_POLY_SET copper_shape;
zone->TransformSolidAreasShapesToPolygon( layer, copper_shape );
copper_shape.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
SHAPE_POLY_SET copper_shape;
zone->TransformSolidAreasShapesToPolygon( layer, copper_shape );
copper_shape.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer == F_Cu, aOrigin, false );
}
m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer, aOrigin, false );
}
}
}
@ -347,24 +341,18 @@ bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin )
{
PCB_SHAPE* graphic = dynamic_cast<PCB_SHAPE*>( aItem );
if( ! graphic )
if( !graphic )
return false;
PCB_LAYER_ID pcblayer = graphic->GetLayer();
if( pcblayer != F_Cu && pcblayer != B_Cu )
if( !IsCopperLayer( pcblayer ) )
return false;
SHAPE_POLY_SET copper_shapes;
int maxError = m_board->GetDesignSettings().m_MaxError;
if( pcblayer == F_Cu )
graphic->TransformShapeToPolygon( m_top_copper_shapes, pcblayer, 0,
maxError, ERROR_INSIDE );
else
graphic->TransformShapeToPolygon( m_bottom_copper_shapes, pcblayer, 0,
maxError, ERROR_INSIDE );
graphic->TransformShapeToPolygon( m_poly_copper_shapes[pcblayer], pcblayer, 0, maxError,
ERROR_INSIDE );
return true;
}
@ -384,6 +372,13 @@ bool EXPORTER_STEP::buildBoard3DShapes()
wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
}
LSET layersToExport = LSET::ExternalCuMask();
if( m_params.m_exportInnerCopper )
layersToExport |= LSET::InternalCuMask();
layersToExport &= m_board->GetEnabledLayers();
VECTOR2D origin;
// Determine the coordinate system reference:
@ -401,7 +396,10 @@ bool EXPORTER_STEP::buildBoard3DShapes()
m_pcbModel->SetBoardColor( m_solderMaskColor.r, m_solderMaskColor.g, m_solderMaskColor.b );
m_pcbModel->SetCopperColor( m_copperColor.r, m_copperColor.g, m_copperColor.b );
m_pcbModel->SetPCBThickness( m_boardThickness );
wxCHECK( m_board->GetDesignSettings().m_HasStackup, false );
m_pcbModel->SetStackup( m_board->GetDesignSettings().GetStackupDescriptor() );
m_pcbModel->SetEnabledLayers( layersToExport );
m_pcbModel->SetFuseShapes( m_params.m_fuseShapes );
// Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines,
@ -420,7 +418,7 @@ bool EXPORTER_STEP::buildBoard3DShapes()
for( FOOTPRINT* fp : m_board->Footprints() )
buildFootprint3DShapes( fp, origin );
if( ExportTracksAndVias() )
if( m_params.m_exportTracksVias )
{
for( PCB_TRACK* track : m_board->Tracks() )
buildTrack3DShape( track, origin );
@ -429,8 +427,11 @@ bool EXPORTER_STEP::buildBoard3DShapes()
buildGraphic3DShape( item, origin );
}
m_pcbModel->AddCopperPolygonShapes( &m_top_copper_shapes, true, origin, true );
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin, true );
for( PCB_LAYER_ID pcblayer : layersToExport.Seq() )
{
m_pcbModel->AddCopperPolygonShapes( &m_poly_copper_shapes[pcblayer], pcblayer, origin,
true );
}
if( m_params.m_exportZones )
{
@ -443,7 +444,7 @@ bool EXPORTER_STEP::buildBoard3DShapes()
msg.Printf( wxT( "Board outline: find %d initial points\n" ), pcbOutlines.FullPointCount() );
ReportMessage( msg );
if( !m_pcbModel->CreatePCB( pcbOutlines, origin ) )
if( !m_pcbModel->CreatePCB( pcbOutlines, origin, m_params.m_exportBoardBody ) )
{
ReportMessage( wxT( "could not create PCB solid model\n" ) );
return false;
@ -453,46 +454,6 @@ bool EXPORTER_STEP::buildBoard3DShapes()
}
void EXPORTER_STEP::calculatePcbThickness()
{
m_boardThickness = DEFAULT_BOARD_THICKNESS_MM;
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:
// Dielectric can have sub-layers. Layer 0 is the main layer
// Not frequent, but possible
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
thickness += item->GetThickness( idx );
break;
case BS_ITEM_TYPE_COPPER:
case BS_ITEM_TYPE_SOLDERMASK:
if( item->IsEnabled() )
thickness += item->GetThickness();
break;
default:
break;
}
}
if( thickness > 0 )
m_boardThickness = pcbIUScale.IUTomm( thickness );
}
}
bool EXPORTER_STEP::Export()
{
// Display the export time, for statistics
@ -503,10 +464,6 @@ bool EXPORTER_STEP::Export()
Message::DefaultMessenger()->AddPrinter( new KiCadPrinter( this ) );
ReportMessage( _( "Determining PCB data\n" ) );
calculatePcbThickness();
wxString msg;
msg.Printf( _( "Board Thickness from stackup: %.3f mm\n" ), m_boardThickness );
ReportMessage( msg );
if( m_params.m_outputFile.IsEmpty() )
{
@ -565,9 +522,11 @@ bool EXPORTER_STEP::Export()
if( m_fail || m_error )
{
wxString msg;
if( m_fail )
{
msg = wxString::Format( _( "Unable to create %s file.\n"
msg = wxString::Format( _( "Unable to create %s file.\n"
"Check that the board has a valid outline and models." ),
m_params.GetFormatName() );
}

View File

@ -28,6 +28,7 @@
#include <geometry/shape_poly_set.h>
#include <gal/color4d.h>
#include <layer_ids.h>
// Default value to chain 2 shapes when creating the board outlines
// from shapes on Edges.Cut layer
@ -54,8 +55,11 @@ public:
m_substModels( true ),
m_BoardOutlinesChainingEpsilon( BOARD_DEFAULT_CHAINING_EPSILON ),
m_boardOnly( false ),
m_exportTracks( false ),
m_exportBoardBody( true ),
m_exportComponents( true ),
m_exportTracksVias( false ),
m_exportZones( false ),
m_exportInnerCopper( false ),
m_fuseShapes( false ),
m_optimizeStep( true ),
m_format( FORMAT::STEP )
@ -80,8 +84,11 @@ public:
bool m_substModels;
double m_BoardOutlinesChainingEpsilon;
bool m_boardOnly;
bool m_exportTracks;
bool m_exportBoardBody;
bool m_exportComponents;
bool m_exportTracksVias;
bool m_exportZones;
bool m_exportInnerCopper;
bool m_fuseShapes;
bool m_optimizeStep;
FORMAT m_format;
@ -104,16 +111,12 @@ public:
void SetFail() { m_fail = true; }
void SetWarn() { m_warn = true; }
/// Return rue to export tracks and vias on top and bottom copper layers
bool ExportTracksAndVias() { return m_params.m_exportTracks; }
private:
bool buildBoard3DShapes();
bool buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOrigin );
bool buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin );
void buildZones3DShape( VECTOR2D aOrigin );
bool buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin );
void calculatePcbThickness();
EXPORTER_STEP_PARAMS m_params;
std::unique_ptr<FILENAME_RESOLVER> m_resolver;
@ -130,10 +133,7 @@ private:
/// used to identify items in step file
wxString m_pcbBaseName;
double m_boardThickness;
SHAPE_POLY_SET m_top_copper_shapes;
SHAPE_POLY_SET m_bottom_copper_shapes;
std::map<PCB_LAYER_ID, SHAPE_POLY_SET> m_poly_copper_shapes;
KIGFX::COLOR4D m_solderMaskColor;
KIGFX::COLOR4D m_copperColor;

View File

@ -45,6 +45,8 @@
#include <build_version.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_circle.h>
#include <board_stackup_manager/board_stackup.h>
#include <board_stackup_manager/stackup_predefined_prms.h>
#include "step_pcb_model.h"
#include "streamwrapper.h"
@ -59,27 +61,33 @@
#include <STEPCAFControl_Reader.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <APIHeaderSection_MakeHeader.hxx>
#include <Standard_Failure.hxx>
#include <Standard_Version.hxx>
#include <TCollection_ExtendedString.hxx>
#include <TDocStd_Document.hxx>
#include <TDataStd_Name.hxx>
#include <TDataStd_TreeNode.hxx>
#include <TDF_LabelSequence.hxx>
#include <TDF_ChildIterator.hxx>
#include <TopExp_Explorer.hxx>
#include <XCAFApp_Application.hxx>
#include <XCAFDoc.hxx>
#include <XCAFDoc_DocumentTool.hxx>
#include <XCAFDoc_ColorTool.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <BRep_Tool.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRepBuilderAPI.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepPrimAPI_MakePrism.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepTools.hxx>
#include <BRepLib_MakeWire.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <ShapeUpgrade_UnifySameDomain.hxx>
@ -87,19 +95,10 @@
#include <BRepBndLib.hxx>
#include <Bnd_BoundSortBox.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Builder.hxx>
#include <Standard_Failure.hxx>
#include <Geom_Curve.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <gp_Ax2.hxx>
#include <gp_Circ.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <GC_MakeArcOfCircle.hxx>
@ -203,8 +202,6 @@ STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName )
m_components = 0;
m_precision = USER_PREC;
m_angleprec = USER_ANGLE_PREC;
m_boardThickness = BOARD_THICKNESS_DEFAULT_MM;
m_copperThickness = COPPER_THICKNESS_DEFAULT_MM;
m_mergeOCCMaxDist = OCC_MAX_DISTANCE_TO_MERGE_POINTS;
m_minx = 1.0e10; // absurdly large number; any valid PCB X value will be smaller
m_pcbName = aPcbName;
@ -219,141 +216,149 @@ STEP_PCB_MODEL::~STEP_PCB_MODEL()
m_doc->Close();
}
bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin )
{
bool success = true;
for( PCB_LAYER_ID pcb_layer = F_Cu; ; pcb_layer = B_Cu )
for( PCB_LAYER_ID pcb_layer : aPad->GetLayerSet().Seq() )
{
if( !IsCopperLayer( pcb_layer ) )
continue;
if( !m_enabledLayers.Contains( pcb_layer ) )
continue;
TopoDS_Shape curr_shape;
double Zpos = pcb_layer == F_Cu ? m_boardThickness : -m_copperThickness;
if( aPad->IsOnLayer( pcb_layer ) )
double Zpos, thickness;
getLayerZPlacement( pcb_layer, Zpos, thickness );
// Make a shape on copper layers
std::shared_ptr<SHAPE> effShapePtr = aPad->GetEffectiveShape( pcb_layer );
wxCHECK( effShapePtr->Type() == SHAPE_TYPE::SH_COMPOUND, false );
SHAPE_COMPOUND* compoundShape = static_cast<SHAPE_COMPOUND*>( effShapePtr.get() );
std::vector<TopoDS_Shape> topodsShapes;
for( SHAPE* shape : compoundShape->Shapes() )
{
// Make a shape on top/bottom copper layer
std::shared_ptr<SHAPE> effShapePtr = aPad->GetEffectiveShape( pcb_layer );
wxCHECK( effShapePtr->Type() == SHAPE_TYPE::SH_COMPOUND, false );
SHAPE_COMPOUND* compoundShape = static_cast<SHAPE_COMPOUND*>( effShapePtr.get() );
std::vector<TopoDS_Shape> topodsShapes;
for( SHAPE* shape : compoundShape->Shapes() )
if( shape->Type() == SHAPE_TYPE::SH_SEGMENT || shape->Type() == SHAPE_TYPE::SH_CIRCLE )
{
if( shape->Type() == SHAPE_TYPE::SH_SEGMENT
|| shape->Type() == SHAPE_TYPE::SH_CIRCLE )
VECTOR2I start, end;
int width = 0;
if( shape->Type() == SHAPE_TYPE::SH_SEGMENT )
{
VECTOR2I start, end;
int width = 0;
SHAPE_SEGMENT* sh_seg = static_cast<SHAPE_SEGMENT*>( shape );
if( shape->Type() == SHAPE_TYPE::SH_SEGMENT )
{
SHAPE_SEGMENT* sh_seg = static_cast<SHAPE_SEGMENT*>( shape );
start = sh_seg->GetSeg().A;
end = sh_seg->GetSeg().B;
width = sh_seg->GetWidth();
}
else if( shape->Type() == SHAPE_TYPE::SH_CIRCLE )
{
SHAPE_CIRCLE* sh_circ = static_cast<SHAPE_CIRCLE*>( shape );
start = sh_seg->GetSeg().A;
end = sh_seg->GetSeg().B;
width = sh_seg->GetWidth();
}
else if( shape->Type() == SHAPE_TYPE::SH_CIRCLE )
{
SHAPE_CIRCLE* sh_circ = static_cast<SHAPE_CIRCLE*>( shape );
start = end = sh_circ->GetCenter();
width = sh_circ->GetRadius() * 2;
}
start = end = sh_circ->GetCenter();
width = sh_circ->GetRadius() * 2;
}
TopoDS_Shape topods_shape;
TopoDS_Shape topods_shape;
if( MakeShapeAsThickSegment( topods_shape, start, end, width, m_copperThickness,
Zpos, aOrigin ) )
{
topodsShapes.emplace_back( topods_shape );
}
else
{
success = false;
}
if( MakeShapeAsThickSegment( topods_shape, start, end, width, thickness, Zpos,
aOrigin ) )
{
topodsShapes.emplace_back( topods_shape );
}
else
{
SHAPE_POLY_SET polySet;
shape->TransformToPolygon( polySet, ARC_HIGH_DEF, ERROR_INSIDE );
success &= MakeShapes( topodsShapes, polySet, m_copperThickness, Zpos, aOrigin );
success = false;
}
}
// Fuse shapes
if( topodsShapes.size() == 1 )
{
m_board_copper_pads.emplace_back( topodsShapes.front() );
}
else
{
BRepAlgoAPI_Fuse mkFuse;
TopTools_ListOfShape shapeArguments, shapeTools;
SHAPE_POLY_SET polySet;
shape->TransformToPolygon( polySet, ARC_HIGH_DEF, ERROR_INSIDE );
for( TopoDS_Shape& sh : topodsShapes )
{
if( sh.IsNull() )
continue;
if( shapeArguments.IsEmpty() )
shapeArguments.Append( sh );
else
shapeTools.Append( sh );
}
mkFuse.SetRunParallel( true );
mkFuse.SetToFillHistory( false );
mkFuse.SetArguments( shapeArguments );
mkFuse.SetTools( shapeTools );
mkFuse.Build();
if( mkFuse.IsDone() )
{
TopoDS_Shape fusedShape = mkFuse.Shape();
ShapeUpgrade_UnifySameDomain unify( fusedShape, false, true, false );
unify.History() = nullptr;
unify.Build();
TopoDS_Shape unifiedShapes = unify.Shape();
if( !unifiedShapes.IsNull() )
{
m_board_copper_pads.emplace_back( unifiedShapes );
}
else
{
ReportMessage(
_( "** ShapeUpgrade_UnifySameDomain produced a null shape **\n" ) );
m_board_copper_pads.emplace_back( fusedShape );
}
}
else
{
for( TopoDS_Shape& sh : topodsShapes )
m_board_copper_pads.emplace_back( sh );
}
success &= MakeShapes( topodsShapes, polySet, thickness, Zpos, aOrigin );
}
}
if( pcb_layer == B_Cu )
break;
// Fuse shapes
if( topodsShapes.size() == 1 )
{
m_board_copper_pads.emplace_back( topodsShapes.front() );
}
else
{
BRepAlgoAPI_Fuse mkFuse;
TopTools_ListOfShape shapeArguments, shapeTools;
for( TopoDS_Shape& sh : topodsShapes )
{
if( sh.IsNull() )
continue;
if( shapeArguments.IsEmpty() )
shapeArguments.Append( sh );
else
shapeTools.Append( sh );
}
mkFuse.SetRunParallel( true );
mkFuse.SetToFillHistory( false );
mkFuse.SetArguments( shapeArguments );
mkFuse.SetTools( shapeTools );
mkFuse.Build();
if( mkFuse.IsDone() )
{
TopoDS_Shape fusedShape = mkFuse.Shape();
ShapeUpgrade_UnifySameDomain unify( fusedShape, false, true, false );
unify.History() = nullptr;
unify.Build();
TopoDS_Shape unifiedShapes = unify.Shape();
if( !unifiedShapes.IsNull() )
{
m_board_copper_pads.emplace_back( unifiedShapes );
}
else
{
ReportMessage(
_( "** ShapeUpgrade_UnifySameDomain produced a null shape **\n" ) );
m_board_copper_pads.emplace_back( fusedShape );
}
}
else
{
for( TopoDS_Shape& sh : topodsShapes )
m_board_copper_pads.emplace_back( sh );
}
}
}
if( aPad->GetAttribute() == PAD_ATTRIB::PTH && aPad->IsOnLayer( F_Cu )
&& aPad->IsOnLayer( B_Cu ) )
{
double f_pos, f_thickness;
double b_pos, b_thickness;
getLayerZPlacement( F_Cu, f_pos, f_thickness );
getLayerZPlacement( B_Cu, b_pos, b_thickness );
double top = std::max( f_pos, f_pos + f_thickness );
double bottom = std::min( b_pos, b_pos + b_thickness );
TopoDS_Shape plating;
std::shared_ptr<SHAPE_SEGMENT> seg_hole = aPad->GetEffectiveHoleShape();
double width = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
if( MakeShapeAsThickSegment( plating, seg_hole->GetSeg().A, seg_hole->GetSeg().B, width,
m_boardThickness + m_copperThickness * 2, -m_copperThickness,
aOrigin ) )
( top - bottom ), bottom, aOrigin ) )
{
m_board_copper_pads.push_back( plating );
}
@ -394,15 +399,16 @@ bool STEP_PCB_MODEL::AddTrackSegment( const PCB_TRACK* aTrack, const VECTOR2D& a
{
PCB_LAYER_ID pcblayer = aTrack->GetLayer();
if( pcblayer != F_Cu && pcblayer != B_Cu )
return false;
if( !m_enabledLayers.Contains( pcblayer ) )
return true;
TopoDS_Shape shape;
double zposition = pcblayer == F_Cu ? m_boardThickness : -m_copperThickness;
double zposition, thickness;
getLayerZPlacement( pcblayer, zposition, thickness );
bool success = MakeShapeAsThickSegment( shape, aTrack->GetStart(), aTrack->GetEnd(),
aTrack->GetWidth(), m_copperThickness,
zposition, aOrigin );
aTrack->GetWidth(), thickness, zposition, aOrigin );
if( success )
m_board_copper_tracks.push_back( shape );
@ -411,7 +417,65 @@ bool STEP_PCB_MODEL::AddTrackSegment( const PCB_TRACK* aTrack, const VECTOR2D& a
}
bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, bool aOnTop,
void STEP_PCB_MODEL::getLayerZPlacement( const PCB_LAYER_ID aLayer, double& aZPos,
double& aThickness )
{
int z = 0;
int thickness = 0;
bool wasPrepreg = false;
const std::vector<BOARD_STACKUP_ITEM*>& materials = m_stackup.GetList();
for( auto it = materials.rbegin(); it != materials.rend(); ++it )
{
const BOARD_STACKUP_ITEM* item = *it;
if( item->GetType() == BS_ITEM_TYPE_COPPER )
{
// Inner copper position is usually inside prepreg
if( ( wasPrepreg || aLayer == B_Cu ) && aLayer != F_Cu )
thickness = -item->GetThickness();
else
thickness = item->GetThickness();
if( item->GetBrdLayerId() == aLayer )
break;
z += thickness;
}
else if( item->GetType() == BS_ITEM_TYPE_DIELECTRIC )
{
wasPrepreg = ( item->GetTypeName() == KEY_PREPREG );
// Dielectric can have sub-layers. Layer 0 is the main layer
// Not frequent, but possible
for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
z += item->GetThickness( idx );
}
}
aZPos = pcbIUScale.IUTomm( z );
aThickness = pcbIUScale.IUTomm( thickness );
}
void STEP_PCB_MODEL::getBoardBodyZPlacement( double& aZPos, double& aThickness )
{
double f_pos, f_thickness;
double b_pos, b_thickness;
getLayerZPlacement( F_Cu, f_pos, f_thickness );
getLayerZPlacement( B_Cu, b_pos, b_thickness );
double top = std::min( f_pos, f_pos + f_thickness );
double bottom = std::max( b_pos, b_pos + b_thickness );
aThickness = ( top - bottom );
aZPos = bottom;
wxASSERT( aZPos == 0.0 );
}
bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, PCB_LAYER_ID aLayer,
const VECTOR2D& aOrigin, bool aTrack )
{
bool success = true;
@ -419,14 +483,18 @@ bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes,
if( aPolyShapes->IsEmpty() )
return true;
double z_pos = aOnTop ? m_boardThickness : -m_copperThickness;
if( !m_enabledLayers.Contains( aLayer ) )
return true;
if( !MakeShapes( aTrack ? m_board_copper_tracks : m_board_copper_zones, *aPolyShapes,
m_copperThickness, z_pos, aOrigin ) )
double z_pos, thickness;
getLayerZPlacement( aLayer, z_pos, thickness );
if( !MakeShapes( aTrack ? m_board_copper_tracks : m_board_copper_zones, *aPolyShapes, thickness,
z_pos, aOrigin ) )
{
ReportMessage( wxString::Format(
wxT( "Could not add shape (%d points) to copper layer on %s.\n" ),
aPolyShapes->FullPointCount(), aOnTop ? wxT( "top" ) : wxT( "bottom" ) ) );
ReportMessage(
wxString::Format( wxT( "Could not add shape (%d points) to copper layer on %s.\n" ),
aPolyShapes->FullPointCount(), LayerName( aLayer ) ) );
success = false;
}
@ -446,7 +514,15 @@ bool STEP_PCB_MODEL::AddPadHole( const PAD* aPad, const VECTOR2D& aOrigin )
const double margin = 0.01; // a small margin on the Z axix to be sure the hole
// is bigger than the board with copper
// must be > OCC_MAX_DISTANCE_TO_MERGE_POINTS
double holeZsize = m_boardThickness + ( m_copperThickness * 2 ) + ( margin * 2 );
double f_pos, f_thickness;
double b_pos, b_thickness;
getLayerZPlacement( F_Cu, f_pos, f_thickness );
getLayerZPlacement( B_Cu, b_pos, b_thickness );
double top = std::max( f_pos, f_pos + f_thickness );
double bottom = std::min( b_pos, b_pos + b_thickness );
double holeZsize = ( top - bottom ) + ( margin * 2 );
std::shared_ptr<SHAPE_SEGMENT> seg_hole = aPad->GetEffectiveHoleShape();
@ -456,7 +532,7 @@ bool STEP_PCB_MODEL::AddPadHole( const PAD* aPad, const VECTOR2D& aOrigin )
TopoDS_Shape copperHole, boardHole;
if( MakeShapeAsThickSegment( copperHole, seg_hole->GetSeg().A, seg_hole->GetSeg().B,
copperDrill, holeZsize, -m_copperThickness - margin, aOrigin ) )
copperDrill, holeZsize, bottom - margin, aOrigin ) )
{
m_copperCutouts.push_back( copperHole );
}
@ -466,7 +542,7 @@ bool STEP_PCB_MODEL::AddPadHole( const PAD* aPad, const VECTOR2D& aOrigin )
}
if( MakeShapeAsThickSegment( boardHole, seg_hole->GetSeg().A, seg_hole->GetSeg().B, boardDrill,
holeZsize, -m_copperThickness - margin, aOrigin ) )
holeZsize, bottom - margin, aOrigin ) )
{
m_boardCutouts.push_back( boardHole );
}
@ -534,14 +610,9 @@ bool STEP_PCB_MODEL::AddComponent( const std::string& aFileNameUTF8, const std::
}
void STEP_PCB_MODEL::SetPCBThickness( double aThickness )
void STEP_PCB_MODEL::SetEnabledLayers( const LSET& aLayers )
{
if( aThickness < 0.0 )
m_boardThickness = BOARD_THICKNESS_DEFAULT_MM;
else if( aThickness < BOARD_THICKNESS_MIN_MM )
m_boardThickness = BOARD_THICKNESS_MIN_MM;
else
m_boardThickness = aThickness;
m_enabledLayers = aLayers;
}
@ -551,6 +622,12 @@ void STEP_PCB_MODEL::SetFuseShapes( bool aValue )
}
void STEP_PCB_MODEL::SetStackup( const BOARD_STACKUP& aStackup )
{
m_stackup = aStackup;
}
void STEP_PCB_MODEL::SetBoardColor( double r, double g, double b )
{
m_boardColor[0] = r;
@ -1001,7 +1078,7 @@ bool STEP_PCB_MODEL::MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE
}
bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin, bool aPushBoardBody )
{
if( m_hasPCB )
{
@ -1014,10 +1091,14 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
Handle( XCAFDoc_ColorTool ) colorTool = XCAFDoc_DocumentTool::ColorTool( m_doc->Main() );
m_hasPCB = true; // whether or not operations fail we note that CreatePCB has been invoked
// Support for more than one main outline (more than one board)
ReportMessage( wxString::Format( wxT( "Build board outlines (%d outlines) with %d points.\n" ),
aOutline.OutlineCount(), aOutline.FullPointCount() ) );
double boardThickness;
double boardZPos;
getBoardBodyZPlacement( boardZPos, boardThickness );
#if 0
// This code should work, and it is working most of time
// However there are issues if the main outline is a circle with holes:
@ -1025,7 +1106,7 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
// see bug https://gitlab.com/kicad/code/kicad/-/issues/17446
// (Holes are missing from STEP export with circular PCB outline)
// Hard to say if the bug is in our code or in OCC 7.7
if( !MakeShapes( m_board_outlines, aOutline, m_boardThickness, 0.0, aOrigin ) )
if( !MakeShapes( m_board_outlines, aOutline, boardThickness, boardZPos, aOrigin ) )
{
// Error
ReportMessage( wxString::Format(
@ -1043,18 +1124,19 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
if( contId == 0 ) // main Outline
{
if( !MakeShapes( m_board_outlines, polyset, m_boardThickness, 0.0, aOrigin ) )
if( !MakeShapes( m_board_outlines, polyset, boardThickness, boardZPos, aOrigin ) )
ReportMessage( wxT( "OCC error creating main outline.\n" ) );
}
else // Hole inside the main outline
{
if( !MakeShapes( m_boardCutouts, polyset, m_boardThickness, 0.0, aOrigin ) )
if( !MakeShapes( m_boardCutouts, polyset, boardThickness, boardZPos, aOrigin ) )
ReportMessage( wxT( "OCC error creating hole in main outline.\n" ) );
}
}
}
#endif
// Even if we've disabled board body export, we still need the shapes for bounding box calculations.
Bnd_Box brdBndBox;
for( const TopoDS_Shape& brdShape : m_board_outlines )
@ -1312,7 +1394,8 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
pushToAssembly( m_board_copper_pads, copper_color, "pad" );
pushToAssembly( m_board_copper_fused, copper_color, "copper" );
pushToAssembly( m_board_outlines, board_color, "PCB" );
if( aPushBoardBody )
pushToAssembly( m_board_outlines, board_color, "PCB" );
#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 )
m_assy->UpdateAssemblies();
@ -1719,10 +1802,17 @@ bool STEP_PCB_MODEL::getModelLocation( bool aBottom, VECTOR2D aPosition, double
// Offset board thickness
aOffset.z += BOARD_OFFSET;
double boardThickness;
double boardZPos;
getBoardBodyZPlacement( boardZPos, boardThickness );
double top = std::max( boardZPos, boardZPos + boardThickness );
double bottom = std::min( boardZPos, boardZPos + boardThickness );
gp_Trsf lRot;
if( aBottom )
{
aOffset.z -= bottom;
lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
lPos.Multiply( lRot );
lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 1.0, 0.0, 0.0 ) ), M_PI );
@ -1730,7 +1820,7 @@ bool STEP_PCB_MODEL::getModelLocation( bool aBottom, VECTOR2D aPosition, double
}
else
{
aOffset.z += m_boardThickness;
aOffset.z += top;
lRot.SetRotation( gp_Ax1( gp_Pnt( 0.0, 0.0, 0.0 ), gp_Dir( 0.0, 0.0, 1.0 ) ), aRotation );
lPos.Multiply( lRot );
}

View File

@ -31,17 +31,14 @@
#include <utility>
#include <vector>
#include <Standard_Version.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <TDocStd_Document.hxx>
#include <XCAFApp_Application.hxx>
#include <XCAFDoc_ShapeTool.hxx>
#include <Standard_Handle.hxx>
#include <TDF_Label.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Edge.hxx>
#include <math/vector2d.h>
#include <math/vector3.h>
#include <geometry/shape_poly_set.h>
#include <board_stackup_manager/board_stackup.h>
/**
* Default distance between points to treat them as separate ones (mm)
@ -69,6 +66,10 @@ static constexpr double ARC_TO_SEGMENT_MAX_ERROR_MM = 0.005;
class PAD;
class TDocStd_Document;
class XCAFApp_Application;
class XCAFDoc_ShapeTool;
typedef std::pair< std::string, TDF_Label > MODEL_DATUM;
typedef std::map< std::string, TDF_Label > MODEL_MAP;
@ -93,7 +94,7 @@ public:
bool AddTrackSegment( const PCB_TRACK* aTrack, const VECTOR2D& aOrigin );
// add a set of polygons (must be in final position) on top or bottom of the board as copper
bool AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, bool aOnTop,
bool AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, PCB_LAYER_ID aLayer,
const VECTOR2D& aOrigin, bool aTrack );
// add a component at the given position and orientation
@ -104,15 +105,12 @@ public:
void SetBoardColor( double r, double g, double b );
void SetCopperColor( double r, double g, double b );
// set the thickness of the PCB (mm); the top of the PCB shall be at Z = aThickness
// aThickness < 0.0 == use default thickness
// aThickness <= THICKNESS_MIN == use THICKNESS_MIN
// aThickness > THICKNESS_MIN == use aThickness
void SetPCBThickness( double aThickness );
void SetEnabledLayers( const LSET& aLayers );
// enable fusing the geometry
void SetFuseShapes( bool aValue );
void SetStackup( const BOARD_STACKUP& aStackup );
// Set the max distance (in mm) to consider 2 points have the same coordinates
// and can be merged
void OCCSetMergeMaxDistance( double aDistance = OCC_MAX_DISTANCE_TO_MERGE_POINTS );
@ -120,7 +118,7 @@ public:
void SetMaxError( int aMaxError ) { m_maxError = aMaxError; }
// create the PCB model using the current outlines and drill holes
bool CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin );
bool CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin, bool aPushBoardBody );
/**
* Convert a SHAPE_POLY_SET to TopoDS_Shape's (polygonal vertical prisms)
@ -196,12 +194,9 @@ private:
*/
bool isBoardOutlineValid();
/** create one solid board using current outline and drill holes set
* @param aIdx is the main outline index
* @param aOutline is the set of outlines with holes
* @param aOrigin is the coordinate origin for 3 view
*/
bool createOneBoard( int aIdx, SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin );
void getLayerZPlacement( const PCB_LAYER_ID aLayer, double& aZPos, double& aThickness );
void getBoardBodyZPlacement( double& aZPos, double& aThickness );
/**
* Load a 3D model data.
@ -240,8 +235,8 @@ private:
double m_angleprec; // angle numeric precision
double m_boardColor[3]; // board body, RGB values
double m_copperColor[3]; // copper, RGB values
double m_boardThickness; // PCB thickness, mm
double m_copperThickness; // copper thickness, mm
BOARD_STACKUP m_stackup; // board stackup
LSET m_enabledLayers; // a set of layers enabled for export
double m_minx; // leftmost curve point
double m_mergeOCCMaxDist; // minimum distance (mm) below which two

View File

@ -190,8 +190,11 @@ int PCBNEW_JOBS_HANDLER::JobExportStep( JOB* aJob )
else
{
EXPORTER_STEP_PARAMS params;
params.m_exportTracks = aStepJob->m_exportTracks;
params.m_exportBoardBody = aStepJob->m_exportBoardBody;
params.m_exportComponents = aStepJob->m_exportComponents;
params.m_exportTracksVias = aStepJob->m_exportTracks;
params.m_exportZones = aStepJob->m_exportZones;
params.m_exportInnerCopper = aStepJob->m_exportInnerCopper;
params.m_fuseShapes = aStepJob->m_fuseShapes;
params.m_includeUnspecified = aStepJob->m_includeUnspecified;
params.m_includeDNP = aStepJob->m_includeDNP;