#include "../libslic3r.h" #include "../Model.hpp" #include "../TriangleMesh.hpp" #include "STEP.hpp" #include #include #include #include #include #include #ifdef _WIN32 #define DIR_SEPARATOR '\\' #else #define DIR_SEPARATOR '/' #endif #include "STEPCAFControl_Reader.hxx" #include "BRepMesh_IncrementalMesh.hxx" #include "Interface_Static.hxx" #include "XCAFDoc_DocumentTool.hxx" #include "XCAFDoc_ShapeTool.hxx" #include "XCAFApp_Application.hxx" #include "TopoDS_Solid.hxx" #include "TopoDS_Compound.hxx" #include "TopoDS_Builder.hxx" #include "TopoDS.hxx" #include "TDataStd_Name.hxx" #include "BRepBuilderAPI_Transform.hxx" #include "TopExp_Explorer.hxx" #include "TopExp_Explorer.hxx" #include "BRep_Tool.hxx" const double STEP_TRANS_CHORD_ERROR = 0.003; const double STEP_TRANS_ANGLE_RES = 0.5; namespace Slic3r { bool StepPreProcessor::preprocess(const char* path, std::string &output_path) { boost::nowide::ifstream infile(path); if (!infile.good()) { throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n")); return false; } boost::filesystem::path temp_path(temporary_dir()); std::string temp_step_path = temp_path.string() + "/temp.step"; boost::nowide::remove(temp_step_path.c_str()); boost::nowide::ofstream temp_file(temp_step_path, std::ios::app); std::string temp_line; while (std::getline(infile, temp_line)) { if (m_encode_type == EncodedType::UTF8) { //BBS: continue to judge whether is other type if (isUtf8(temp_line)) { //BBS: do nothing, but must be checked before checking whether is GBK } //BBS: not utf8, then maybe GBK else if (isGBK(temp_line)) { m_encode_type = EncodedType::GBK; } //BBS: not UTF8 and not GBK, then maybe some kind of special encoded type which we can't handle // Load the step as UTF and user will see garbage characters in slicer but we have no solution at the moment else { m_encode_type = EncodedType::OTHER; } } if (m_encode_type == EncodedType::GBK) //BBS: transform to UTF8 format if is GBK //todo: use gbkToUtf8 function to replace temp_file << decode_path(temp_line.c_str()) << std::endl; else temp_file << temp_line.c_str() << std::endl; } temp_file.close(); infile.close(); if (m_encode_type == EncodedType::GBK) { output_path = temp_step_path; } else { boost::nowide::remove(temp_step_path.c_str()); output_path = std::string(path); } return true; } bool StepPreProcessor::isUtf8File(const char* path) { boost::nowide::ifstream infile(path); if (!infile.good()) { throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n")); return false; } std::string temp_line; while (std::getline(infile, temp_line)) { if (!isUtf8(temp_line)) { infile.close(); return false; } } infile.close(); return true; } bool StepPreProcessor::isUtf8(const std::string str) { size_t num = 0; int i = 0; while (i < str.length()) { if ((str[i] & 0x80) == 0x00) { i++; } else if ((num = preNum(str[i])) > 2) { i++; for (int j = 0; j < num - 1; j++) { if ((str[i] & 0xc0) != 0x80) return false; i++; } } else { return false; } } return true; } bool StepPreProcessor::isGBK(const std::string str) { size_t i = 0; while (i < str.length()) { if (str[i] <= 0x7f) { i++; continue; } else { if (str[i] >= 0x81 && str[i] <= 0xfe && str[i + 1] >= 0x40 && str[i + 1] <= 0xfe && str[i + 1] != 0xf7) { i += 2; continue; } else { return false; } } } return true; } int StepPreProcessor::preNum(const unsigned char byte) { unsigned char mask = 0x80; int num = 0; for (int i = 0; i < 8; i++) { if ((byte & mask) == mask) { mask = mask >> 1; num++; } else { break; } } return num; } struct NamedSolid { NamedSolid(const TopoDS_Shape& s, const std::string& n) : solid{s}, name{n} {} const TopoDS_Shape solid; const std::string name; }; static void getNamedSolids(const TopLoc_Location& location, const std::string& prefix, unsigned int& id, const Handle(XCAFDoc_ShapeTool) shapeTool, const TDF_Label label, std::vector& namedSolids) { TDF_Label referredLabel{label}; if (shapeTool->IsReference(label)) shapeTool->GetReferredShape(label, referredLabel); std::string name; Handle(TDataStd_Name) shapeName; if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName)) name = TCollection_AsciiString(shapeName->Get()).ToCString(); if (name == "") name = std::to_string(id++); std::string fullName{name}; TopLoc_Location localLocation = location * shapeTool->GetLocation(label); TDF_LabelSequence components; if (shapeTool->GetComponents(referredLabel, components)) { for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex) { getNamedSolids(localLocation, fullName, id, shapeTool, components.Value(compIndex), namedSolids); } } else { TopoDS_Shape shape; shapeTool->GetShape(referredLabel, shape); TopAbs_ShapeEnum shape_type = shape.ShapeType(); BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True); switch (shape_type) { case TopAbs_COMPOUND: namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), fullName); break; case TopAbs_COMPSOLID: namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName); break; case TopAbs_SOLID: namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName); break; default: break; } } } bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn) { bool cb_cancel = false; if (stepFn) { stepFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel); is_cancel = cb_cancel; if (cb_cancel) { return false; } } if (!StepPreProcessor::isUtf8File(path) && isUtf8Fn) isUtf8Fn(false); std::string file_after_preprocess = std::string(path); std::vector namedSolids; Handle(TDocStd_Document) document; Handle(XCAFApp_Application) application = XCAFApp_Application::GetApplication(); application->NewDocument(file_after_preprocess.c_str(), document); STEPCAFControl_Reader reader; reader.SetNameMode(true); //BBS: Todo, read file is slow which cause the progress_bar no update and gui no response IFSelect_ReturnStatus stat = reader.ReadFile(file_after_preprocess.c_str()); if (stat != IFSelect_RetDone || !reader.Transfer(document)) { application->Close(document); throw std::logic_error{ std::string{"Could not read '"} + path + "'" }; return false; } Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main()); TDF_LabelSequence topLevelShapes; shapeTool->GetFreeShapes(topLevelShapes); unsigned int id{1}; Standard_Integer topShapeLength = topLevelShapes.Length() + 1; auto stage_unit2 = topShapeLength / LOAD_STEP_STAGE_UNIT_NUM + 1; for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) { if (stepFn) { if ((iLabel % stage_unit2) == 0) { stepFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel); is_cancel = cb_cancel; } if (cb_cancel) { shapeTool.reset(nullptr); application->Close(document); return false; } } getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids); } std::vector stl; stl.resize(namedSolids.size()); tbb::parallel_for(tbb::blocked_range(0, namedSolids.size()), [&](const tbb::blocked_range &range) { for (size_t i = range.begin(); i < range.end(); i++) { BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true); // BBS: calculate total number of the nodes and triangles int aNbNodes = 0; int aNbTriangles = 0; for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) { TopLoc_Location aLoc; Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc); if (!aTriangulation.IsNull()) { aNbNodes += aTriangulation->NbNodes(); aNbTriangles += aTriangulation->NbTriangles(); } } if (aNbTriangles == 0 || aNbNodes == 0) // BBS: No triangulation on the shape. continue; stl[i].stats.type = inmemory; stl[i].stats.number_of_facets = (uint32_t) aNbTriangles; stl[i].stats.original_num_facets = stl[i].stats.number_of_facets; stl_allocate(&stl[i]); std::vector points; points.reserve(aNbNodes); // BBS: count faces missing triangulation Standard_Integer aNbFacesNoTri = 0; // BBS: fill temporary triangulation Standard_Integer aNodeOffset = 0; Standard_Integer aTriangleOffet = 0; for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) { const TopoDS_Shape &aFace = anExpSF.Current(); TopLoc_Location aLoc; Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc); if (aTriangulation.IsNull()) { ++aNbFacesNoTri; continue; } // BBS: copy nodes gp_Trsf aTrsf = aLoc.Transformation(); for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) { gp_Pnt aPnt = aTriangulation->Node(aNodeIter); aPnt.Transform(aTrsf); points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z()))); } // BBS: copy triangles const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation(); Standard_Integer anId[3]; for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) { Poly_Triangle aTri = aTriangulation->Triangle(aTriIter); aTri.Get(anId[0], anId[1], anId[2]); if (anOrientation == TopAbs_REVERSED) std::swap(anId[1], anId[2]); // BBS: save triangles facets stl_facet facet; facet.vertex[0] = points[anId[0] + aNodeOffset - 1].cast(); facet.vertex[1] = points[anId[1] + aNodeOffset - 1].cast(); facet.vertex[2] = points[anId[2] + aNodeOffset - 1].cast(); facet.extra[0] = 0; facet.extra[1] = 0; stl_normal normal; stl_calculate_normal(normal, &facet); stl_normalize_vector(normal); facet.normal = normal; stl[i].facet_start[aTriangleOffet + aTriIter - 1] = facet; } aNodeOffset += aTriangulation->NbNodes(); aTriangleOffet += aTriangulation->NbTriangles(); } } }); ModelObject *new_object = model->add_object(); const char * last_slash = strrchr(path, DIR_SEPARATOR); new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1); new_object->input_file = path; auto stage_unit3 = stl.size() / LOAD_STEP_STAGE_UNIT_NUM + 1; for (size_t i = 0; i < stl.size(); i++) { if (stepFn) { if ((i % stage_unit3) == 0) { stepFn(LOAD_STEP_STAGE_GET_MESH, i, stl.size(), cb_cancel); is_cancel = cb_cancel; } if (cb_cancel) { model->delete_object(new_object); shapeTool.reset(nullptr); application->Close(document); return false; } } //BBS: maybe mesh is empty from step file. Don't add if (stl[i].stats.number_of_facets > 0) { TriangleMesh triangle_mesh; triangle_mesh.from_stl(stl[i]); ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh)); new_volume->name = namedSolids[i].name; new_volume->source.input_file = path; new_volume->source.object_idx = (int)model->objects.size() - 1; new_volume->source.volume_idx = (int)new_object->volumes.size() - 1; } } shapeTool.reset(nullptr); application->Close(document); //BBS: no valid shape from the step, delete the new object as well if (new_object->volumes.size() == 0) { model->delete_object(new_object); return false; } return true; } }; // namespace Slic3r