diff --git a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp index 5963b2910..05685ec74 100644 --- a/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp +++ b/src/libslic3r/CSGMesh/ModelToCSGMesh.hpp @@ -19,7 +19,7 @@ enum ModelParts { }; template -bool model_to_csgmesh(const ModelObject &mo, +bool model_to_csgmesh(const std::vector & volumes, const Transform3d &trafo, // Applies to all exported parts OutIt out, // Output iterator // values of ModelParts OR-ed @@ -32,7 +32,7 @@ bool model_to_csgmesh(const ModelObject &mo, bool do_splits = parts_to_include & mpartsDoSplits; bool has_splitable_volume = false; - for (const ModelVolume *vol : mo.volumes) { + for (const ModelVolume *vol : volumes) { if (vol && vol->mesh_ptr() && ((do_positives && vol->is_model_part()) || (do_negatives && vol->is_negative_volume()))) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 2131eb611..71a563320 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -389,6 +389,7 @@ public: CutConnectors cut_connectors; CutObjectBase cut_id; + std::vector const_volumes() const {return std::vector(volumes.begin(), volumes.end());} Model* get_model() { return m_model; } const Model* get_model() const { return m_model; } // BBS: production extension diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index d35f3716b..9226c834f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -1,12 +1,16 @@ #include "GLGizmoMeshBoolean.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/ImGuiWrapper.hpp" -#include "slic3r/GUI/GUI.hpp" +#include "libslic3r/CSGMesh/CSGMesh.hpp" #include "libslic3r/MeshBoolean.hpp" -#include "slic3r/GUI/GUI_ObjectList.hpp" -#include "slic3r/GUI/Plater.hpp" +#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" +#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" #include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/NotificationManager.hpp" +#include "slic3r/GUI/Plater.hpp" + #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif @@ -364,18 +368,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l if (m_operation_mode == MeshBooleanOperation::Union) { if (operate_button(_L("Union") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(true, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_common; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if(m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(true, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_common; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); @@ -385,18 +392,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l else if (m_operation_mode == MeshBooleanOperation::Difference) { m_imgui->bbl_checkbox(_L("Delete input"), m_diff_delete_input); if (operate_button(_L("Difference") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_common; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if (m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_common; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); @@ -406,18 +416,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l else if (m_operation_mode == MeshBooleanOperation::Intersection){ m_imgui->bbl_checkbox(_L("Delete input"), m_inter_delete_input); if (operate_button(_L("Intersection") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_intersection; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if (m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_intersection; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ea333fbc8..47d0708fa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -11561,6 +11561,29 @@ void Plater::export_core_3mf() export_3mf(path_u8, SaveStrategy::Silence); } +// OK if fail_msg is empty +std::string check_boolean_possible(const std::vector& volumes) { + std::string fail_msg; + std::vector csgmesh; + csgmesh.reserve(2 * volumes.size()); + bool has_splitable_volume = csg::model_to_csgmesh(volumes, Transform3d::Identity(), std::back_inserter(csgmesh), + csg::mpartsPositive | csg::mpartsNegative); + + if (auto fail_reason_name = csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); std::get<0>(fail_reason_name) != csg::BooleanFailReason::OK) { + fail_msg = _u8L("Unable to perform boolean operation on model meshes. " + "You may fix the meshes and try agian."); + std::string name = std::get<1>(fail_reason_name); + std::map fail_reasons = { + {csg::BooleanFailReason::OK, "OK"}, + {csg::BooleanFailReason::MeshEmpty, Slic3r::format(_u8L("Reason: part \"%1%\" is empty."), name)}, + {csg::BooleanFailReason::NotBoundAVolume, Slic3r::format(_u8L("Reason: part \"%1%\" does not bound a volume."), name)}, + {csg::BooleanFailReason::SelfIntersect, Slic3r::format(_u8L("Reason: part \"%1%\" has self intersection."), name)}, + {csg::BooleanFailReason::NoIntersection, Slic3r::format(_u8L("Reason: \"%1%\" and another part have no intersection."), name)} }; + fail_msg += " " + fail_reasons[std::get<0>(fail_reason_name)]; + } + return fail_msg; +} + // Following lambda generates a combined mesh for export with normals pointing outwards. TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, std::function notify_func) { @@ -11568,22 +11591,11 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st std::vector csgmesh; csgmesh.reserve(2 * mo.volumes.size()); - bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh), + bool has_splitable_volume = csg::model_to_csgmesh(mo.const_volumes(), Transform3d::Identity(), std::back_inserter(csgmesh), csg::mpartsPositive | csg::mpartsNegative); - std::string fail_msg = _u8L("Unable to perform boolean operation on model meshes. " - "Only positive parts will be kept. You may fix the meshes and try agian."); - if (auto fail_reason_name = csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); std::get<0>(fail_reason_name) != csg::BooleanFailReason::OK) { - std::string name = std::get<1>(fail_reason_name); - std::map fail_reasons = { - {csg::BooleanFailReason::OK, "OK"}, - {csg::BooleanFailReason::MeshEmpty, Slic3r::format( _u8L("Reason: part \"%1%\" is empty."), name)}, - {csg::BooleanFailReason::NotBoundAVolume, Slic3r::format(_u8L("Reason: part \"%1%\" does not bound a volume."), name)}, - {csg::BooleanFailReason::SelfIntersect, Slic3r::format(_u8L("Reason: part \"%1%\" has self intersection."), name)}, - {csg::BooleanFailReason::NoIntersection, Slic3r::format(_u8L("Reason: \"%1%\" and another part have no intersection."), name)} }; - fail_msg += " " + fail_reasons[std::get<0>(fail_reason_name)]; - } - else { + std::string fail_msg = check_boolean_possible(mo.const_volumes()); + if (fail_msg.empty()) { try { MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) }); mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 221ca4d57..3649a9df9 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -792,6 +792,7 @@ private: }; std::vector get_min_flush_volumes(const DynamicPrintConfig& full_config); +std::string check_boolean_possible(const std::vector& volumes); } // namespace GUI } // namespace Slic3r