diff --git a/common/jobs/job_export_pcb_3d.h b/common/jobs/job_export_pcb_3d.h index 9bbb93b9dd..f70deb4eb1 100644 --- a/common/jobs/job_export_pcb_3d.h +++ b/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; }; diff --git a/kicad/cli/command_pcb_export_3d.cpp b/kicad/cli/command_pcb_export_3d.cpp index 67773c0bd3..693613006f 100644 --- a/kicad/cli/command_pcb_export_3d.cpp +++ b/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( ARG_INPUT ).c_str() ); step->m_outputFile = FROM_UTF8( m_argParser.get( ARG_OUTPUT ).c_str() ); step->m_boardOnly = m_argParser.get( ARG_BOARD_ONLY ); - step->m_exportTracks = m_argParser.get( ARG_EXPORT_TRACKS ); + step->m_exportTracks = m_argParser.get( ARG_INCLUDE_TRACKS ); + step->m_exportZones = m_argParser.get( ARG_INCLUDE_ZONES ); step->m_format = m_format; if( step->m_format == JOB_EXPORT_PCB_3D::FORMAT::UNKNOWN ) diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index 946ab0e02a..13b3f30867 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/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 diff --git a/pcbnew/dialogs/dialog_export_step_base.cpp b/pcbnew/dialogs/dialog_export_step_base.cpp index f92ef93f1e..e266163533 100644 --- a/pcbnew/dialogs/dialog_export_step_base.cpp +++ b/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 ); diff --git a/pcbnew/dialogs/dialog_export_step_base.fbp b/pcbnew/dialogs/dialog_export_step_base.fbp index 57c3bf986f..891dcf3991 100644 --- a/pcbnew/dialogs/dialog_export_step_base.fbp +++ b/pcbnew/dialogs/dialog_export_step_base.fbp @@ -1219,6 +1219,70 @@ + + 5 + wxBOTTOM|wxRIGHT|wxTOP + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Export zones (time consuming) + + 0 + + + 0 + + 1 + m_cbExportZones + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + Export tracks and vias on external copper layers. Warning: this is *extremely* time consuming. + + wxFILTER_NONE + wxDefaultValidator + + + + + + 5 wxLEFT|wxRIGHT|wxTOP diff --git a/pcbnew/dialogs/dialog_export_step_base.h b/pcbnew/dialogs/dialog_export_step_base.h index 4f6dbfa14d..7880fa0bb6 100644 --- a/pcbnew/dialogs/dialog_export_step_base.h +++ b/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; diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp index 73736a6ab8..b593a94581 100644 --- a/pcbnew/exporters/step/exporter_step.cpp +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #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( 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" ) ); diff --git a/pcbnew/exporters/step/exporter_step.h b/pcbnew/exporters/step/exporter_step.h index 2963adfc95..fd3891ec17 100644 --- a/pcbnew/exporters/step/exporter_step.h +++ b/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(); diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 9c7b3bc4a2..4d1c8c854e 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/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& 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 ) ); + auto pushToAssembly = [&]( std::vector& aShapesList, Quantity_Color aColor, const wxString& aShapeName ) + { + int i = 1; + for( TopoDS_Shape& shape : aShapesList ) + { + Handle( TDataStd_TreeNode ) node; - if( m_pcb_labels.back().IsNull() ) - return false; - } + // 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(); diff --git a/pcbnew/exporters/step/step_pcb_model.h b/pcbnew/exporters/step/step_pcb_model.h index 26931d4e5f..b1470c1aee 100644 --- a/pcbnew/exporters/step/step_pcb_model.h +++ b/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 m_cutouts; // Main outlines (more than one board) - std::vector m_board_outlines; + std::vector m_board_outlines; + std::vector m_board_copper_zones; + std::vector m_board_copper_tracks; + std::vector m_board_copper_pads; /// Name of the PCB, which will most likely be the file name of the path. wxString m_pcbName; diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index c4969e7735..6a6ad4dad8 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/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;