diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 1021fb7658c..f9e66e9ebb4 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -620,7 +620,7 @@ MCAPI_ATTR void MCAPI_CALL mcDebugOutput(McDebugSource source, } -void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &boolean_opts) +bool do_boolean_single(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &boolean_opts) { // create context McContext context = MC_NULL_HANDLE; @@ -651,7 +651,7 @@ void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &b if (srcMesh.vertexCoordsArray.empty() && (boolean_opts == "UNION" || boolean_opts == "B_NOT_A")) { srcMesh = cutMesh; mcReleaseContext(context); - return; + return true; } err = mcDispatch(context, @@ -669,31 +669,9 @@ void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &b mcReleaseContext(context); if (boolean_opts == "UNION") { merge_mcut_meshes(srcMesh, cutMesh); + return true; } - else { - // when src mesh has multiple connected components, mcut refuses to work. - // But we can force it to work by spliting the src mesh into disconnected components, - // and do booleans seperately, then merge all the results. - indexed_triangle_set all_its; - TriangleMesh tri_src = mcut_to_triangle_mesh(srcMesh); - std::vector src_parts = its_split(tri_src.its); - if (src_parts.size() == 1) - { - //can not split, return error directly - BOOST_LOG_TRIVIAL(error) << boost::format("bool operation %1% failed, also can not split")%boolean_opts; - return; - } - for (size_t i = 0; i < src_parts.size(); i++) - { - auto part = triangle_mesh_to_mcut(src_parts[i]); - do_boolean(*part, cutMesh, boolean_opts); - TriangleMesh tri_part = mcut_to_triangle_mesh(*part); - its_merge(all_its, tri_part.its); - } - srcMesh = *triangle_mesh_to_mcut(all_its); - } - - return; + return false; } // query the number of available connected component @@ -704,8 +682,9 @@ void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &b mcReleaseContext(context); if (numConnComps == 0 && boolean_opts == "UNION") { merge_mcut_meshes(srcMesh, cutMesh); + return true; } - return; + return false; } std::vector connectedComponents(numConnComps, MC_NULL_HANDLE); @@ -772,126 +751,57 @@ void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &b // free connected component data err = mcReleaseConnectedComponents(context, 0, NULL); - // destroy context err = mcReleaseContext(context); srcMesh = outMesh; + + return true; } -/* BBS: Musang King - * mcut for Mesh Boolean which provides C-style syntax API - */ -std::vector make_boolean(const McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &boolean_opts) +void do_boolean(McutMesh& srcMesh, const McutMesh& cutMesh, const std::string& boolean_opts) { - // create context - McContext context = MC_NULL_HANDLE; - McResult err = mcCreateContext(&context, 0); - // add debug callback according to https://cutdigital.github.io/mcut.site/tutorials/debugging/ - mcDebugMessageCallback(context, mcDebugOutput, nullptr); - mcDebugMessageControl( - context, - MC_DEBUG_SOURCE_ALL, - MC_DEBUG_TYPE_ERROR, - MC_DEBUG_SEVERITY_MEDIUM, - true); - // We can either let MCUT compute all possible meshes (including patches etc.), or we can - // constrain the library to compute exactly the boolean op mesh we want. This 'constrained' case - // is done with the following flags. - // NOTE#1: you can extend these flags by bitwise ORing with additional flags (see `McDispatchFlags' in mcut.h) - // NOTE#2: below order of columns MATTERS - const std::map booleanOpts = { - {"A_NOT_B", MC_DISPATCH_FILTER_FRAGMENT_SEALING_INSIDE | MC_DISPATCH_FILTER_FRAGMENT_LOCATION_ABOVE}, - {"B_NOT_A", MC_DISPATCH_FILTER_FRAGMENT_SEALING_OUTSIDE | MC_DISPATCH_FILTER_FRAGMENT_LOCATION_BELOW}, - {"UNION", MC_DISPATCH_FILTER_FRAGMENT_SEALING_OUTSIDE | MC_DISPATCH_FILTER_FRAGMENT_LOCATION_ABOVE}, - {"INTERSECTION", MC_DISPATCH_FILTER_FRAGMENT_SEALING_INSIDE | MC_DISPATCH_FILTER_FRAGMENT_LOCATION_BELOW}, - }; + TriangleMesh tri_src = mcut_to_triangle_mesh(srcMesh); + std::vector src_parts = its_split(tri_src.its); - std::map::const_iterator it = booleanOpts.find(boolean_opts); - McFlags boolOpFlags = it->second; + TriangleMesh tri_cut = mcut_to_triangle_mesh(cutMesh); + std::vector cut_parts = its_split(tri_cut.its); - err = mcDispatch(context, - MC_DISPATCH_VERTEX_ARRAY_DOUBLE | // vertices are in array of doubles - MC_DISPATCH_ENFORCE_GENERAL_POSITION | // perturb if necessary - boolOpFlags, // filter flags which specify the type of output we want - // source mesh - reinterpret_cast(srcMesh.vertexCoordsArray.data()), reinterpret_cast(srcMesh.faceIndicesArray.data()), - srcMesh.faceSizesArray.data(), static_cast(srcMesh.vertexCoordsArray.size() / 3), static_cast(srcMesh.faceSizesArray.size()), - // cut mesh - reinterpret_cast(cutMesh.vertexCoordsArray.data()), cutMesh.faceIndicesArray.data(), cutMesh.faceSizesArray.data(), - static_cast(cutMesh.vertexCoordsArray.size() / 3), static_cast(cutMesh.faceSizesArray.size())); - - // query the number of available connected component - uint32_t numConnComps; - err = mcGetConnectedComponents(context, MC_CONNECTED_COMPONENT_TYPE_FRAGMENT, 0, NULL, &numConnComps); - - std::vector connectedComponents(numConnComps, MC_NULL_HANDLE); - err = mcGetConnectedComponents(context, MC_CONNECTED_COMPONENT_TYPE_FRAGMENT, (uint32_t) connectedComponents.size(), connectedComponents.data(), NULL); - - std::vector outs; - // traversal of all connected components - for (int n = 0; n < numConnComps; ++n) { - // query the data of each connected component from MCUT - McConnectedComponent connComp = connectedComponents[n]; - - // query the vertices - McSize numBytes = 0; - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_VERTEX_DOUBLE, 0, NULL, &numBytes); - uint32_t ccVertexCount = (uint32_t) (numBytes / (sizeof(double) * 3)); - std::vector ccVertices((uint64_t) ccVertexCount * 3u, 0); - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_VERTEX_DOUBLE, numBytes, (void *) ccVertices.data(), NULL); - - // query the faces - numBytes = 0; - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_FACE_TRIANGULATION, 0, NULL, &numBytes); - std::vector ccFaceIndices(numBytes / sizeof(uint32_t), 0); - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_FACE_TRIANGULATION, numBytes, ccFaceIndices.data(), NULL); - std::vector faceSizes(ccFaceIndices.size() / 3, 3); - - const uint32_t ccFaceCount = static_cast(faceSizes.size()); - - // Here we show, how to know when connected components, pertain particular boolean operations. - McPatchLocation patchLocation = (McPatchLocation) 0; - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_PATCH_LOCATION, sizeof(McPatchLocation), &patchLocation, NULL); - - McFragmentLocation fragmentLocation = (McFragmentLocation) 0; - err = mcGetConnectedComponentData(context, connComp, MC_CONNECTED_COMPONENT_DATA_FRAGMENT_LOCATION, sizeof(McFragmentLocation), &fragmentLocation, NULL); + if (src_parts.empty() && boolean_opts == "UNION") { + srcMesh = cutMesh; + return; + } + if(cut_parts.empty()) return; - // rearrange vertices/faces and save into result mesh - std::vector vertices(ccVertexCount); - for (uint32_t i = 0; i < ccVertexCount; ++i) { - vertices[i][0] = (float) ccVertices[(uint64_t) i * 3 + 0]; - vertices[i][1] = (float) ccVertices[(uint64_t) i * 3 + 1]; - vertices[i][2] = (float) ccVertices[(uint64_t) i * 3 + 2]; + // when src mesh has multiple connected components, mcut refuses to work. + // But we can force it to work by spliting the src mesh into disconnected components, + // and do booleans seperately, then merge all the results. + indexed_triangle_set all_its; + if (boolean_opts == "UNION" || boolean_opts == "A_NOT_B") { + for (size_t i = 0; i < src_parts.size(); i++) { + auto src_part = triangle_mesh_to_mcut(src_parts[i]); + for (size_t j = 0; j < cut_parts.size(); j++) { + auto cut_part = triangle_mesh_to_mcut(cut_parts[j]); + bool success = do_boolean_single(*src_part, *cut_part, boolean_opts); + } + TriangleMesh tri_part = mcut_to_triangle_mesh(*src_part); + its_merge(all_its, tri_part.its); } - - // output faces - int faceVertexOffsetBase = 0; - - // for each face in CC - std::vector faces(ccFaceCount); - for (uint32_t f = 0; f < ccFaceCount; ++f) { - bool reverseWindingOrder = (fragmentLocation == MC_FRAGMENT_LOCATION_BELOW) && (patchLocation == MC_PATCH_LOCATION_OUTSIDE); - int faceSize = faceSizes.at(f); - - // for each vertex in face - for (int v = (reverseWindingOrder ? (faceSize - 1) : 0); (reverseWindingOrder ? (v >= 0) : (v < faceSize)); v += (reverseWindingOrder ? -1 : 1)) { - faces[f][v] = ccFaceIndices[(uint64_t) faceVertexOffsetBase + v]; + } + else if (boolean_opts == "INTERSECTION") { + for (size_t i = 0; i < src_parts.size(); i++) { + for (size_t j = 0; j < cut_parts.size(); j++) { + auto src_part = triangle_mesh_to_mcut(src_parts[i]); + auto cut_part = triangle_mesh_to_mcut(cut_parts[j]); + bool success = do_boolean_single(*src_part, *cut_part, boolean_opts); + if (success) { + TriangleMesh tri_part = mcut_to_triangle_mesh(*src_part); + its_merge(all_its, tri_part.its); + } } - faceVertexOffsetBase += faceSize; } - - TriangleMesh out(vertices, faces); - outs.emplace_back(out); } - - // free connected component data - err = mcReleaseConnectedComponents(context, (uint32_t) connectedComponents.size(), connectedComponents.data()); - - // destroy context - err = mcReleaseContext(context); - - return outs; + srcMesh = *triangle_mesh_to_mcut(all_its); } void make_boolean(const TriangleMesh &src_mesh, const TriangleMesh &cut_mesh, std::vector &dst_mesh, const std::string &boolean_opts) diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 520d2f189a9..92592f5f61b 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -85,9 +85,12 @@ McutMeshPtr triangle_mesh_to_mcut(const indexed_triangle_set &M); TriangleMesh mcut_to_triangle_mesh(const McutMesh &mcutmesh); // do boolean and save result to srcMesh +// return true if sucessful +bool do_boolean_single(McutMesh& srcMesh, const McutMesh& cutMesh, const std::string& boolean_opts); +// do boolean of mesh with multiple volumes and save result to srcMesh +// Both srcMesh and cutMesh may have multiple volumes. void do_boolean(McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &boolean_opts); -std::vector make_boolean(const McutMesh &srcMesh, const McutMesh &cutMesh, const std::string &boolean_opts); // do boolean and convert result to TriangleMesh void make_boolean(const TriangleMesh &src_mesh, const TriangleMesh &cut_mesh, std::vector &dst_mesh, const std::string &boolean_opts); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index e0c67a5dcd9..be90c40790b 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2892,15 +2892,16 @@ void ObjectList::boolean() Plater::TakeSnapshot snapshot(wxGetApp().plater(), "boolean"); - Model* model = (*m_objects)[0]->get_model(); + ModelObject* object = (*m_objects)[obj_idxs.front()]; + TriangleMesh mesh = Plater::combine_mesh_fff(*object, -1, [this](const std::string& msg) {return wxGetApp().notification_manager()->push_plater_error_notification(msg); }); + + // add mesh to model as a new object, keep the original object's name and config + Model* model = object->get_model(); ModelObject* new_object = model->add_object(); - new_object->name = (*m_objects)[0]->name; - new_object->config.assign_config((*m_objects)[0]->config); + new_object->name = object->name; + new_object->config.assign_config(object->config); if (new_object->instances.empty()) new_object->add_instance(); - - ModelObject* object = (*m_objects)[obj_idxs.front()]; - TriangleMesh mesh = Plater::combine_mesh_fff(*object, -1, [this](const std::string& msg) {return wxGetApp().notification_manager()->push_plater_error_notification(msg); }); ModelVolume* new_volume = new_object->add_volume(mesh); // BBS: ensure on bed but no need to ensure locate in the center around origin diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aadc0d8b21a..809154f2cf8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -8315,6 +8315,11 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_ get_notification_manager()->bbl_close_plateinfo_notification(); get_notification_manager()->bbl_close_preview_only_notification(); get_notification_manager()->bbl_close_3mf_warn_notification(); + get_notification_manager()->close_notification_of_type(NotificationType::PlaterError); + get_notification_manager()->close_notification_of_type(NotificationType::PlaterWarning); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingError); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingSeriousWarning); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingWarning); if (!silent) wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); @@ -8400,6 +8405,11 @@ void Plater::load_project(wxString const& filename2, get_notification_manager()->bbl_close_plateinfo_notification(); get_notification_manager()->bbl_close_preview_only_notification(); get_notification_manager()->bbl_close_3mf_warn_notification(); + get_notification_manager()->close_notification_of_type(NotificationType::PlaterError); + get_notification_manager()->close_notification_of_type(NotificationType::PlaterWarning); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingError); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingSeriousWarning); + get_notification_manager()->close_notification_of_type(NotificationType::SlicingWarning); auto path = into_path(filename);