diff --git a/resources/images/step_mesh_info.svg b/resources/images/step_mesh_info.svg new file mode 100644 index 0000000000..28f840853a --- /dev/null +++ b/resources/images/step_mesh_info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 4586a7f52d..6b4954a024 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -1386,7 +1386,7 @@ int CLI::run(int argc, char **argv) // BBS: adjust whebackup //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances; //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary; - model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice); + model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, plate_to_slice); if (is_bbl_3mf) { if (!first_file) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 9a09335e97..588d1e0c0a 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -411,6 +411,17 @@ void AppConfig::set_defaults() set_str("print", "timelapse", "1"); } + if (get("enable_step_mesh_setting").empty()) { + set_bool("enable_step_mesh_setting", true); + } + if (get("linear_defletion", "angle_defletion").empty()) { + set("linear_defletion", "0.003"); + set("angle_defletion", "0.5"); + } + if (get("is_split_compound").empty()) { + set_bool("is_split_compound", false); + } + // Remove legacy window positions/sizes erase("app", "main_frame_maximized"); erase("app", "main_frame_pos"); diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index 45e938d1b6..5105d26776 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -33,9 +33,8 @@ #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; +#include "BRepTools.hxx" +#include namespace Slic3r { @@ -166,26 +165,24 @@ int StepPreProcessor::preNum(const unsigned char byte) { 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) { +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, + bool isSplitCompound = false) { 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)) + if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName) || + label.FindAttribute(TDataStd_Name::GetID(), shapeName)) name = TCollection_AsciiString(shapeName->Get()).ToCString(); - if (name == "") + if (name == "" || !StepPreProcessor::isUtf8(name)) name = std::to_string(id++); std::string fullName{name}; @@ -193,7 +190,7 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p 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); + getNamedSolids(localLocation, fullName, id, shapeTool, components.Value(compIndex), namedSolids, isSplitCompound); } } else { TopoDS_Shape shape; @@ -204,12 +201,20 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p int i = 0; switch (shape_type) { case TopAbs_COMPOUND: + if (!isSplitCompound) { + namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), fullName); + break; + } case TopAbs_COMPSOLID: - for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) { - i++; - const TopoDS_Shape& currentShape = explorer.Current(); - namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i)); - } + if (!isSplitCompound) { + namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName); + } else { + for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) { + i++; + const TopoDS_Shape& currentShape = explorer.Current(); + namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i)); + } + } break; case TopAbs_SOLID: namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName); @@ -223,7 +228,11 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p } } -bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn) +bool load_step(const char *path, Model *model, bool& is_cancel, + double linear_defletion/*=0.003*/, + double angle_defletion/*= 0.5*/, + bool isSplitCompound, + ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn, long& mesh_face_num) { bool cb_cancel = false; if (stepFn) { @@ -271,14 +280,14 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre return false; } } - getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids); + getNamedSolids(TopLoc_Location{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids, isSplitCompound); } 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); + BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, linear_defletion, false, angle_defletion, true); // BBS: calculate total number of the nodes and triangles int aNbNodes = 0; int aNbTriangles = 0; @@ -324,7 +333,7 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre } // BBS: copy triangles const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation(); - Standard_Integer anId[3]; + Standard_Integer anId[3] = {}; for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) { Poly_Triangle aTri = aTriangulation->Triangle(aTriIter); @@ -351,6 +360,14 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre } }); + if (mesh_face_num != -1) { + for (size_t i = 0; i < stl.size(); i++) { + // Test for overflow + mesh_face_num += stl[i].stats.number_of_facets; + } + return true; + } + 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); @@ -395,4 +412,109 @@ bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgre return true; } +Step::Step(fs::path path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn): + m_stepFn(stepFn), + m_utf8Fn(isUtf8Fn) +{ + m_path = path.string(); + m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc); +} + +Step::Step(std::string path, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn) : + m_path(path), + m_stepFn(stepFn), + m_utf8Fn(isUtf8Fn) +{ + m_app->NewDocument(TCollection_ExtendedString("BinXCAF"), m_doc); +} + +bool Step::load() +{ + if (!StepPreProcessor::isUtf8File(m_path.c_str()) && m_utf8Fn) { + m_utf8Fn(false); + return false; + } + + STEPCAFControl_Reader reader; + reader.SetNameMode(true); + IFSelect_ReturnStatus stat = reader.ReadFile(m_path.c_str()); + if (stat != IFSelect_RetDone || !reader.Transfer(m_doc)) { + m_app->Close(m_doc); + return false; + } + m_shape_tool = XCAFDoc_DocumentTool::ShapeTool(m_doc->Main()); + TDF_LabelSequence topLevelShapes; + m_shape_tool->GetFreeShapes(topLevelShapes); + unsigned int id{ 1 }; + Standard_Integer topShapeLength = topLevelShapes.Length() + 1; + for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) { + getNamedSolids(TopLoc_Location{}, "", id, m_shape_tool, topLevelShapes.Value(iLabel), m_name_solids); + } + + return true; +} + +void Step::clean_mesh_data() +{ + for (const auto& name_solid : m_name_solids) { + BRepTools::Clean(name_solid.solid); + } +} + +unsigned int Step::get_triangle_num(double linear_defletion, double angle_defletion) +{ + unsigned int tri_num = 0; + try { + Handle(StepProgressIncdicator) progress = new StepProgressIncdicator(m_stop_mesh); + clean_mesh_data(); + IMeshTools_Parameters param; + param.Deflection = linear_defletion; + param.Angle = angle_defletion; + param.InParallel = true; + for (int i = 0; i < m_name_solids.size(); ++i) { + BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, param, progress->Start()); + for (TopExp_Explorer anExpSF(m_name_solids[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()) { + tri_num += aTriangulation->NbTriangles(); + } + } + if (m_stop_mesh.load()) { + return 0; + } + } + } catch(Exception e) { + return 0; + } + + return tri_num; +} + +unsigned int Step::get_triangle_num_tbb(double linear_defletion, double angle_defletion) +{ + unsigned int tri_num = 0; + clean_mesh_data(); + tbb::parallel_for(tbb::blocked_range(0, m_name_solids.size()), + [&](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); i++) { + unsigned int solids_tri_num = 0; + BRepMesh_IncrementalMesh mesh(m_name_solids[i].solid, linear_defletion, false, angle_defletion, true); + for (TopExp_Explorer anExpSF(m_name_solids[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()) { + solids_tri_num += aTriangulation->NbTriangles(); + } + } + m_name_solids[i].tri_face_cout = solids_tri_num; + } + + }); + for (int i = 0; i < m_name_solids.size(); ++i) { + tri_num += m_name_solids[i].tri_face_cout; + } + return tri_num; +} + }; // namespace Slic3r diff --git a/src/libslic3r/Format/STEP.hpp b/src/libslic3r/Format/STEP.hpp index b86933a346..85c8f655ef 100644 --- a/src/libslic3r/Format/STEP.hpp +++ b/src/libslic3r/Format/STEP.hpp @@ -1,5 +1,14 @@ #ifndef slic3r_Format_STEP_hpp_ #define slic3r_Format_STEP_hpp_ +#include "XCAFDoc_DocumentTool.hxx" +#include "XCAFApp_Application.hxx" +#include "XCAFDoc_ShapeTool.hxx" +#include +#include +#include +#include + +namespace fs = boost::filesystem; namespace Slic3r { @@ -16,8 +25,25 @@ const int LOAD_STEP_STAGE_UNIT_NUM = 5; typedef std::function ImportStepProgressFn; typedef std::function StepIsUtf8Fn; +struct NamedSolid +{ + NamedSolid(const TopoDS_Shape& s, + const std::string& n) : solid{ s }, name{ n } { + } + const TopoDS_Shape solid; + const std::string name; + int tri_face_cout = 0; +}; + //BBS: Load an step file into a provided model. -extern bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr); +extern bool load_step(const char *path, Model *model, + bool& is_cancel, + double linear_defletion = 0.003, + double angle_defletion = 0.5, + bool isSplitCompound = false, + ImportStepProgressFn proFn = nullptr, + StepIsUtf8Fn isUtf8Fn = nullptr, + long& mesh_face_num = *(new long(-1))); //BBS: Used to detect what kind of encoded type is used in name field of step // If is encoded in UTF8, the file don't need to be handled, then return the original path directly. @@ -36,14 +62,49 @@ class StepPreProcessor { public: bool preprocess(const char* path, std::string &output_path); static bool isUtf8File(const char* path); -private: static bool isUtf8(const std::string str); +private: static bool isGBK(const std::string str); static int preNum(const unsigned char byte); //BBS: default is UTF8 for most step file. EncodedType m_encode_type = EncodedType::UTF8; }; +class StepProgressIncdicator : public Message_ProgressIndicator +{ +public: + StepProgressIncdicator(std::atomic& stop_flag) : should_stop(stop_flag){} + + Standard_Boolean UserBreak() override { return should_stop.load(); } + + void Show(const Message_ProgressScope&, const Standard_Boolean) override { + std::cout << "Progress: " << GetPosition() << "%" << std::endl; + } +private: + std::atomic& should_stop; +}; + +class Step +{ +public: + Step(fs::path path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr); + Step(std::string path, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr); + bool load(); + unsigned int get_triangle_num(double linear_defletion, double angle_defletion); + unsigned int get_triangle_num_tbb(double linear_defletion, double angle_defletion); + void clean_mesh_data(); + + std::atomic m_stop_mesh; +private: + std::string m_path; + ImportStepProgressFn m_stepFn; + StepIsUtf8Fn m_utf8Fn; + Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication(); + Handle(TDocStd_Document) m_doc; + Handle(XCAFDoc_ShapeTool) m_shape_tool; + std::vector m_name_solids; +}; + }; // namespace Slic3r #endif /* slic3r_Format_STEP_hpp_ */ diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index dcbf0120b8..fba27b289b 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -4,6 +4,7 @@ #include "../libslic3r.h" #include #include +#include namespace Slic3r { diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 699402c861..34e1949b98 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -27,6 +27,7 @@ #include "SVG.hpp" #include +#include #include "GCodeWriter.hpp" // BBS: for segment @@ -176,17 +177,68 @@ Model::~Model() Slic3r::remove_backup(*this, true); } +Model Model::read_from_step(const std::string& input_file, + LoadStrategy options, + ImportStepProgressFn stepFn, + StepIsUtf8Fn stepIsUtf8Fn, + std::function step_mesh_fn, + double linear_defletion, + double angle_defletion, + bool is_split_compound) +{ + Model model; + bool result = false; + bool is_cb_cancel = false; + std::string message; + Step step_file(input_file); + step_file.load(); + if (step_mesh_fn) { + if (step_mesh_fn(step_file, linear_defletion, angle_defletion, is_split_compound) == -1) { + Model empty_model; + return empty_model; + } + } + result = load_step(input_file.c_str(), &model, is_cb_cancel, linear_defletion, angle_defletion, is_split_compound, stepFn, stepIsUtf8Fn); + if (is_cb_cancel) { + Model empty_model; + return empty_model; + } + + if (!result) { + if (message.empty()) + throw Slic3r::RuntimeError(_L("Loading of a model file failed.")); + else + throw Slic3r::RuntimeError(message); + } + + if (model.objects.empty()) + throw Slic3r::RuntimeError(_L("The supplied file couldn't be read because it's empty")); + + for (ModelObject *o : model.objects) + o->input_file = input_file; + + if (options & LoadStrategy::AddDefaultInstances) + model.add_default_instances(); + + return model; +} + // BBS: add part plate related logic // BBS: backup & restore // Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well. -Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, - LoadStrategy options, PlateDataPtrs* plate_data, std::vector* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn, - ImportstlProgressFn stlFn, - ImportStepProgressFn stepFn, - StepIsUtf8Fn stepIsUtf8Fn, - BBLProject * project, - int plate_id, - ObjImportColorFn objFn) +Model Model::read_from_file(const std::string& input_file, + DynamicPrintConfig* config, + ConfigSubstitutionContext* config_substitutions, + LoadStrategy options, + PlateDataPtrs* plate_data, + std::vector* project_presets, + bool *is_xxx, + Semver* file_version, + Import3mfProgressFn proFn, + ImportstlProgressFn stlFn, + BBLProject * project, + int plate_id, + ObjImportColorFn objFn) { Model model; @@ -210,10 +262,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c bool result = false; bool is_cb_cancel = false; std::string message; - if (boost::algorithm::iends_with(input_file, ".stp") || - boost::algorithm::iends_with(input_file, ".step")) - result = load_step(input_file.c_str(), &model, is_cb_cancel, stepFn, stepIsUtf8Fn); - else if (boost::algorithm::iends_with(input_file, ".stl")) + if (boost::algorithm::iends_with(input_file, ".stl")) result = load_stl(input_file.c_str(), &model, nullptr, stlFn); else if (boost::algorithm::iends_with(input_file, ".oltp")) result = load_stl(input_file.c_str(), &model, nullptr, stlFn,256); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c61b539e82..3866e252b7 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -6,6 +6,7 @@ #include "Geometry.hpp" #include "ObjectID.hpp" #include "Point.hpp" +#include "AppConfig.hpp" #include "PrintConfig.hpp" #include "Slicing.hpp" #include "SLA/SupportPoint.hpp" @@ -1540,6 +1541,15 @@ public: OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model) + static Model read_from_step(const std::string& input_file, + LoadStrategy options, + ImportStepProgressFn stepFn, + StepIsUtf8Fn stepIsUtf8Fn, + std::function step_mesh_fn, + double linear_defletion, + double angle_defletion, + bool is_split_compound); + //BBS: add part plate related logic // BBS: backup //BBS: is_xxx is used for is_bbs_3mf when loading 3mf, is used for is_inches when loading amf @@ -1549,8 +1559,6 @@ public: LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr, std::vector* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr, ImportstlProgressFn stlFn = nullptr, - ImportStepProgressFn stepFn = nullptr, - StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject * project = nullptr, int plate_id = 0, ObjImportColorFn objFn = nullptr diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index ee250dab16..1bedfb482a 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -87,6 +87,7 @@ #include #include #include +#include // boost/property_tree/json_parser/detail/parser.hpp includes boost/bind.hpp, which is deprecated. // Suppress the following boost message: diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1e5ae34510..628aab75b5 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -312,6 +312,8 @@ set(SLIC3R_GUI_SOURCES GUI/RemovableDriveManager.hpp GUI/SendSystemInfoDialog.cpp GUI/SendSystemInfoDialog.hpp + GUI/StepMeshDialog.cpp + GUI/StepMeshDialog.hpp GUI/SurfaceDrag.cpp GUI/SurfaceDrag.hpp GUI/TextLines.cpp diff --git a/src/slic3r/GUI/CameraUtils.cpp b/src/slic3r/GUI/CameraUtils.cpp index fb8e84fb8a..a14e99c9c5 100644 --- a/src/slic3r/GUI/CameraUtils.cpp +++ b/src/slic3r/GUI/CameraUtils.cpp @@ -38,7 +38,7 @@ Points CameraUtils::project(const Camera & camera, return result; } -Point CameraUtils::project(const Camera &camera, const Vec3d &point) +Slic3r::Point CameraUtils::project(const Camera &camera, const Vec3d &point) { // IMPROVE: do it faster when you need it (inspire in project multi point) return project(camera, std::vector{point}).front(); diff --git a/src/slic3r/GUI/CameraUtils.hpp b/src/slic3r/GUI/CameraUtils.hpp index c3e938ec42..6e953bf6ee 100644 --- a/src/slic3r/GUI/CameraUtils.hpp +++ b/src/slic3r/GUI/CameraUtils.hpp @@ -25,7 +25,7 @@ public: /// projected points by camera into coordinate of camera. /// x(from left to right), y(from top to bottom) static Points project(const Camera& camera, const std::vector &points); - static Point project(const Camera& camera, const Vec3d &point); + static Slic3r::Point project(const Camera& camera, const Vec3d &point); /// /// Create hull around GLVolume in 2d space of camera diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp index e316861dbd..0d6480abd7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp @@ -174,6 +174,8 @@ protected: void find_single(); }; +wxDECLARE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent); + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 097c6dc58b..a99ace52f7 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -151,6 +151,7 @@ #include "DailyTips.hpp" #include "CreatePresetsDialog.hpp" #include "FileArchiveDialog.hpp" +#include "StepMeshDialog.hpp" using boost::optional; namespace fs = boost::filesystem; @@ -4155,32 +4156,25 @@ std::vector Plater::priv::load_files(const std::vector& input_ filament_ids.clear(); } }; - model = Slic3r::Model::read_from_file( - path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr, - [this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code) - { - designer_model_id = mode_id; - designer_country_code = code; - - bool cont = true; - float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files; - BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total; - progress_percent = (int)percent_float; - wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename)); - cont = dlg.Update(progress_percent, msg); - cancel = !cont; - }, - [this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel) - { - bool cont = true; - float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files; - BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total; - progress_percent = (int)percent_float; - wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename)); - cont = dlg.Update(progress_percent, msg); - cancel = !cont; - }, - [](int isUtf8StepFile) { + if (boost::iends_with(path.string(), ".stp") || + boost::iends_with(path.string(), ".step")) { + double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion")); + if (linear <= 0) linear = 0.003; + double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion")); + if (angle <= 0) angle = 0.5; + bool split_compound = wxGetApp().app_config->get_bool("is_split_compound"); + model = Slic3r::Model:: read_from_step(path.string(), strategy, + [this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel) + { + bool cont = true; + float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * ((float)step_percent[load_stage] + (float)current * (float)(step_percent[load_stage + 1] - step_percent[load_stage]) / (float)total) / (float)total_files; + BOOST_LOG_TRIVIAL(trace) << "load_step_file: percent(float)=" << percent_float << ", stage = " << load_stage << ", curr = " << current << ", total = " << total; + progress_percent = (int)percent_float; + wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename)); + cont = dlg.Update(progress_percent, msg); + cancel = !cont; + }, + [](int isUtf8StepFile) { if (!isUtf8StepFile) { const auto no_warn = wxGetApp().app_config->get_bool("step_not_utf8_no_warn"); if (!no_warn) { @@ -4194,8 +4188,42 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } }, - nullptr, 0, obj_color_fun); + [this, &path, &is_user_cancel, &linear, &angle, &split_compound](Slic3r::Step& file, double& linear_value, double& angle_value, bool& is_split)-> int { + if (wxGetApp().app_config->get_bool("enable_step_mesh_setting")) { + StepMeshDialog mesh_dlg(nullptr, file, linear, angle); + if (mesh_dlg.ShowModal() == wxID_OK) { + linear_value = mesh_dlg.get_linear_defletion(); + angle_value = mesh_dlg.get_angle_defletion(); + is_split = mesh_dlg.get_split_compound_value(); + return 1; + } + }else { + linear_value = linear; + angle_value = angle; + is_split = split_compound; + return 1; + } + is_user_cancel = true; + return -1; + }, linear, angle, split_compound); + }else { + model = Slic3r::Model:: read_from_file( + path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr, + [this, &dlg, real_filename, &progress_percent, &file_percent, INPUT_FILES_RATIO, total_files, i, &designer_model_id, &designer_country_code](int current, int total, bool &cancel, std::string &mode_id, std::string &code) + { + designer_model_id = mode_id; + designer_country_code = code; + bool cont = true; + float percent_float = (100.0f * (float)i / (float)total_files) + INPUT_FILES_RATIO * 100.0f * ((float)current / (float)total) / (float)total_files; + BOOST_LOG_TRIVIAL(trace) << "load_stl_file: percent(float)=" << percent_float << ", curr = " << current << ", total = " << total; + progress_percent = (int)percent_float; + wxString msg = wxString::Format(_L("Loading file: %s"), from_path(real_filename)); + cont = dlg.Update(progress_percent, msg); + cancel = !cont; + }, + nullptr, 0, obj_color_fun); + } if (designer_model_id.empty() && boost::algorithm::iends_with(path.string(), ".stl")) { read_binary_stl(path.string(), designer_model_id, designer_country_code); @@ -5937,8 +5965,17 @@ void Plater::priv::reload_from_disk() std::vector project_presets; // BBS: backup - new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun); + if (boost::iends_with(path, ".stp") || + boost::iends_with(path, ".step")) { + double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion")); + double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion")); + bool is_split = wxGetApp().app_config->get_bool("is_split_compound"); + new_model = Model::read_from_step(path, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, nullptr, nullptr, nullptr, linear, angle, is_split); + }else { + new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun); + } + + for (ModelObject* model_object : new_model.objects) { model_object->center_around_origin(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 3851495b2b..08ea9d0e18 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1225,6 +1225,7 @@ wxWindow* PreferencesDialog::create_general_page() auto item_calc_mode = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time the color changed."), page, _L("If enabled, auto-calculate every time the color changed."), 50, "auto_calculate"); auto item_calc_in_long_retract = create_item_checkbox(_L("Flushing volumes: Auto-calculate every time when the filament is changed."), page, _L("If enabled, auto-calculate every time when filament is changed"), 50, "auto_calculate_when_filament_change"); auto item_remember_printer_config = create_item_checkbox(_L("Remember printer configuration"), page, _L("If enabled, Orca will remember and switch filament/process configuration for each printer automatically."), 50, "remember_printer_config"); + auto item_step_mesh_setting = create_item_checkbox(_L("Show the step mesh parameter setting dialog."), page, _L("If enabled,a parameter settings dialog will appear during STEP file import."), 50, "enable_step_mesh_setting"); auto item_multi_machine = create_item_checkbox(_L("Multi-device Management(Take effect after restarting Orca)."), page, _L("With this option enabled, you can send a task to multiple devices at the same time and manage multiple devices."), 50, "enable_multi_machine"); auto item_auto_arrange = create_item_checkbox(_L("Auto arrange plate after cloning"), page, _L("Auto arrange plate after object cloning"), 50, "auto_arrange"); auto title_presets = create_item_title(_L("Presets"), page, _L("Presets")); @@ -1305,6 +1306,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_hints, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_calc_in_long_retract, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_multi_machine, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_step_mesh_setting, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_auto_arrange, 0, wxTOP, FromDIP(3)); sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_calc_mode, 0, wxTOP, FromDIP(3)); diff --git a/src/slic3r/GUI/StepMeshDialog.cpp b/src/slic3r/GUI/StepMeshDialog.cpp new file mode 100644 index 0000000000..c2ad838c66 --- /dev/null +++ b/src/slic3r/GUI/StepMeshDialog.cpp @@ -0,0 +1,386 @@ +#include "StepMeshDialog.hpp" + +#include +#include +#include +#include +#include +#include "GUI_App.hpp" +#include "I18N.hpp" +#include "MainFrame.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/TextInput.hpp" +#include + +using namespace Slic3r; +using namespace Slic3r::GUI; + +static int _scale(const int val) { return val * Slic3r::GUI::wxGetApp().em_unit() / 10; } +static int _ITEM_WIDTH() { return _scale(30); } +#define MIN_DIALOG_WIDTH FromDIP(400) +#define SLIDER_WIDTH FromDIP(200) +#define SLIDER_HEIGHT FromDIP(25) +#define TEXT_CTRL_WIDTH FromDIP(70) +#define BUTTON_SIZE wxSize(FromDIP(58), FromDIP(24)) +#define BUTTON_BORDER FromDIP(int(400 - 58 * 2) / 8) +#define SLIDER_SCALE(val) ((val) / 0.001) +#define SLIDER_UNSCALE(val) ((val) * 0.001) +#define SLIDER_SCALE_10(val) ((val) / 0.01) +#define SLIDER_UNSCALE_10(val) ((val) * 0.01) +#define LEFT_RIGHT_PADING FromDIP(20) +#define FONT_COLOR wxColour("#6B6B6B") + +wxDEFINE_EVENT(wxEVT_THREAD_DONE, wxCommandEvent); + +class CenteredStaticText : public wxStaticText +{ +public: + CenteredStaticText(wxWindow* parent, wxWindowID id, const wxString& label, const wxPoint& position, const wxSize& size = wxDefaultSize, long style = 0) + : wxStaticText(parent, id, label, position, size, style) { + CenterOnPosition(position); + } + + void CenterOnPosition(const wxPoint& position) { + int textWidth, textHeight; + GetTextExtent(GetLabel(), &textWidth, &textHeight); + int x = position.x - textWidth / 2; + int y = position.y - textHeight / 2; + SetPosition(wxPoint(x, y)); + } +}; + +void StepMeshDialog::on_dpi_changed(const wxRect& suggested_rect) { +}; + +bool StepMeshDialog:: validate_number_range(const wxString& value, double min, double max) { + double num = 0.0; + if (value.IsEmpty()) { + return false; + } + try { + if (!value.ToDouble(&num)) { + return false; + } + } catch (...) { + return false; + } + + return (num >= min && num <= max); +} + +StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init) + : DPIDialog(parent ? parent : static_cast(wxGetApp().mainframe), + wxID_ANY, + _(L("Step file import parameters")), + wxDefaultPosition, + wxDefaultSize, + wxDEFAULT_DIALOG_STYLE /* | wxRESIZE_BORDER*/), m_file(file) +{ + m_linear_last = wxString::Format("%.3f", linear_init); + m_angle_last = wxString::Format("%.2f", angle_init); + + Bind(wxEVT_THREAD_DONE, &StepMeshDialog::on_task_done, this); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") + % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + SetBackgroundColour(*wxWHITE); + + wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL); + bSizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1)); + + auto image_bitmap = create_scaled_bitmap("step_mesh_info", this, FromDIP(120)); + + // wxPanel* overlay_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, image_bitmap.GetSize(), wxTAB_TRAVERSAL); + // overlay_panel->SetBackgroundStyle(wxBG_STYLE_PAINT); + // wxStaticBitmap *image = new wxStaticBitmap(overlay_panel, wxID_ANY, image_bitmap, wxDefaultPosition, overlay_panel->GetSize(), 0); + + // CenteredStaticText* text_1 = new CenteredStaticText (overlay_panel, wxID_ANY, _L("Smooth"), + // wxPoint(overlay_panel->GetSize().GetWidth() / 6, + // overlay_panel->GetSize().GetHeight() / 2)); + // CenteredStaticText* text_2 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Rough"), + // wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6, + // overlay_panel->GetSize().GetHeight() / 2)); + // CenteredStaticText* text_3 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Linear"), + // wxPoint(overlay_panel->GetSize().GetWidth() / 2, + // overlay_panel->GetSize().GetHeight() * 1.3 / 3)); + // CenteredStaticText* text_4 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Reduce Angle"), + // wxPoint(overlay_panel->GetSize().GetWidth() / 2, + // overlay_panel->GetSize().GetHeight() * 2 / 3)); + // CenteredStaticText* text_5 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("More faces"), + // wxPoint(overlay_panel->GetSize().GetWidth() / 6, + // overlay_panel->GetSize().GetHeight() * 2.8 / 3)); + // CenteredStaticText* text_6 = new CenteredStaticText(overlay_panel, wxID_ANY, _L("Fewer faces"), + // wxPoint(overlay_panel->GetSize().GetWidth() * 5 / 6, + // overlay_panel->GetSize().GetHeight() * 2.8 / 3)); + + // bSizer->Add(overlay_panel, 0, wxALIGN_CENTER | wxALL, 10); + + wxBoxSizer* tips_sizer = new wxBoxSizer(wxVERTICAL); + wxStaticText* info = new wxStaticText(this, wxID_ANY, _L("Smaller linear and angular deflections result in higher-quality transformations but increase the processing time.")); + info->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + wxStaticText *tips = new wxStaticText(this, wxID_ANY, _L("View Wiki for more information")); + wxFont font(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false); + font.SetUnderlined(true); + tips->SetForegroundColour(StateColor::darkModeColorFor(wxColour(0, 151, 137))); + tips->SetFont(font); + tips->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) { + wxLaunchDefaultBrowser("https://wiki.bambulab.com/en/software/bambu-studio/step"); + }); + info->Wrap(FromDIP(400)); + tips_sizer->Add(info, 0, wxALIGN_LEFT); + tips_sizer->Add(tips, 0, wxALIGN_LEFT); + bSizer->Add(tips_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING); + + wxBoxSizer* linear_sizer = new wxBoxSizer(wxHORIZONTAL); + //linear_sizer->SetMinSize(wxSize(MIN_DIALOG_WIDTH, -1)); + wxStaticText* linear_title = new wxStaticText(this, + wxID_ANY, _L("Linear Deflection") + ": "); + linear_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + linear_sizer->Add(linear_title, 0, wxALIGN_LEFT); + linear_sizer->AddStretchSpacer(1); + wxSlider* linear_slider = new wxSlider(this, wxID_ANY, + SLIDER_SCALE(get_linear_defletion()), + 1, 100, wxDefaultPosition, + wxSize(SLIDER_WIDTH, SLIDER_HEIGHT), + wxSL_HORIZONTAL); + linear_sizer->Add(linear_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5)); + + auto linear_input = new ::TextInput(this, m_linear_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER); + linear_input->GetTextCtrl()->SetFont(Label::Body_12); + linear_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + linear_sizer->Add(linear_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5)); + linear_input->Bind(wxEVT_KILL_FOCUS, ([this, linear_input](wxFocusEvent& e) { + wxString value = linear_input->GetTextCtrl()->GetValue(); + if (validate_number_range(value, 0.001, 0.1)) { + m_linear_last = value; + update_mesh_number_text(); + } else { + MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.001 < linear deflection < 0.1)"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + linear_input->GetTextCtrl()->SetValue(m_linear_last); + } + e.Skip(); + })); + // textctrl bind slider + linear_input->Bind(wxEVT_TEXT, ([this, linear_slider, linear_input](wxCommandEvent& e) { + double slider_value_long; + int slider_value; + wxString value = linear_input->GetTextCtrl()->GetValue(); + if (value.ToDouble(&slider_value_long)) { + slider_value = SLIDER_SCALE(slider_value_long); + if (slider_value >= linear_slider->GetMin() && slider_value <= linear_slider->GetMax()) { + linear_slider->SetValue(slider_value); + } + } + })); + linear_slider->Bind(wxEVT_SLIDER, ([this, linear_slider, linear_input](wxCommandEvent& e) { + double slider_value = SLIDER_UNSCALE(linear_slider->GetValue()); + linear_input->GetTextCtrl()->SetValue(wxString::Format("%.3f", slider_value)); + m_linear_last = wxString::Format("%.3f", slider_value); + })); + linear_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) { + update_mesh_number_text(); + e.Skip(); + })); + + bSizer->Add(linear_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, LEFT_RIGHT_PADING); + + wxBoxSizer* angle_sizer = new wxBoxSizer(wxHORIZONTAL); + wxStaticText* angle_title = new wxStaticText(this, + wxID_ANY, _L("Angle Deflection") + ": "); + angle_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + angle_sizer->Add(angle_title, 0, wxALIGN_LEFT); + angle_sizer->AddStretchSpacer(1); + wxSlider* angle_slider = new wxSlider(this, wxID_ANY, + SLIDER_SCALE_10(get_angle_defletion()), + 1, 100, wxDefaultPosition, + wxSize(SLIDER_WIDTH, SLIDER_HEIGHT), + wxSL_HORIZONTAL); + angle_sizer->Add(angle_slider, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5)); + + auto angle_input = new ::TextInput(this, m_angle_last, wxEmptyString, wxEmptyString, wxDefaultPosition, wxSize(TEXT_CTRL_WIDTH, -1), wxTE_CENTER); + angle_input->GetTextCtrl()->SetFont(Label::Body_12); + angle_input->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC)); + angle_sizer->Add(angle_input, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(5)); + angle_input->Bind(wxEVT_KILL_FOCUS, ([this, angle_input](wxFocusEvent& e) { + wxString value = angle_input->GetTextCtrl()->GetValue(); + if (validate_number_range(value, 0.01, 1)) { + m_angle_last = value; + update_mesh_number_text(); + } else { + MessageDialog msg_dlg(nullptr, _L("Please input a valid value (0.01 < angle deflection < 1.0)"), wxEmptyString, wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + angle_input->GetTextCtrl()->SetValue(m_angle_last); + } + e.Skip(); + })); + // textctrl bind slider + angle_input->Bind(wxEVT_TEXT, ([this, angle_slider, angle_input](wxCommandEvent& e) { + double slider_value_long; + int slider_value; + wxString value = angle_input->GetTextCtrl()->GetValue(); + if (value.ToDouble(&slider_value_long)) { + slider_value = SLIDER_SCALE_10(slider_value_long); + if (slider_value >= angle_slider->GetMin() && slider_value <= angle_slider->GetMax()) { + angle_slider->SetValue(slider_value); + } + } + })); + + angle_slider->Bind(wxEVT_SLIDER, ([this, angle_slider, angle_input](wxCommandEvent& e) { + double slider_value = SLIDER_UNSCALE_10(angle_slider->GetValue()); + angle_input->GetTextCtrl()->SetValue(wxString::Format("%.2f", slider_value)); + m_angle_last = wxString::Format("%.2f", slider_value); + })); + angle_slider->Bind(wxEVT_LEFT_UP, ([this](wxMouseEvent& e) { + update_mesh_number_text(); + e.Skip(); + })); + + bSizer->Add(angle_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING); + + wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL); + m_split_compound_checkbox = new wxCheckBox(this, wxID_ANY, _L("Split compound and compsolid into multiple objects"), wxDefaultPosition, wxDefaultSize, 0); + m_split_compound_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + m_split_compound_checkbox->SetValue(wxGetApp().app_config->get_bool("is_split_compound")); + check_sizer->Add(m_split_compound_checkbox, 0, wxALIGN_LEFT); + bSizer->Add(check_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING); + + wxBoxSizer* mesh_face_number_sizer = new wxBoxSizer(wxHORIZONTAL); + wxStaticText *mesh_face_number_title = new wxStaticText(this, wxID_ANY, _L("Number of triangular facets") + ": "); + mesh_face_number_title->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + mesh_face_number_text = new wxStaticText(this, wxID_ANY, _L("0")); + mesh_face_number_text->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + mesh_face_number_text->SetMinSize(wxSize(FromDIP(150), -1)); + mesh_face_number_sizer->Add(mesh_face_number_title, 0, wxALIGN_LEFT); + mesh_face_number_sizer->Add(mesh_face_number_text, 0, wxALIGN_LEFT); + bSizer->Add(mesh_face_number_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING); + + wxBoxSizer* bSizer_button = new wxBoxSizer(wxHORIZONTAL); + bSizer_button->SetMinSize(wxSize(FromDIP(100), -1)); + m_checkbox = new wxCheckBox(this, wxID_ANY, _L("Don't show again"), wxDefaultPosition, wxDefaultSize, 0); + m_checkbox->SetForegroundColour(StateColor::darkModeColorFor(FONT_COLOR)); + bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT); + bSizer_button->AddStretchSpacer(1); + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(AMS_CONTROL_BRAND_COLOUR, StateColor::Normal)); + m_button_ok = new Button(this, _L("OK")); + m_button_ok->SetBackgroundColor(btn_bg_green); + m_button_ok->SetBorderColor(*wxWHITE); + m_button_ok->SetTextColor(wxColour(0xFFFFFE)); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(BUTTON_SIZE); + m_button_ok->SetMinSize(BUTTON_SIZE); + m_button_ok->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT, BUTTON_BORDER); + + m_button_ok->Bind(wxEVT_LEFT_DOWN, [this, angle_input, linear_input](wxMouseEvent& e) { + stop_task(); + if (validate_number_range(angle_input->GetTextCtrl()->GetValue(), 0.01, 1) && + validate_number_range(linear_input->GetTextCtrl()->GetValue(), 0.001, 0.1)) { + if (m_checkbox->IsChecked()) { + wxGetApp().app_config->set_bool("enable_step_mesh_setting", false); + } + wxGetApp().app_config->set_bool("is_split_compound", m_split_compound_checkbox->GetValue()); + wxGetApp().app_config->set("linear_defletion", float_to_string_decimal_point(get_linear_defletion(), 3)); + wxGetApp().app_config->set("angle_defletion", float_to_string_decimal_point(get_angle_defletion(), 2)); + + EndModal(wxID_OK); + } + SetFocusIgnoringChildren(); + }); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(BUTTON_SIZE); + m_button_cancel->SetMinSize(BUTTON_SIZE); + m_button_cancel->SetCornerRadius(FromDIP(12)); + bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT, BUTTON_BORDER); + + m_button_cancel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent& e) { + stop_task(); + EndModal(wxID_CANCEL); + }); + + bSizer->Add(bSizer_button, 1, wxEXPAND | wxALL, LEFT_RIGHT_PADING); + + this->SetSizer(bSizer); + update_mesh_number_text(); + this->Layout(); + bSizer->Fit(this); + + this->Bind(wxEVT_LEFT_DOWN, [this](auto& e) { + SetFocusIgnoringChildren(); + }); + mesh_face_number_text->Bind(wxEVT_LEFT_DOWN, [this](auto& e) { + SetFocusIgnoringChildren(); + }); + + this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { + stop_task(); + EndModal(wxID_CANCEL); + }); + + wxGetApp().UpdateDlgDarkUI(this); +} + +StepMeshDialog::~StepMeshDialog() +{ + stop_task(); +} + +void StepMeshDialog::on_task_done(wxCommandEvent& event) +{ + wxString text = event.GetString(); + mesh_face_number_text->SetLabel(text); + if(m_task) { + if (m_task->joinable()) { + m_task->join(); + delete m_task; + m_task = nullptr; + } + } +} + +void StepMeshDialog::stop_task() +{ + if(m_task) { + m_file.m_stop_mesh.store(true); + if (m_task->joinable()) { + m_task->join(); + delete m_task; + m_task = nullptr; + } + m_file.m_stop_mesh.store(false); + } + +} + +void StepMeshDialog::update_mesh_number_text() +{ + if ((m_last_linear == get_linear_defletion()) && (m_last_angle == get_angle_defletion()) && (m_mesh_number != 0)) + return; + wxString newText = wxString::Format(_L("Calculating, please wait...")); + mesh_face_number_text->SetLabel(newText); + stop_task(); + if (!m_task) { + m_task = new boost::thread(Slic3r::create_thread([this]() -> void { + m_mesh_number = m_file.get_triangle_num(get_linear_defletion(), get_angle_defletion()); + if (m_mesh_number != 0) { + wxString number_text = wxString::Format("%d", m_mesh_number); + wxCommandEvent event(wxEVT_THREAD_DONE); + event.SetString(number_text); + wxPostEvent(this, event); + m_last_linear = get_linear_defletion(); + m_last_angle = get_angle_defletion(); + } + })); + } +} \ No newline at end of file diff --git a/src/slic3r/GUI/StepMeshDialog.hpp b/src/slic3r/GUI/StepMeshDialog.hpp new file mode 100644 index 0000000000..4ea70ae6e7 --- /dev/null +++ b/src/slic3r/GUI/StepMeshDialog.hpp @@ -0,0 +1,55 @@ +#ifndef _STEP_MESH_DIALOG_H_ +#define _STEP_MESH_DIALOG_H_ + +#include +#include "GUI_App.hpp" +#include "GUI_Utils.hpp" +#include "libslic3r/Format/STEP.hpp" +#include "Widgets/Button.hpp" +class Button; + +class StepMeshDialog : public Slic3r::GUI::DPIDialog +{ +public: + StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double linear_init, double angle_init); + ~StepMeshDialog() override; + void on_dpi_changed(const wxRect& suggested_rect) override; + inline double get_linear_defletion() { + double value; + if (m_linear_last.ToDouble(&value)) { + return value; + }else { + return m_last_linear; + } + } + inline double get_angle_defletion() { + double value; + if (m_angle_last.ToDouble(&value)) { + return value; + } else { + return m_last_angle; + } + } + inline bool get_split_compound_value() { + return m_split_compound_checkbox->GetValue(); + } +private: + Slic3r::Step& m_file; + Button* m_button_ok = nullptr; + Button* m_button_cancel = nullptr; + wxCheckBox* m_checkbox = nullptr; + wxCheckBox* m_split_compound_checkbox = nullptr; + wxString m_linear_last; + wxString m_angle_last; + wxStaticText* mesh_face_number_text; + double m_last_linear = 0.003; + double m_last_angle = 0.5; + unsigned int m_mesh_number = 0; + boost::thread* m_task {nullptr}; + bool validate_number_range(const wxString& value, double min, double max); + void update_mesh_number_text(); + void on_task_done(wxCommandEvent& event); + void stop_task(); +}; + +#endif // _STEP_MESH_DIALOG_H_ \ No newline at end of file diff --git a/src/slic3r/Utils/CalibUtils.cpp b/src/slic3r/Utils/CalibUtils.cpp index 0732b99370..bb51427654 100644 --- a/src/slic3r/Utils/CalibUtils.cpp +++ b/src/slic3r/Utils/CalibUtils.cpp @@ -303,7 +303,7 @@ static void read_model_from_file(const std::string& input_file, Model& model) std::vector project_presets; model = Model::read_from_file(input_file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, - &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice); + &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, plate_to_slice); model.add_default_instances(); for (auto object : model.objects) diff --git a/src/slic3r/pchheader.hpp b/src/slic3r/pchheader.hpp index 12bae039c7..46cd9907d0 100644 --- a/src/slic3r/pchheader.hpp +++ b/src/slic3r/pchheader.hpp @@ -6,6 +6,7 @@ #define NOMINMAX #endif #include + #include #endif #include