FIX: gizmo boolean crashes due to self-intersection
Check if boolean operation is possible first, just like in context menu. jira: STUDIO-6471 Change-Id: I9c201010dad90bbfa615178aa835c7e371755cd2 (cherry picked from commit db8a02e8328b5529123fb83c0373eb01cb925f0a)
This commit is contained in:
parent
3849bfd4d6
commit
ca3a372630
|
@ -19,7 +19,7 @@ enum ModelParts {
|
|||
};
|
||||
|
||||
template<class OutIt>
|
||||
bool model_to_csgmesh(const ModelObject &mo,
|
||||
bool model_to_csgmesh(const std::vector<const ModelVolume*> & 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()))) {
|
||||
|
|
|
@ -389,6 +389,7 @@ public:
|
|||
CutConnectors cut_connectors;
|
||||
CutObjectBase cut_id;
|
||||
|
||||
std::vector<const ModelVolume*> const_volumes() const {return std::vector<const ModelVolume*>(volumes.begin(), volumes.end());}
|
||||
Model* get_model() { return m_model; }
|
||||
const Model* get_model() const { return m_model; }
|
||||
// BBS: production extension
|
||||
|
|
|
@ -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<TriangleMesh> 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<TriangleMesh> 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<TriangleMesh> 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<TriangleMesh> 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<TriangleMesh> 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<TriangleMesh> 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();
|
||||
|
|
|
@ -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<const ModelVolume*>& volumes) {
|
||||
std::string fail_msg;
|
||||
std::vector<csg::CSGPart> 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<csg::BooleanFailReason, std::string> 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<void(const std::string&)> notify_func)
|
||||
{
|
||||
|
@ -11568,22 +11591,11 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
|
|||
|
||||
std::vector<csg::CSGPart> 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<csg::BooleanFailReason, std::string> 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);
|
||||
|
|
|
@ -792,6 +792,7 @@ private:
|
|||
};
|
||||
|
||||
std::vector<int> get_min_flush_volumes(const DynamicPrintConfig& full_config);
|
||||
std::string check_boolean_possible(const std::vector<const ModelVolume*>& volumes);
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
Loading…
Reference in New Issue