diff --git a/common/jobs/job_export_pcb_3d.h b/common/jobs/job_export_pcb_3d.h index 0e2d08bc57..06a0e84c97 100644 --- a/common/jobs/job_export_pcb_3d.h +++ b/common/jobs/job_export_pcb_3d.h @@ -56,6 +56,7 @@ public: bool m_includeDNP; bool m_substModels; bool m_optimizeStep; + wxString m_netFilter; wxString m_filename; wxString m_outputFile; double m_xOrigin; diff --git a/kicad/cli/command_pcb_export_3d.cpp b/kicad/cli/command_pcb_export_3d.cpp index 1ae75f6244..103a03b448 100644 --- a/kicad/cli/command_pcb_export_3d.cpp +++ b/kicad/cli/command_pcb_export_3d.cpp @@ -44,6 +44,7 @@ #define ARG_INCLUDE_INNER_COPPER "--include-inner-copper" #define ARG_FUSE_SHAPES "--fuse-shapes" #define ARG_NO_OPTIMIZE_STEP "--no-optimize-step" +#define ARG_NET_FILTER "--net-filter" #define ARG_FORMAT "--format" #define ARG_VRML_UNITS "--units" #define ARG_VRML_MODELS_DIR "--models-dir" @@ -135,6 +136,11 @@ CLI::PCB_EXPORT_3D_COMMAND::PCB_EXPORT_3D_COMMAND( const std::string& aNa .help( UTF8STDSTR( _( "Minimum distance between points to treat them as separate ones" ) ) ) .metavar( "MIN_DIST" ); + + m_argParser.add_argument( ARG_NET_FILTER ) + .default_value( std::string() ) + .help( UTF8STDSTR( _( + "Only include copper items belonging to nets matching this wildcard" ) ) ); } if( m_format == JOB_EXPORT_PCB_3D::FORMAT::STEP ) @@ -187,6 +193,7 @@ int CLI::PCB_EXPORT_3D_COMMAND::doPerform( KIWAY& aKiway ) step->m_exportInnerCopper = m_argParser.get( ARG_INCLUDE_INNER_COPPER ); step->m_fuseShapes = m_argParser.get( ARG_FUSE_SHAPES ); step->m_boardOnly = m_argParser.get( ARG_BOARD_ONLY ); + step->m_netFilter = From_UTF8( m_argParser.get( ARG_NET_FILTER ).c_str() ); } if( m_format == JOB_EXPORT_PCB_3D::FORMAT::STEP ) diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index 73dd70d692..a743a438cf 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -508,6 +508,8 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent ) if( m_fuseShapes ) cmdK2S.Append( wxT( " --fuse-shapes" ) ); + // TODO: --net-filter + // 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 int quote = '\''; diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp index dcacdae45d..2ec182fdb2 100644 --- a/pcbnew/exporters/step/exporter_step.cpp +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -24,6 +24,7 @@ */ #include "exporter_step.h" +#include #include #include #include @@ -172,7 +173,8 @@ EXPORTER_STEP::~EXPORTER_STEP() bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOrigin ) { - bool hasdata = false; + bool hasdata = false; + std::vector padsMatchingNetFilter; // Dump the pad holes into the PCB for( PAD* pad : aFootprint->Pads() ) @@ -180,11 +182,16 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri if( m_pcbModel->AddPadHole( pad, aOrigin ) ) hasdata = true; + if( !m_params.m_netFilter.IsEmpty() && !pad->GetNetname().Matches( m_params.m_netFilter ) ) + continue; + if( m_params.m_exportTracksVias ) { if( m_pcbModel->AddPadShape( pad, aOrigin ) ) hasdata = true; } + + padsMatchingNetFilter.push_back( pad ); } // Build 3D shapes of the footprint graphic items on external layers: @@ -192,13 +199,42 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri { int maxError = m_board->GetDesignSettings().m_MaxError; - for( PCB_LAYER_ID pcblayer : aFootprint->GetLayerSet().CuStack() ) + for( PCB_LAYER_ID pcblayer : LSET( aFootprint->GetLayerSet() & LSET::AllCuMask() ).Seq() ) { - aFootprint->TransformFPShapesToPolySet( m_poly_copper_shapes[pcblayer], pcblayer, 0, - maxError, ERROR_INSIDE, + SHAPE_POLY_SET buffer; + + aFootprint->TransformFPShapesToPolySet( buffer, pcblayer, 0, maxError, ERROR_INSIDE, false, /* include text */ true, /* include shapes */ false /* include private items */ ); + + if( m_params.m_netFilter.IsEmpty() ) + { + m_poly_copper_shapes[pcblayer].Append( buffer ); + } + else + { + // Only add shapes colliding with any matching pads + for( const SHAPE_POLY_SET::POLYGON& poly : buffer.CPolygons() ) + { + bool connectsToPad = false; + + for( PAD* pad : padsMatchingNetFilter ) + { + if( !pad->IsOnLayer( pcblayer ) ) + continue; + + std::shared_ptr padPoly = pad->GetEffectivePolygon(); + SHAPE_POLY_SET gfxPoly( poly ); + + if( padPoly->Collide( &gfxPoly ) ) + { + m_poly_copper_shapes[pcblayer].Append( gfxPoly ); + break; + } + } + } + } } } @@ -301,6 +337,9 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, VECTOR2D aOri bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin ) { + if( !m_params.m_netFilter.IsEmpty() && !aTrack->GetNetname().Matches( m_params.m_netFilter ) ) + return true; + if( aTrack->Type() == PCB_VIA_T ) { return m_pcbModel->AddViaShape( static_cast( aTrack ), aOrigin ); @@ -329,12 +368,18 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin ) { for( ZONE* zone : m_board->Zones() ) { + if( !m_params.m_netFilter.IsEmpty() && !zone->GetNetname().Matches( m_params.m_netFilter ) ) + continue; + for( PCB_LAYER_ID layer : zone->GetLayerSet().CuStack() ) { SHAPE_POLY_SET copper_shape; zone->TransformSolidAreasShapesToPolygon( layer, copper_shape ); copper_shape.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + copper_shape.SimplifyOutlines( + ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel ); + m_pcbModel->AddCopperPolygonShapes( &copper_shape, layer, aOrigin, false ); } } @@ -348,6 +393,9 @@ bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin ) if( !graphic ) return false; + if( !m_params.m_netFilter.IsEmpty() && !graphic->GetNetname().Matches( m_params.m_netFilter ) ) + return true; + PCB_LAYER_ID pcblayer = graphic->GetLayer(); if( !IsCopperLayer( pcblayer ) ) @@ -405,6 +453,7 @@ bool EXPORTER_STEP::buildBoard3DShapes() m_pcbModel->SetStackup( m_board->GetDesignSettings().GetStackupDescriptor() ); m_pcbModel->SetEnabledLayers( layersToExport ); m_pcbModel->SetFuseShapes( m_params.m_fuseShapes ); + m_pcbModel->SetNetFilter( m_params.m_netFilter ); // Note: m_params.m_BoardOutlinesChainingEpsilon is used only to build the board outlines, // not to set OCC chaining epsilon (much smaller) diff --git a/pcbnew/exporters/step/exporter_step.h b/pcbnew/exporters/step/exporter_step.h index 3f82da8229..06907824aa 100644 --- a/pcbnew/exporters/step/exporter_step.h +++ b/pcbnew/exporters/step/exporter_step.h @@ -73,6 +73,7 @@ public: }; wxString m_outputFile; + wxString m_netFilter; VECTOR2D m_origin; diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 03ee8f28b6..11062f1959 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -108,7 +108,6 @@ #include #include -#include static constexpr double USER_PREC = 1e-4; static constexpr double USER_ANGLE_PREC = 1e-6; @@ -510,7 +509,7 @@ bool STEP_PCB_MODEL::AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin ) { TopoDS_Shape fusedShape = mkFuse.Shape(); - ShapeUpgrade_UnifySameDomain unify( fusedShape, false, true, false ); + ShapeUpgrade_UnifySameDomain unify( fusedShape, true, true, false ); unify.History() = nullptr; unify.Build(); @@ -821,6 +820,12 @@ void STEP_PCB_MODEL::SetStackup( const BOARD_STACKUP& aStackup ) } +void STEP_PCB_MODEL::SetNetFilter( const wxString& aFilter ) +{ + m_netFilter = aFilter; +} + + void STEP_PCB_MODEL::SetBoardColor( double r, double g, double b ) { m_boardColor[0] = r; @@ -1055,7 +1060,6 @@ bool STEP_PCB_MODEL::MakeShapes( std::vector& aShapes, const SHAPE { SHAPE_POLY_SET simplified = aPolySet; simplified.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - simplified.SimplifyOutlines( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel ); auto toPoint = [&]( const VECTOR2D& aKiCoords ) -> gp_Pnt { @@ -1637,11 +1641,11 @@ bool STEP_PCB_MODEL::CreatePCB( SHAPE_POLY_SET& aOutline, VECTOR2D aOrigin, bool if( mkFuse.IsDone() ) { - ReportMessage( wxT( "Removing extra faces\n" ) ); + ReportMessage( wxT( "Removing extra edges/faces\n" ) ); TopoDS_Shape fusedShape = mkFuse.Shape(); - ShapeUpgrade_UnifySameDomain unify( fusedShape, false, true, false ); + ShapeUpgrade_UnifySameDomain unify( fusedShape, true, true, false ); unify.History() = nullptr; unify.Build(); diff --git a/pcbnew/exporters/step/step_pcb_model.h b/pcbnew/exporters/step/step_pcb_model.h index 4f6c879969..ae752ae2f8 100644 --- a/pcbnew/exporters/step/step_pcb_model.h +++ b/pcbnew/exporters/step/step_pcb_model.h @@ -111,6 +111,8 @@ public: void SetStackup( const BOARD_STACKUP& aStackup ); + void SetNetFilter( const wxString& aFilter ); + // 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 ); @@ -239,6 +241,7 @@ private: double m_copperColor[3]; // copper, RGB values BOARD_STACKUP m_stackup; // board stackup LSET m_enabledLayers; // a set of layers enabled for export + wxString m_netFilter; // remove nets not matching this wildcard double m_minx; // leftmost curve point double m_mergeOCCMaxDist; // minimum distance (mm) below which two diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp index 6def196da2..a1d404a32c 100644 --- a/pcbnew/pcbnew_jobs_handler.cpp +++ b/pcbnew/pcbnew_jobs_handler.cpp @@ -206,6 +206,7 @@ int PCBNEW_JOBS_HANDLER::JobExportStep( JOB* aJob ) params.m_useGridOrigin = aStepJob->m_useGridOrigin; params.m_boardOnly = aStepJob->m_boardOnly; params.m_optimizeStep = aStepJob->m_optimizeStep; + params.m_netFilter = aStepJob->m_netFilter; switch( aStepJob->m_format ) {