ENH: give exaxt reason of boolean fail

jira: none
Change-Id: Ied645373a4516be7466078ace73c69ef584a743a
(cherry picked from commit f21819b4f6718357984650e7eb89a2e496dfa03c)
This commit is contained in:
Arthur 2024-01-17 15:26:12 +08:00 committed by Lane.Wei
parent cb1a384593
commit 72ef0a4d2a
2 changed files with 40 additions and 35 deletions

View File

@ -11,6 +11,7 @@
#include "libslic3r/MeshBoolean.hpp"
namespace Slic3r { namespace csg {
enum class BooleanFailReason { OK, MeshEmpty, NotBoundAVolume, SelfIntersect, NoIntersection};
// This method can be overriden when a specific CSGPart type supports caching
// of the voxel grid
@ -256,12 +257,12 @@ void perform_csgmesh_booleans_mcut(MeshBoolean::mcut::McutMeshPtr& mcutm,
template<class It, class Visitor>
It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
{
using namespace detail_cgal;
BooleanFailReason fail_reason = BooleanFailReason::OK;
std::vector<CGALMeshPtr> cgalmeshes(csgrange.size());
auto check_part = [&csgrange, &cgalmeshes](size_t i)
auto check_part = [&csgrange, &cgalmeshes,&fail_reason](size_t i)
{
auto it = csgrange.begin();
std::advance(it, i);
@ -277,16 +278,19 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
try {
if (!m || MeshBoolean::cgal::empty(*m)) {
BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh " << i << "/" << csgrange.size() << " is empty, cannot do boolean!";
fail_reason= BooleanFailReason::MeshEmpty;
return;
}
if (!MeshBoolean::cgal::does_bound_a_volume(*m)) {
BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh "<<i<<"/"<<csgrange.size()<<" does_bound_a_volume is false, cannot do boolean!";
fail_reason= BooleanFailReason::NotBoundAVolume;
return;
}
if (MeshBoolean::cgal::does_self_intersect(*m)) {
BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh " << i << "/" << csgrange.size() << " does_self_intersect is true, cannot do boolean!";
fail_reason= BooleanFailReason::SelfIntersect;
return;
}
}
@ -296,31 +300,32 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
};
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
It ret = csgrange.end();
for (size_t i = 0; i < csgrange.size(); ++i) {
if (!cgalmeshes[i]) {
auto it = csgrange.begin();
std::advance(it, i);
vfn(it);
//It ret = csgrange.end();
//for (size_t i = 0; i < csgrange.size(); ++i) {
// if (!cgalmeshes[i]) {
// auto it = csgrange.begin();
// std::advance(it, i);
// vfn(it);
if (ret == csgrange.end())
ret = it;
}
}
// if (ret == csgrange.end())
// ret = it;
// }
//}
return ret;
return fail_reason;
}
template<class It>
It check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
{
if(!use_mcut)
return check_csgmesh_booleans(csgrange, [](auto &) {});
else {
using namespace detail_mcut;
BooleanFailReason fail_reason = BooleanFailReason::OK;
std::vector<McutMeshPtr> McutMeshes(csgrange.size());
auto check_part = [&csgrange, &McutMeshes](size_t i) {
auto check_part = [&csgrange, &McutMeshes,&fail_reason](size_t i) {
auto it = csgrange.begin();
std::advance(it, i);
auto& csgpart = *it;
@ -333,27 +338,17 @@ It check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
}
try {
if (!m || MeshBoolean::mcut::empty(*m))
if (!m || MeshBoolean::mcut::empty(*m)) {
fail_reason=BooleanFailReason::MeshEmpty;
return;
}
}
catch (...) { return; }
McutMeshes[i] = std::move(m);
};
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
It ret = csgrange.end();
for (size_t i = 0; i < csgrange.size(); ++i) {
if (!McutMeshes[i]) {
auto it = csgrange.begin();
std::advance(it, i);
if (ret == csgrange.end())
ret = it;
}
}
return ret;
return fail_reason;
}
}

View File

@ -10881,11 +10881,22 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh),
csg::mpartsPositive | csg::mpartsNegative);
if (csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }) == csgmesh.end()) {
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 = csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); fail_reason != csg::BooleanFailReason::OK) {
std::map<csg::BooleanFailReason, std::string> fail_reasons = {
{csg::BooleanFailReason::OK, "OK"},
{csg::BooleanFailReason::MeshEmpty, _u8L("Reason: mesh is empty.")},
{csg::BooleanFailReason::NotBoundAVolume, _u8L("Reason: mesh does not bound a volume.")},
{csg::BooleanFailReason::SelfIntersect, _u8L("Reason: mesh has self intersection.")},
{csg::BooleanFailReason::NoIntersection, _u8L("Reason: meshes have no intersection.")} };
fail_msg += " " + fail_reasons[fail_reason];
}
else {
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);
}
}
catch (...) {}
#if 0
// if mcut fails, try again with CGAL
@ -10893,7 +10904,7 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
try {
auto meshPtr = csg::perform_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) });
mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*meshPtr);
}
}
catch (...) {}
}
#endif
@ -10901,8 +10912,7 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
if (mesh.empty()) {
if (notify_func)
notify_func(_u8L("Unable to perform boolean operation on model meshes. "
"Only positive parts will be exported."));
notify_func(fail_msg);
for (const ModelVolume* v : mo.volumes)
if (v->is_model_part()) {