ENH: output the part name that causes mesh boolean failure

jira: none
Change-Id: Ie90809575291d66d54462f81c157d3e89fc34d55
(cherry picked from commit fdb6457798757a35b9623ece3b3071b3ad7ad38d)
This commit is contained in:
Arthur 2024-02-07 09:15:30 +08:00 committed by Lane.Wei
parent 356e4d825e
commit d75e79c2ec
4 changed files with 21 additions and 13 deletions

View File

@ -70,6 +70,7 @@ struct CSGPart {
Transform3f trafo;
CSGType operation;
CSGStackOp stack_operation;
std::string name;
CSGPart(AnyPtr<const indexed_triangle_set> ptr = {},
CSGType op = CSGType::Union,

View File

@ -64,7 +64,7 @@ bool model_to_csgmesh(const ModelObject &mo,
CSGPart part{&(vol->mesh().its),
vol->is_model_part() ? CSGType::Union : CSGType::Difference,
(trafo * vol->get_matrix()).cast<float>()};
part.name = vol->name;
*out = std::move(part);
++out;
}

View File

@ -257,12 +257,13 @@ void perform_csgmesh_booleans_mcut(MeshBoolean::mcut::McutMeshPtr& mcutm,
template<class It, class Visitor>
BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
std::tuple<BooleanFailReason,std::string> check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vfn)
{
using namespace detail_cgal;
BooleanFailReason fail_reason = BooleanFailReason::OK;
std::string fail_part_name;
std::vector<CGALMeshPtr> cgalmeshes(csgrange.size());
auto check_part = [&csgrange, &cgalmeshes,&fail_reason](size_t i)
auto check_part = [&csgrange, &cgalmeshes,&fail_reason,&fail_part_name](size_t i)
{
auto it = csgrange.begin();
std::advance(it, i);
@ -279,18 +280,21 @@ BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vf
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;
fail_part_name = csgpart.name;
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;
fail_part_name = csgpart.name;
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;
fail_part_name = csgpart.name;
return;
}
}
@ -312,20 +316,21 @@ BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, Visitor &&vf
// }
//}
return fail_reason;
return { fail_reason,fail_part_name };
}
template<class It>
BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcut=false)
std::tuple<BooleanFailReason, std::string> 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::string fail_part_name;
std::vector<McutMeshPtr> McutMeshes(csgrange.size());
auto check_part = [&csgrange, &McutMeshes,&fail_reason](size_t i) {
auto check_part = [&csgrange, &McutMeshes,&fail_reason,&fail_part_name](size_t i) {
auto it = csgrange.begin();
std::advance(it, i);
auto& csgpart = *it;
@ -340,6 +345,7 @@ BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcu
try {
if (!m || MeshBoolean::mcut::empty(*m)) {
fail_reason=BooleanFailReason::MeshEmpty;
fail_part_name = csgpart.name;
return;
}
}
@ -348,7 +354,7 @@ BooleanFailReason check_csgmesh_booleans(const Range<It> &csgrange, bool use_mcu
McutMeshes[i] = std::move(m);
};
execution::for_each(ex_tbb, size_t(0), csgrange.size(), check_part);
return fail_reason;
return { fail_reason,fail_part_name };
}
}

View File

@ -10987,14 +10987,15 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st
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) {
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, _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];
{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 {