Browse Source

Add zone export to step export

Mileage may vary on performance, decent enough for my board.
Warning, freecad chokes on boards with zones, but commerical tools are fine

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15234
newinvert
Marek Roszko 2 years ago
parent
commit
b52b05ebbb
  1. 2
      common/jobs/job_export_pcb_3d.h
  2. 13
      kicad/cli/command_pcb_export_3d.cpp
  3. 9
      pcbnew/dialogs/dialog_export_step.cpp
  4. 5
      pcbnew/dialogs/dialog_export_step_base.cpp
  5. 64
      pcbnew/dialogs/dialog_export_step_base.fbp
  6. 1
      pcbnew/dialogs/dialog_export_step_base.h
  7. 29
      pcbnew/exporters/step/exporter_step.cpp
  8. 3
      pcbnew/exporters/step/exporter_step.h
  9. 166
      pcbnew/exporters/step/step_pcb_model.cpp
  10. 8
      pcbnew/exporters/step/step_pcb_model.h
  11. 1
      pcbnew/pcbnew_jobs_handler.cpp

2
common/jobs/job_export_pcb_3d.h

@ -43,6 +43,7 @@ public:
// 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 ), // Extremely time consuming if true
m_exportZones( false ), // Extremely time consuming if true
m_format( JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN )
{
}
@ -67,6 +68,7 @@ public:
double m_yOrigin;
double m_BoardOutlinesChainingEpsilon;
bool m_exportTracks;
bool m_exportZones;
JOB_EXPORT_PCB_3D::FORMAT m_format;
};

13
kicad/cli/command_pcb_export_3d.cpp

@ -38,7 +38,8 @@
#define ARG_MIN_DISTANCE "--min-distance"
#define ARG_USER_ORIGIN "--user-origin"
#define ARG_BOARD_ONLY "--board-only"
#define ARG_EXPORT_TRACKS "--export-tracks"
#define ARG_INCLUDE_TRACKS "--include-tracks"
#define ARG_INCLUDE_ZONES "--include-zones"
#define ARG_FORMAT "--format"
#define REGEX_QUANTITY "([\\s]*[+-]?[\\d]*[.]?[\\d]*)"
@ -92,11 +93,16 @@ CLI::PCB_EXPORT_3D_COMMAND::PCB_EXPORT_3D_COMMAND( const std::string& aName,
.implicit_value( true )
.default_value( false );
m_argParser.add_argument( ARG_EXPORT_TRACKS )
m_argParser.add_argument( ARG_INCLUDE_TRACKS )
.help( UTF8STDSTR( _( "Export tracks (extremely time consuming)" ) ) )
.implicit_value( true )
.default_value( false );
m_argParser.add_argument( ARG_INCLUDE_ZONES )
.help( UTF8STDSTR( _( "Export zones (extremely time consuming)" ) ) )
.implicit_value( true )
.default_value( false );
m_argParser.add_argument( ARG_MIN_DISTANCE )
.default_value( std::string( "0.01mm" ) )
.help( UTF8STDSTR( _( "Minimum distance between points to treat them as separate ones" ) ) );
@ -125,7 +131,8 @@ int CLI::PCB_EXPORT_3D_COMMAND::doPerform( KIWAY& aKiway )
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 );
step->m_exportTracks = m_argParser.get<bool>( ARG_EXPORT_TRACKS );
step->m_exportTracks = m_argParser.get<bool>( ARG_INCLUDE_TRACKS );
step->m_exportZones = m_argParser.get<bool>( ARG_INCLUDE_ZONES );
step->m_format = m_format;
if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN )

9
pcbnew/dialogs/dialog_export_step.cpp

@ -115,6 +115,8 @@ private:
bool m_noDNP; // remember last preference for No DNP Component
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)
wxString m_boardPath; // path to the exported board file
static int m_toleranceLastChoice; // Store m_tolerance option during a session
};
@ -122,6 +124,7 @@ private:
int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default
bool DIALOG_EXPORT_STEP::m_exportTracks = false;
bool DIALOG_EXPORT_STEP::m_exportZones = false;
DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& aBoardPath ) :
DIALOG_EXPORT_STEP_BASE( aParent )
@ -187,6 +190,7 @@ DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString&
m_noDNP = cfg->m_ExportStep.no_dnp;
m_cbExportTracks->SetValue( m_exportTracks );
m_cbExportZones->SetValue( m_exportZones );
m_cbRemoveUnspecified->SetValue( m_noUnspecified );
m_cbRemoveDNP->SetValue( m_noDNP );
m_cbSubstModels->SetValue( cfg->m_ExportStep.replace_models );
@ -424,7 +428,10 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
cmdK2S.Append( wxT( " --subst-models" ) );
if( m_exportTracks )
cmdK2S.Append( wxT( " --export-tracks" ) );
cmdK2S.Append( wxT( " --include-tracks" ) );
if( m_exportZones )
cmdK2S.Append( wxT( " --include-zones" ) );
// Note: for some reason, using \" to insert a quote in a format string, under MacOS
// wxString::Format does not work. So use a %c format in string

