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>
|
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
|
const Transform3d &trafo, // Applies to all exported parts
|
||||||
OutIt out, // Output iterator
|
OutIt out, // Output iterator
|
||||||
// values of ModelParts OR-ed
|
// values of ModelParts OR-ed
|
||||||
|
@ -32,7 +32,7 @@ bool model_to_csgmesh(const ModelObject &mo,
|
||||||
bool do_splits = parts_to_include & mpartsDoSplits;
|
bool do_splits = parts_to_include & mpartsDoSplits;
|
||||||
bool has_splitable_volume = false;
|
bool has_splitable_volume = false;
|
||||||
|
|
||||||
for (const ModelVolume *vol : mo.volumes) {
|
for (const ModelVolume *vol : volumes) {
|
||||||
if (vol && vol->mesh_ptr() &&
|
if (vol && vol->mesh_ptr() &&
|
||||||
((do_positives && vol->is_model_part()) ||
|
((do_positives && vol->is_model_part()) ||
|
||||||
(do_negatives && vol->is_negative_volume()))) {
|
(do_negatives && vol->is_negative_volume()))) {
|
||||||
|
|
|
@ -389,6 +389,7 @@ public:
|
||||||
CutConnectors cut_connectors;
|
CutConnectors cut_connectors;
|
||||||
CutObjectBase cut_id;
|
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; }
|
Model* get_model() { return m_model; }
|
||||||
const Model* get_model() const { return m_model; }
|
const Model* get_model() const { return m_model; }
|
||||||
// BBS: production extension
|
// BBS: production extension
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
#include "GLGizmoMeshBoolean.hpp"
|
#include "GLGizmoMeshBoolean.hpp"
|
||||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
#include "libslic3r/CSGMesh/CSGMesh.hpp"
|
||||||
#include "slic3r/GUI/ImGuiWrapper.hpp"
|
|
||||||
#include "slic3r/GUI/GUI.hpp"
|
|
||||||
#include "libslic3r/MeshBoolean.hpp"
|
#include "libslic3r/MeshBoolean.hpp"
|
||||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp"
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp"
|
||||||
#include "slic3r/GUI/Camera.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/NotificationManager.hpp"
|
||||||
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#endif
|
#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 (m_operation_mode == MeshBooleanOperation::Union)
|
||||||
{
|
{
|
||||||
if (operate_button(_L("Union") + "##btn", enable_button)) {
|
if (operate_button(_L("Union") + "##btn", enable_button)) {
|
||||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv });
|
||||||
temp_src_mesh.transform(m_src.trafo);
|
if(m_warning_texts[index] == "") {
|
||||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||||
temp_tool_mesh.transform(m_tool.trafo);
|
temp_src_mesh.transform(m_src.trafo);
|
||||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION");
|
temp_tool_mesh.transform(m_tool.trafo);
|
||||||
if (temp_mesh_resuls.size() != 0) {
|
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||||
generate_new_volume(true, *temp_mesh_resuls.begin());
|
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION");
|
||||||
m_warning_texts[index] = "";
|
if (temp_mesh_resuls.size() != 0) {
|
||||||
}
|
generate_new_volume(true, *temp_mesh_resuls.begin());
|
||||||
else {
|
m_warning_texts[index] = "";
|
||||||
m_warning_texts[index] = warning_text_common;
|
}
|
||||||
|
else {
|
||||||
|
m_warning_texts[index] = warning_text_common;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||||
m_src.reset();
|
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) {
|
else if (m_operation_mode == MeshBooleanOperation::Difference) {
|
||||||
m_imgui->bbl_checkbox(_L("Delete input"), m_diff_delete_input);
|
m_imgui->bbl_checkbox(_L("Delete input"), m_diff_delete_input);
|
||||||
if (operate_button(_L("Difference") + "##btn", enable_button)) {
|
if (operate_button(_L("Difference") + "##btn", enable_button)) {
|
||||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv });
|
||||||
temp_src_mesh.transform(m_src.trafo);
|
if (m_warning_texts[index] == "") {
|
||||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||||
temp_tool_mesh.transform(m_tool.trafo);
|
temp_src_mesh.transform(m_src.trafo);
|
||||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B");
|
temp_tool_mesh.transform(m_tool.trafo);
|
||||||
if (temp_mesh_resuls.size() != 0) {
|
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||||
generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin());
|
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B");
|
||||||
m_warning_texts[index] = "";
|
if (temp_mesh_resuls.size() != 0) {
|
||||||
}
|
generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin());
|
||||||
else {
|
m_warning_texts[index] = "";
|
||||||
m_warning_texts[index] = warning_text_common;
|
}
|
||||||
|
else {
|
||||||
|
m_warning_texts[index] = warning_text_common;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||||
m_src.reset();
|
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){
|
else if (m_operation_mode == MeshBooleanOperation::Intersection){
|
||||||
m_imgui->bbl_checkbox(_L("Delete input"), m_inter_delete_input);
|
m_imgui->bbl_checkbox(_L("Delete input"), m_inter_delete_input);
|
||||||
if (operate_button(_L("Intersection") + "##btn", enable_button)) {
|
if (operate_button(_L("Intersection") + "##btn", enable_button)) {
|
||||||
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv });
|
||||||
temp_src_mesh.transform(m_src.trafo);
|
if (m_warning_texts[index] == "") {
|
||||||
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
TriangleMesh temp_src_mesh = m_src.mv->mesh();
|
||||||
temp_tool_mesh.transform(m_tool.trafo);
|
temp_src_mesh.transform(m_src.trafo);
|
||||||
std::vector<TriangleMesh> temp_mesh_resuls;
|
TriangleMesh temp_tool_mesh = m_tool.mv->mesh();
|
||||||
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION");
|
temp_tool_mesh.transform(m_tool.trafo);
|
||||||
if (temp_mesh_resuls.size() != 0) {
|
std::vector<TriangleMesh> temp_mesh_resuls;
|
||||||
generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin());
|
Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION");
|
||||||
m_warning_texts[index] = "";
|
if (temp_mesh_resuls.size() != 0) {
|
||||||
}
|
generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin());
|
||||||
else {
|
m_warning_texts[index] = "";
|
||||||
m_warning_texts[index] = warning_text_intersection;
|
}
|
||||||
|
else {
|
||||||
|
m_warning_texts[index] = warning_text_intersection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
m_selecting_state = MeshBooleanSelectingState::SelectSource;
|
||||||
m_src.reset();
|
m_src.reset();
|
||||||
|
|
|
@ -11561,6 +11561,29 @@ void Plater::export_core_3mf()
|
||||||
export_3mf(path_u8, SaveStrategy::Silence);
|
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.
|
// 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)
|
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;
|
std::vector<csg::CSGPart> csgmesh;
|
||||||
csgmesh.reserve(2 * mo.volumes.size());
|
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);
|
csg::mpartsPositive | csg::mpartsNegative);
|
||||||
|
|
||||||
std::string fail_msg = _u8L("Unable to perform boolean operation on model meshes. "
|
std::string fail_msg = check_boolean_possible(mo.const_volumes());
|
||||||
"Only positive parts will be kept. You may fix the meshes and try agian.");
|
if (fail_msg.empty()) {
|
||||||
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 {
|
|
||||||
try {
|
try {
|
||||||
MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) });
|
||||||
mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr);
|
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::vector<int> get_min_flush_volumes(const DynamicPrintConfig& full_config);
|
||||||
|
std::string check_boolean_possible(const std::vector<const ModelVolume*>& volumes);
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue