ENH: give exaxt reason of boolean fail
jira: none Change-Id: Ied645373a4516be7466078ace73c69ef584a743a (cherry picked from commit f21819b4f6718357984650e7eb89a2e496dfa03c)
This commit is contained in:
parent
cb1a384593
commit
72ef0a4d2a
|
@ -11,6 +11,7 @@
|
||||||
#include "libslic3r/MeshBoolean.hpp"
|
#include "libslic3r/MeshBoolean.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace csg {
|
namespace Slic3r { namespace csg {
|
||||||
|
enum class BooleanFailReason { OK, MeshEmpty, NotBoundAVolume, SelfIntersect, NoIntersection};
|
||||||
|
|
||||||
// This method can be overriden when a specific CSGPart type supports caching
|
// This method can be overriden when a specific CSGPart type supports caching
|
||||||
// of the voxel grid
|
// of the voxel grid
|
||||||
|
@ -256,12 +257,12 @@ void perform_csgmesh_booleans_mcut(MeshBoolean::mcut::McutMeshPtr& mcutm,
|
||||||
|
|
||||||
|
|
||||||
template<class It, class Visitor>
|
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;
|
using namespace detail_cgal;
|
||||||
|
BooleanFailReason fail_reason = BooleanFailReason::OK;
|
||||||
std::vector<CGALMeshPtr> cgalmeshes(csgrange.size());
|
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();
|
auto it = csgrange.begin();
|
||||||
std::advance(it, i);
|
std::advance(it, i);
|
||||||
|
@ -277,16 +278,19 @@ It check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
|
||||||
try {
|
try {
|
||||||
if (!m || MeshBoolean::cgal::empty(*m)) {
|
if (!m || MeshBoolean::cgal::empty(*m)) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh " << i << "/" << csgrange.size() << " is empty, cannot do boolean!";
|
BOOST_LOG_TRIVIAL(info) << "check_csgmesh_booleans fails! mesh " << i << "/" << csgrange.size() << " is empty, cannot do boolean!";
|
||||||
|
fail_reason= BooleanFailReason::MeshEmpty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MeshBoolean::cgal::does_bound_a_volume(*m)) {
|
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!";
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MeshBoolean::cgal::does_self_intersect(*m)) {
|
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!";
|
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;
|
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);
|
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
|
||||||
|
|
||||||
It ret = csgrange.end();
|
//It ret = csgrange.end();
|
||||||
for (size_t i = 0; i < csgrange.size(); ++i) {
|
//for (size_t i = 0; i < csgrange.size(); ++i) {
|
||||||
if (!cgalmeshes[i]) {
|
// if (!cgalmeshes[i]) {
|
||||||
auto it = csgrange.begin();
|
// auto it = csgrange.begin();
|
||||||
std::advance(it, i);
|
// std::advance(it, i);
|
||||||
vfn(it);
|
// vfn(it);
|
||||||
|
|
||||||
if (ret == csgrange.end())
|
// if (ret == csgrange.end())
|
||||||
ret = it;
|
// ret = it;
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
return ret;
|
return fail_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class It>
|
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)
|
if(!use_mcut)
|
||||||
return check_csgmesh_booleans(csgrange, [](auto &) {});
|
return check_csgmesh_booleans(csgrange, [](auto &) {});
|
||||||
else {
|
else {
|
||||||
using namespace detail_mcut;
|
using namespace detail_mcut;
|
||||||
|
BooleanFailReason fail_reason = BooleanFailReason::OK;
|
||||||
|
|
||||||
std::vector<McutMeshPtr> McutMeshes(csgrange.size());
|
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();
|
auto it = csgrange.begin();
|
||||||
std::advance(it, i);
|
std::advance(it, i);
|
||||||
auto& csgpart = *it;
|
auto& csgpart = *it;
|
||||||
|
@ -333,27 +338,17 @@ It check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!m || MeshBoolean::mcut::empty(*m))
|
if (!m || MeshBoolean::mcut::empty(*m)) {
|
||||||
|
fail_reason=BooleanFailReason::MeshEmpty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (...) { return; }
|
catch (...) { return; }
|
||||||
|
|
||||||
McutMeshes[i] = std::move(m);
|
McutMeshes[i] = std::move(m);
|
||||||
};
|
};
|
||||||
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
|
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
|
||||||
|
return fail_reason;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10881,7 +10881,18 @@ 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),
|
bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh),
|
||||||
csg::mpartsPositive | csg::mpartsNegative);
|
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 {
|
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);
|
||||||
|
@ -10901,8 +10912,7 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
|
||||||
|
|
||||||
if (mesh.empty()) {
|
if (mesh.empty()) {
|
||||||
if (notify_func)
|
if (notify_func)
|
||||||
notify_func(_u8L("Unable to perform boolean operation on model meshes. "
|
notify_func(fail_msg);
|
||||||
"Only positive parts will be exported."));
|
|
||||||
|
|
||||||
for (const ModelVolume* v : mo.volumes)
|
for (const ModelVolume* v : mo.volumes)
|
||||||
if (v->is_model_part()) {
|
if (v->is_model_part()) {
|
||||||
|
|
Loading…
Reference in New Issue