5
pcbnew/dialogs/dialog_export_step_base.cpp

@ -130,6 +130,11 @@ DIALOG_EXPORT_STEP_BASE::DIALOG_EXPORT_STEP_BASE( wxWindow* parent, wxWindowID i
sbOtherOptions->Add( m_cbExportTracks, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
m_cbExportZones = new wxCheckBox( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Export zones (time consuming)"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbExportZones->SetToolTip( _("Export tracks and vias on external copper layers.\nWarning: this is *extremely* time consuming.") );
sbOtherOptions->Add( m_cbExportZones, 0, wxBOTTOM|wxRIGHT|wxTOP, 5 );
m_staticTextTolerance = new wxStaticText( sbOtherOptions->GetStaticBox(), wxID_ANY, _("Board outline chaining tolerance:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticTextTolerance->Wrap( -1 );
sbOtherOptions->Add( m_staticTextTolerance, 0, wxLEFT|wxRIGHT|wxTOP, 5 );

64
pcbnew/dialogs/dialog_export_step_base.fbp

@ -1219,6 +1219,70 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxTOP</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<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="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 zones (time consuming)</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_cbExportZones</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">Export tracks and vias on external copper layers.&#x0A;Warning: this is *extremely* time consuming.</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="0">
<property name="border">5</property>
<property name="flag">wxLEFT|wxRIGHT|wxTOP</property>

1
pcbnew/dialogs/dialog_export_step_base.h

@ -59,6 +59,7 @@ class DIALOG_EXPORT_STEP_BASE : public DIALOG_SHIM
wxCheckBox* m_cbSubstModels;
wxCheckBox* m_cbOverwriteFile;
wxCheckBox* m_cbExportTracks;
wxCheckBox* m_cbExportZones;
wxStaticText* m_staticTextTolerance;
wxChoice* m_choiceTolerance;
wxStdDialogButtonSizer* m_sdbSizer;

29
pcbnew/exporters/step/exporter_step.cpp

@ -30,6 +30,7 @@
#include <pcb_track.h>
#include <pcb_shape.h>
#include <pad.h>
#include <zone.h>
#include <fp_lib_table.h>
#include "step_pcb_model.h"
@ -322,6 +323,25 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin )
}
void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin )
{
for( ZONE* zone : m_board->Zones() )
{
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
if( layer == F_Cu || layer == B_Cu )
{
SHAPE_POLY_SET copper_shape;
zone->TransformSolidAreasShapesToPolygon( layer, copper_shape );
copper_shape.Fracture( SHAPE_POLY_SET::PM_FAST );
m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer == F_Cu, aOrigin, false );
}
}
}
}
bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin )
{
PCB_SHAPE* graphic = dynamic_cast<PCB_SHAPE*>( aItem );
@ -410,8 +430,13 @@ bool EXPORTER_STEP::buildBoard3DShapes()
m_top_copper_shapes.Fracture( SHAPE_POLY_SET::PM_FAST );
m_bottom_copper_shapes.Fracture( SHAPE_POLY_SET::PM_FAST );
m_pcbModel->AddCopperPolygonShapes( &m_top_copper_shapes, true, origin );
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin );
m_pcbModel->AddCopperPolygonShapes( &m_top_copper_shapes, true, origin, true );
m_pcbModel->AddCopperPolygonShapes( &m_bottom_copper_shapes, false, origin, true );
if( m_params.m_exportZones )
{
buildZones3DShape( origin );
}
ReportMessage( wxT( "Create PCB solid model\n" ) );

3
pcbnew/exporters/step/exporter_step.h

@ -55,6 +55,7 @@ public:
m_BoardOutlinesChainingEpsilon( BOARD_DEFAULT_CHAINING_EPSILON ),
m_boardOnly( false ),
m_exportTracks( false ),
m_exportZones( false ),
m_format( FORMAT::STEP )
{};
@ -77,6 +78,7 @@ public:
double m_BoardOutlinesChainingEpsilon;
bool m_boardOnly;
bool m_exportTracks;
bool m_exportZones;
FORMAT m_format;
wxString GetDefaultExportExtension();
@ -104,6 +106,7 @@ 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();

166
pcbnew/exporters/step/step_pcb_model.cpp

