diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index a7a8551314..4b7bf7f168 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -127,6 +127,9 @@ protected: return m_cbOverwriteFile->GetValue(); } + // Called to update filename extension after the output file format is changed + void OnFmtChoiceOptionChanged(); + private: enum class COMPONENT_MODE { @@ -161,8 +164,8 @@ private: }; -int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default -int DIALOG_EXPORT_STEP::m_formatLastChoice = -1; // Use default +int DIALOG_EXPORT_STEP::m_toleranceLastChoice = -1; // Use default +int DIALOG_EXPORT_STEP::m_formatLastChoice = -1; // Use default bool DIALOG_EXPORT_STEP::m_optimizeStep = true; bool DIALOG_EXPORT_STEP::m_exportBoardBody = true; bool DIALOG_EXPORT_STEP::m_exportComponents = true; @@ -302,6 +305,10 @@ DIALOG_EXPORT_STEP::DIALOG_EXPORT_STEP( PCB_EDIT_FRAME* aParent, const wxString& if( m_formatLastChoice >= 0 ) m_choiceFormat->SetSelection( m_formatLastChoice ); + else + // ensure the selected fmt and the output file ext are synchronized the first time + // the dialog is opened + OnFmtChoiceOptionChanged(); // Now all widgets have the size fixed, call FinishDialogSettings finishDialogSettings(); @@ -474,6 +481,12 @@ void DIALOG_EXPORT_STEP::onBrowseClicked( wxCommandEvent& aEvent ) void DIALOG_EXPORT_STEP::onFormatChoice( wxCommandEvent& event ) +{ + OnFmtChoiceOptionChanged(); +} + + +void DIALOG_EXPORT_STEP::OnFmtChoiceOptionChanged() { wxString newExt = c_formatCommand[m_choiceFormat->GetSelection()]; wxString path = m_outputFileName->GetValue(); diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp index b69304decb..4443ba22ce 100644 --- a/pcbnew/exporters/step/exporter_step.cpp +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -514,6 +514,35 @@ bool EXPORTER_STEP::buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin ) } +void EXPORTER_STEP::initOutputVariant() +{ + // Specialize the STEP_PCB_MODEL generator for specific output format + // it can have some minor actions for the generator + switch( m_params.m_Format ) + { + case EXPORTER_STEP_PARAMS::FORMAT::STEP: + m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_STEP ); + break; + + case EXPORTER_STEP_PARAMS::FORMAT::BREP: + m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_BREP ); + break; + + case EXPORTER_STEP_PARAMS::FORMAT::XAO: + m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_XAO ); + break; + + case EXPORTER_STEP_PARAMS::FORMAT::GLB: + m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_GLTF ); + break; + + default: + m_pcbModel->SpecializeVariant( OUTPUT_FORMAT::FMT_OUT_UNKNOWN ); + break; + } +} + + bool EXPORTER_STEP::buildBoard3DShapes() { if( m_pcbModel ) @@ -541,6 +570,8 @@ bool EXPORTER_STEP::buildBoard3DShapes() m_pcbModel = std::make_unique<STEP_PCB_MODEL>( m_pcbBaseName ); + initOutputVariant(); + m_pcbModel->SetCopperColor( m_copperColor.r, m_copperColor.g, m_copperColor.b ); m_pcbModel->SetPadColor( m_padColor.r, m_padColor.g, m_padColor.b ); diff --git a/pcbnew/exporters/step/exporter_step.h b/pcbnew/exporters/step/exporter_step.h index 3989225e0d..773632f9a2 100644 --- a/pcbnew/exporters/step/exporter_step.h +++ b/pcbnew/exporters/step/exporter_step.h @@ -62,6 +62,7 @@ private: bool buildTrack3DShape( PCB_TRACK* aTrack, VECTOR2D aOrigin ); void buildZones3DShape( VECTOR2D aOrigin ); bool buildGraphic3DShape( BOARD_ITEM* aItem, VECTOR2D aOrigin ); + void initOutputVariant(); EXPORTER_STEP_PARAMS m_params; std::unique_ptr<FILENAME_RESOLVER> m_resolver; diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 51db2ebde9..07768ee4c2 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -179,30 +179,51 @@ MODEL3D_FORMAT_TYPE fileType( const char* aFileName ) return FMT_NONE; char iline[82]; - memset( iline, 0, 82 ); - ifile.getline( iline, 82 ); + MODEL3D_FORMAT_TYPE format_type = FMT_NONE; + + // The expected header should be the first line. + // However some files can have a comment at the beginning of the file + // So read up to max_line_count lines to try to find the actual header + const int max_line_count = 3; + + for( int ii = 0; ii < max_line_count; ii++ ) + { + memset( iline, 0, 82 ); + ifile.getline( iline, 82 ); + + iline[81] = 0; // ensure NULL termination when string is too long + + // check for STEP in Part 21 format + // (this can give false positives since Part 21 is not exclusively STEP) + if( !strncmp( iline, "ISO-10303-21;", 13 ) ) + { + format_type = FMT_STEP; + break; + } + + std::string fstr = iline; + + // check for STEP in XML format + // (this can give both false positive and false negatives) + if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos ) + { + format_type = FMT_STEP; + break; + } + + // Note: this is a very simple test which can yield false positives; the only + // sure method for determining if a file *not* an IGES model is to attempt + // to load it. + if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) ) + { + format_type = FMT_IGES; + break; + } + } + CLOSE_STREAM( ifile ); - iline[81] = 0; // ensure NULL termination when string is too long - // check for STEP in Part 21 format - // (this can give false positives since Part 21 is not exclusively STEP) - if( !strncmp( iline, "ISO-10303-21;", 13 ) ) - return FMT_STEP; - - std::string fstr = iline; - - // check for STEP in XML format - // (this can give both false positive and false negatives) - if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos ) - return FMT_STEP; - - // Note: this is a very simple test which can yield false positives; the only - // sure method for determining if a file *not* an IGES model is to attempt - // to load it. - if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) ) - return FMT_IGES; - - return FMT_NONE; + return format_type; } @@ -743,6 +764,7 @@ STEP_PCB_MODEL::STEP_PCB_MODEL( const wxString& aPcbName ) m_pcbName = aPcbName; m_maxError = pcbIUScale.mmToIU( ARC_TO_SEGMENT_MAX_ERROR_MM ); m_fuseShapes = false; + m_outFmt = OUTPUT_FORMAT::FMT_OUT_UNKNOWN; } @@ -2091,6 +2113,8 @@ bool STEP_PCB_MODEL::WriteIGES( const wxString& aFileName ) return false; } + m_outFmt = OUTPUT_FORMAT::FMT_OUT_IGES; + wxFileName fn( aFileName ); IGESControl_Controller::Init(); IGESCAFControl_Writer writer; @@ -2123,6 +2147,8 @@ bool STEP_PCB_MODEL::WriteSTEP( const wxString& aFileName, bool aOptimize ) return false; } + m_outFmt = OUTPUT_FORMAT::FMT_OUT_STEP; + wxFileName fn( aFileName ); STEPCAFControl_Writer writer; @@ -2202,6 +2228,8 @@ bool STEP_PCB_MODEL::WriteBREP( const wxString& aFileName ) return false; } + m_outFmt = OUTPUT_FORMAT::FMT_OUT_BREP; + // s_assy = shape tool for the source Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() ); @@ -2236,6 +2264,8 @@ bool STEP_PCB_MODEL::WriteXAO( const wxString& aFileName ) return false; } + m_outFmt = OUTPUT_FORMAT::FMT_OUT_XAO; + // s_assy = shape tool for the source Handle( XCAFDoc_ShapeTool ) s_assy = XCAFDoc_DocumentTool::ShapeTool( m_doc->Main() ); @@ -2562,19 +2592,22 @@ bool STEP_PCB_MODEL::getModelLabel( const std::string& aFileNameUTF8, VECTOR3D a // VRML models only work when exporting to glTF // Also OCCT < 7.9.0 fail to load most VRML 2.0 models because of Switch nodes - if( readVRML( doc, aFileNameUTF8.c_str() ) ) + if( m_outFmt == OUTPUT_FORMAT::FMT_OUT_GLTF ) { - Handle( XCAFDoc_ShapeTool ) shapeTool = - XCAFDoc_DocumentTool::ShapeTool( doc->Main() ); + if( readVRML( doc, aFileNameUTF8.c_str() ) ) + { + Handle( XCAFDoc_ShapeTool ) shapeTool = + XCAFDoc_DocumentTool::ShapeTool( doc->Main() ); - prefixNames( shapeTool->Label(), - TCollection_ExtendedString( baseName.c_str().AsChar() ) ); - } - else - { - ReportMessage( wxString::Format( wxT( "readVRML() failed on filename '%s'.\n" ), - fileName ) ); - return false; + prefixNames( shapeTool->Label(), + TCollection_ExtendedString( baseName.c_str().AsChar() ) ); + } + else + { + ReportMessage( wxString::Format( wxT( "readVRML() failed on filename '%s'.\n" ), + fileName ) ); + return false; + } } } else // Substitution is not allowed @@ -2590,6 +2623,8 @@ bool STEP_PCB_MODEL::getModelLabel( const std::string& aFileNameUTF8, VECTOR3D a // TODO: implement IDF and EMN converters default: + ReportMessage( wxString::Format( wxT( "Cannot identify actual file type for '%s'.\n" ), + fileName ) ); return false; } @@ -2844,6 +2879,8 @@ bool STEP_PCB_MODEL::WriteGLTF( const wxString& aFileName ) return false; } + m_outFmt = OUTPUT_FORMAT::FMT_OUT_GLTF; + TDF_LabelSequence freeShapes; m_assy->GetFreeShapes( freeShapes ); diff --git a/pcbnew/exporters/step/step_pcb_model.h b/pcbnew/exporters/step/step_pcb_model.h index 7941ec1c60..e888f33522 100644 --- a/pcbnew/exporters/step/step_pcb_model.h +++ b/pcbnew/exporters/step/step_pcb_model.h @@ -75,12 +75,25 @@ typedef std::map< std::string, TDF_Label > MODEL_MAP; extern void ReportMessage( const wxString& aMessage ); +enum class OUTPUT_FORMAT +{ + FMT_OUT_UNKNOWN = 0, + FMT_OUT_STEP, + FMT_OUT_IGES, + FMT_OUT_BREP, + FMT_OUT_XAO, + FMT_OUT_GLTF +}; + class STEP_PCB_MODEL { public: STEP_PCB_MODEL( const wxString& aPcbName ); virtual ~STEP_PCB_MODEL(); + // Update m_outFmt to aVariant, giving the output format variant + void SpecializeVariant( OUTPUT_FORMAT aVariant ) { m_outFmt = aVariant; } + // add a pad shape (must be in final position) bool AddPadShape( const PAD* aPad, const VECTOR2D& aOrigin, bool aVia ); @@ -255,9 +268,12 @@ private: std::map<wxString, std::pair<gp_Pnt, TopoDS_Shape>> m_pad_points; /// Name of the PCB, which will most likely be the file name of the path. - wxString m_pcbName; + wxString m_pcbName; - int m_maxError; + int m_maxError; + + /// The current output format for created file + OUTPUT_FORMAT m_outFmt; }; #endif // OCE_VIS_OCE_UTILS_H diff --git a/plugins/3d/oce/loadmodel.cpp b/plugins/3d/oce/loadmodel.cpp index 5aba51c75b..41687cb77f 100644 --- a/plugins/3d/oce/loadmodel.cpp +++ b/plugins/3d/oce/loadmodel.cpp @@ -296,27 +296,39 @@ FormatType fileType( const char* aFileName ) } char iline[82]; - memset( iline, 0, 82 ); - ifile.Read( iline, 82 ); - iline[81] = 0; // ensure NULL termination when string is too long - // check for STEP in Part 21 format - // (this can give false positives since Part 21 is not exclusively STEP) - if( !strncmp( iline, "ISO-10303-21;", 13 ) ) - return FMT_STEP; + // The expected header should be the first line. + // However some files can have a comment at the beginning of the file + // So read up to max_line_count lines to try to find the actual header + const int max_line_count = 3; - std::string fstr = iline; + for( int ii = 0; ii < max_line_count; ii++ ) + { + memset( iline, 0, 82 ); + ifile.Read( iline, 82 ); + iline[81] = 0; // ensure NULL termination when string is too long - // check for STEP in XML format - // (this can give both false positive and false negatives) - if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos ) - return FMT_STEP; + // check for STEP in Part 21 format + // (this can give false positives since Part 21 is not exclusively STEP) + if( !strncmp( iline, "ISO-10303-21;", 13 ) ) + { + return FMT_STEP; + break; + } - // Note: this is a very simple test which can yield false positives; the only - // sure method for determining if a file *not* an IGES model is to attempt - // to load it. - if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) ) - return FMT_IGES; + std::string fstr = iline; + + // check for STEP in XML format + // (this can give both false positive and false negatives) + if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos ) + return FMT_STEP; + + // Note: this is a very simple test which can yield false positives; the only + // sure method for determining if a file *not* an IGES model is to attempt + // to load it. + if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) ) + return FMT_IGES; + } return FMT_NONE; }