@ -228,14 +228,14 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin )
-pcbIUScale.IUTomm( pos.y - aOrigin.y ),
Zpos ) );
BRepBuilderAPI_Transform round_shape( curr_shape, shift );
m_board_outlines.push_back( round_shape.Shape() );
m_board_copper_pads.push_back( round_shape.Shape() );
}
else
{
success = MakeShape( curr_shape, pad_shape.get()->COutline(0), m_copperThickness, Zpos, aOrigin );
if( success )
m_board_outlines.push_back( curr_shape );
m_board_copper_pads.push_back( curr_shape );
}
}
@ -250,7 +250,8 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin )
}
bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, bool aOnTop, const VECTOR2D& aOrigin )
bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, bool aOnTop,
const VECTOR2D& aOrigin, bool aTrack )
{
bool success = true;
@ -261,7 +262,10 @@ bool STEP_PCB_MODEL::AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes,
if( MakeShape( copper_shape, aPolyShapes->COutline( ii ), m_copperThickness, z_pos, aOrigin ) )
{
m_board_outlines.push_back( copper_shape );
if( aTrack )
m_board_copper_tracks.push_back( copper_shape );
else
m_board_copper_zones.push_back( copper_shape );
}
else
{
@ -576,10 +580,9 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
return true;
}
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
// Number of items having the copper color
int copper_item_count = m_board_outlines.size();
// Support for more than one main outline (more than one board)
for( int cnt = 0; cnt < aOutline.OutlineCount(); cnt++ )
@ -632,41 +635,83 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
for( TopoDS_Shape& hole : m_cutouts )
holelist.Append( hole );
// Remove holes for each item (board body or bodies, one can have more than one board)
// and copper items (copper_item_count items)
int cnt = 0;
for( TopoDS_Shape& board: m_board_outlines )
auto subtractShapes = [&]( const wxString& aWhat, std::vector<TopoDS_Shape>& aShapesList,
TopTools_ListOfShape& aSubtrahend )
{
cnt++;
// Remove holes for each item (board body or bodies, one can have more than one board)
int cnt = 0;
for( TopoDS_Shape& shape : aShapesList )
{
cnt++;
if( cnt % 10 == 0 )
ReportMessage( wxString::Format( wxT( "added %d/%d shapes\n" ),
cnt, (int)m_board_outlines.size() ) );
if( cnt % 10 == 0 )
ReportMessage( wxString::Format( _( "Cutting %d/%d %s\n" ), cnt,
(int) aShapesList.size(), aWhat ) );
TopTools_ListOfShape mainbrd;
mainbrd.Append( board );
TopTools_ListOfShape mainbrd;
mainbrd.Append( shape );
BRepAlgoAPI_Cut Cut;
Cut.SetArguments( mainbrd );
BRepAlgoAPI_Cut Cut;
Cut.SetArguments( mainbrd );
Cut.SetTools( holelist );
Cut.Build();
Cut.SetTools( aSubtrahend );
Cut.Build();
board = Cut.Shape();
}
shape = Cut.Shape();
}
};
subtractShapes( _( "pads" ), m_board_copper_pads, holelist );
subtractShapes( _( "shapes" ), m_board_outlines, holelist );
subtractShapes( _( "tracks" ), m_board_copper_tracks, holelist );
subtractShapes( _( "zones" ), m_board_copper_zones, holelist );
}
// push the board to the data structure
ReportMessage( wxT( "\nGenerate board full shape.\n" ) );
// Dont expand the component or else coloring it gets hard
for( TopoDS_Shape& board: m_board_outlines )
{
m_pcb_labels.push_back( m_assy->AddComponent( m_assy_label, board, false ) );
if( m_pcb_labels.back().IsNull() )
return false;
}
auto pushToAssembly = [&]( std::vector<TopoDS_Shape>& aShapesList, Quantity_Color aColor, const wxString& aShapeName )
{
int i = 1;
for( TopoDS_Shape& shape : aShapesList )
{
Handle( TDataStd_TreeNode ) node;
// Dont expand the component or else coloring it gets hard
TDF_Label lbl =
m_assy->AddComponent( m_assy_label, shape, false );
m_pcb_labels.push_back( lbl );
if( m_pcb_labels.back().IsNull() )
break;
lbl.FindAttribute( XCAFDoc::ShapeRefGUID(), node );
TDF_Label shpLbl = node->Father()->Label();
if( !shpLbl.IsNull() )
{
colorTool->SetColor( shpLbl, aColor, XCAFDoc_ColorSurf );
wxString shapeName;
if( aShapesList.size() > 1 )
{
shapeName = wxString::Format(
wxT( "%s_%s_%d" ), m_pcbName, aShapeName, i );
}
else
{
shapeName = wxString::Format(
wxT( "%s_%s" ), m_pcbName, aShapeName );
}
TCollection_ExtendedString partname(
shapeName.ToUTF8().data() );
TDataStd_Name::Set( shpLbl, partname );
}
i++;
}
};
// AddComponent adds a label that has a reference (not a parent/child relation) to the real
// label. We need to extract that real label to name it for the STEP output cleanly
@ -676,68 +721,15 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin )
// to "Component" or "Assembly".
// Init colors for the board body and the copper items (if any)
Handle( XCAFDoc_ColorTool ) colorTool = XCAFDoc_DocumentTool::ColorTool( m_doc->Main() );
Quantity_Color board_color( m_boardColor[0], m_boardColor[1], m_boardColor[2],
Quantity_TOC_RGB );
Quantity_Color copper_color( m_copperColor[0], m_copperColor[1], m_copperColor[2],
Quantity_TOC_RGB );
int pcbIdx = 1;
int copper_objects_cnt = 0;
for( TDF_Label& pcb_label : m_pcb_labels )
{
Handle( TDataStd_TreeNode ) node;
if( pcb_label.FindAttribute( XCAFDoc::ShapeRefGUID(), node ) )
{
// Gives a name to each board object
TDF_Label label = node->Father()->Label();
if( !label.IsNull() )
{
wxString pcbName;
// Note, we include the pcb/project name as a prefix
// because several STEP importing CAD software like SolidWorks
// will deduplicate anything imported by it's STEP name
if( copper_objects_cnt < copper_item_count )
{
pcbName = wxString::Format( wxT( "%s_Copper_Item%d" ),
m_pcbName, copper_objects_cnt+1 );
}
else
{
if( m_pcb_labels.size() == 1 )
pcbName = wxString::Format( wxT( "%s_PCB" ), m_pcbName );
else
pcbName = wxString::Format( wxT( "%s_PCB%d" ), m_pcbName, pcbIdx++ );
}
std::string pcbNameStdString( pcbName.ToUTF8() );
TCollection_ExtendedString partname( pcbNameStdString.c_str() );
TDataStd_Name::Set( label, partname );
}
}
// color the PCB
TopExp_Explorer topex;
topex.Init( m_assy->GetShape( pcb_label ), TopAbs_SOLID );
while( topex.More() )
{
// First objects are copper objects, last(s) is the board body
if( copper_objects_cnt < copper_item_count )
colorTool->SetColor( topex.Current(), copper_color, XCAFDoc_ColorSurf );
else
colorTool->SetColor( topex.Current(), board_color, XCAFDoc_ColorSurf );
topex.Next();
}
copper_objects_cnt++;
}
pushToAssembly( m_board_copper_tracks, copper_color, "track" );
pushToAssembly( m_board_copper_zones, copper_color, "zone" );
pushToAssembly( m_board_copper_pads, copper_color, "pad" );
pushToAssembly( m_board_outlines, board_color, "PCB" );
#if( defined OCC_VERSION_HEX ) && ( OCC_VERSION_HEX > 0x070101 )
m_assy->UpdateAssemblies();

8
pcbnew/exporters/step/step_pcb_model.h

@ -87,7 +87,8 @@ public:
bool AddPadShape( const PAD* aPad, 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, const VECTOR2D& aOrigin );
bool AddCopperPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, bool aOnTop,
const VECTOR2D& aOrigin, bool aTrack );
// add a component at the given position and orientation
bool AddComponent( const std::string& aFileName, const std::string& aRefDes, bool aBottom,
@ -220,7 +221,10 @@ private:
std::vector<TopoDS_Shape> m_cutouts;
// Main outlines (more than one board)
std::vector<TopoDS_Shape> m_board_outlines;
std::vector<TopoDS_Shape> m_board_outlines;
std::vector<TopoDS_Shape> m_board_copper_zones;
std::vector<TopoDS_Shape> m_board_copper_tracks;
std::vector<TopoDS_Shape> m_board_copper_pads;
/// Name of the PCB, which will most likely be the file name of the path.
wxString m_pcbName;

1
pcbnew/pcbnew_jobs_handler.cpp

@ -98,6 +98,7 @@ int PCBNEW_JOBS_HANDLER::JobExportStep( JOB* aJob )
EXPORTER_STEP_PARAMS params;
params.m_exportTracks = aStepJob->m_exportTracks;
params.m_exportZones = aStepJob->m_exportZones;
params.m_includeUnspecified = aStepJob->m_includeUnspecified;
params.m_includeDNP = aStepJob->m_includeDNP;
params.m_BoardOutlinesChainingEpsilon = aStepJob->m_BoardOutlinesChainingEpsilon;

Loading…
Cancel
Save