mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-17 00:26:14 +03:00
CGAL Fix Model: New for Linux and Mac (#12155)
* CGAL Fix Model Multi OS fix model with CGAL Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Clean unused variables * Early exit * Validation * Orca comments * Steps to fix * Simplify fixer * BY10 to BYCGAL Full refactor * repair_polygon_soup * Revert "repair_polygon_soup" This reverts commit cb88841e7a72a42c148e144fbf0cab146a54c3c8. * CGAL 6.1.1 Update CGAL.cmake * Update MeshBoolean.cpp * Revert "CGAL 6.1.1" This reverts commit c581887adb5f84ec4af97b320067b152f8812f49. * Funca with RF New remake, it seems to be working fine... por ahora Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Update src/libslic3r/MeshBoolean.cpp Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Include cleanup Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Update Part list Revert "Update Part list" This reverts commit 95cab337d7ea602682ee00be2986ef941d0b06c2. Reapply "Update Part list" This reverts commit e401bec579f64b97e3f6deeb4131a8e5a79a146d. * Comments * Update FixModelByCgal.cpp * Remove no 3d parts Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Remove netfabb and w10 sdk Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * Update src/slic3r/Utils/FixModelByCgal.cpp Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> * redundant check * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Revet: suggestion from @RF47 Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> --------- Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
7
.github/workflows/build_orca.yml
vendored
7
.github/workflows/build_orca.yml
vendored
@@ -286,9 +286,10 @@ jobs:
|
||||
- name: Build slicer Win
|
||||
if: runner.os == 'Windows'
|
||||
working-directory: ${{ github.workspace }}
|
||||
env:
|
||||
WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
||||
WindowsSDKVersion: '10.0.26100.0\'
|
||||
# Orca: Removed Netfabb STL fixing service support in favor of CGAL.
|
||||
# env:
|
||||
# WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
||||
# WindowsSDKVersion: '10.0.26100.0\'
|
||||
run: .\build_release_vs.bat slicer
|
||||
|
||||
- name: Create installer Win
|
||||
|
||||
@@ -323,43 +323,44 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(WIN32)
|
||||
if(NOT DEFINED WIN10SDK_PATH)
|
||||
if(DEFINED ENV{WIN10SDK_PATH})
|
||||
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED WIN10SDK_PATH)
|
||||
#BBS: modify win10sdk_path
|
||||
if (EXISTS "${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h")
|
||||
set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}")
|
||||
else()
|
||||
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||
message("${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_PATH)
|
||||
endif()
|
||||
else()
|
||||
# Try to use the default Windows 10 SDK path.
|
||||
if (DEFINED ENV{WindowsSdkDir} AND DEFINED ENV{WindowsSDKVersion})
|
||||
set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
|
||||
else ()
|
||||
set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.26100.0")
|
||||
endif ()
|
||||
if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
|
||||
message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_INCLUDE_PATH)
|
||||
endif()
|
||||
endif()
|
||||
if(WIN10SDK_INCLUDE_PATH)
|
||||
message("Building with Win10 Netfabb STL fixing service support")
|
||||
add_definitions(-DHAS_WIN10SDK)
|
||||
include_directories(SYSTEM "${WIN10SDK_INCLUDE_PATH}")
|
||||
else()
|
||||
message("Building without Win10 Netfabb STL fixing service support")
|
||||
endif()
|
||||
endif()
|
||||
# ORCA: Removed Netfabb STL fixing service support in favor of CGAL.
|
||||
# if(WIN32)
|
||||
# if(NOT DEFINED WIN10SDK_PATH)
|
||||
# if(DEFINED ENV{WIN10SDK_PATH})
|
||||
# set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
# endif()
|
||||
# endif()
|
||||
# if(DEFINED WIN10SDK_PATH)
|
||||
# #BBS: modify win10sdk_path
|
||||
# if (EXISTS "${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h")
|
||||
# set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}")
|
||||
# else()
|
||||
# message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||
# message("${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||
# message("STL fixing by the Netfabb service will not be compiled")
|
||||
# unset(WIN10SDK_PATH)
|
||||
# endif()
|
||||
# else()
|
||||
# # Try to use the default Windows 10 SDK path.
|
||||
# if (DEFINED ENV{WindowsSdkDir} AND DEFINED ENV{WindowsSDKVersion})
|
||||
# set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
|
||||
# else ()
|
||||
# set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.26100.0")
|
||||
# endif ()
|
||||
# if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
|
||||
# message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||
# message("STL fixing by the Netfabb service will not be compiled")
|
||||
# unset(WIN10SDK_INCLUDE_PATH)
|
||||
# endif()
|
||||
# endif()
|
||||
# if(WIN10SDK_INCLUDE_PATH)
|
||||
# message("Building with Win10 Netfabb STL fixing service support")
|
||||
# add_definitions(-DHAS_WIN10SDK)
|
||||
# include_directories(SYSTEM "${WIN10SDK_INCLUDE_PATH}")
|
||||
# else()
|
||||
# message("Building without Win10 Netfabb STL fixing service support")
|
||||
# endif()
|
||||
# endif()
|
||||
|
||||
if (APPLE)
|
||||
message("OS X SDK Path: ${CMAKE_OSX_SYSROOT}")
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
#include <CGAL/Polygon_mesh_processing/remesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
|
||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||
// BBS: for segment
|
||||
#include <CGAL/mesh_segmentation.h>
|
||||
@@ -475,6 +475,92 @@ bool empty(const CGALMesh &mesh)
|
||||
return mesh.m.is_empty();
|
||||
}
|
||||
|
||||
bool repair(TriangleMesh& mesh, RepairedMeshErrors* repaired_errors, std::string* error)
|
||||
{
|
||||
using namespace CGAL;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
if (mesh.empty())
|
||||
return true;
|
||||
|
||||
try {
|
||||
// 1) Convert to polygon soup
|
||||
std::vector<_EpicMesh::Point> points;
|
||||
std::vector<std::vector<std::size_t>> polygons;
|
||||
|
||||
points.reserve(mesh.its.vertices.size());
|
||||
polygons.reserve(mesh.its.indices.size());
|
||||
|
||||
for (const auto& v : mesh.its.vertices)
|
||||
points.emplace_back(v.x(), v.y(), v.z());
|
||||
|
||||
for (const auto& f : mesh.its.indices)
|
||||
polygons.push_back({size_t(f[0]), size_t(f[1]), size_t(f[2])});
|
||||
|
||||
// 2) Aggressive soup cleanup
|
||||
PMP::repair_polygon_soup(points, polygons);
|
||||
|
||||
// 3) Convert soup → mesh
|
||||
_EpicMesh cgal_mesh;
|
||||
PMP::polygon_soup_to_polygon_mesh(points, polygons, cgal_mesh);
|
||||
|
||||
// 4) Remove degenerate geometry
|
||||
PMP::remove_degenerate_faces(cgal_mesh);
|
||||
PMP::remove_isolated_vertices(cgal_mesh);
|
||||
|
||||
// 5) Fix remaining non-manifold vertices
|
||||
PMP::duplicate_non_manifold_vertices(cgal_mesh);
|
||||
|
||||
// 6) Boolean union (keeps only outer shell)
|
||||
_EpicMesh tmp;
|
||||
if (PMP::corefine_and_compute_union(cgal_mesh, cgal_mesh, tmp)) {
|
||||
cgal_mesh = std::move(tmp);
|
||||
}
|
||||
// If it fails, continue anyway with previous mesh
|
||||
|
||||
// 7) Fill holes
|
||||
if (!CGAL::is_closed(cgal_mesh)) {
|
||||
using halfedge_descriptor = boost::graph_traits<_EpicMesh>::halfedge_descriptor;
|
||||
|
||||
std::vector<halfedge_descriptor> borders;
|
||||
PMP::extract_boundary_cycles(cgal_mesh, std::back_inserter(borders));
|
||||
|
||||
for (halfedge_descriptor h : borders) {
|
||||
PMP::triangulate_and_refine_hole(cgal_mesh, h);
|
||||
}
|
||||
}
|
||||
|
||||
// 8) Final validity check
|
||||
if (!CGAL::is_closed(cgal_mesh)) {
|
||||
if (error)
|
||||
*error = "Repair failed: mesh still open after hole filling.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 9) Ensure outward orientation
|
||||
if (!PMP::does_bound_a_volume(cgal_mesh))
|
||||
PMP::orient_to_bound_a_volume(cgal_mesh);
|
||||
|
||||
// 10) Convert back
|
||||
indexed_triangle_set its = cgal_to_indexed_triangle_set(cgal_mesh);
|
||||
|
||||
RepairedMeshErrors errs{};
|
||||
errs.facets_removed = 0;
|
||||
errs.edges_fixed = 0;
|
||||
|
||||
mesh = TriangleMesh(std::move(its), errs);
|
||||
|
||||
if (repaired_errors)
|
||||
*repaired_errors = errs;
|
||||
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
if (error)
|
||||
*error = e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CGALMeshPtr clone(const CGALMesh &m)
|
||||
{
|
||||
return CGALMeshPtr{new CGALMesh{m}};
|
||||
|
||||
@@ -70,6 +70,9 @@ TriangleMesh merge(std::vector<TriangleMesh> meshes);
|
||||
|
||||
bool does_bound_a_volume(const CGALMesh &mesh);
|
||||
bool empty(const CGALMesh &mesh);
|
||||
|
||||
// Repair a mesh using CGAL. Returns true on success. Optionally returns a summary of repairs and an error string.
|
||||
bool repair(TriangleMesh &mesh, RepairedMeshErrors *repaired_errors = nullptr, std::string *error = nullptr);
|
||||
}
|
||||
|
||||
namespace mcut {
|
||||
|
||||
@@ -580,8 +580,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/ESP3D.hpp
|
||||
Utils/FileHelp.cpp
|
||||
Utils/FileHelp.hpp
|
||||
Utils/FixModelByWin10.cpp
|
||||
Utils/FixModelByWin10.hpp
|
||||
Utils/FixModelByCgal.cpp
|
||||
Utils/FixModelByCgal.hpp
|
||||
Utils/FlashAir.cpp
|
||||
Utils/FlashAir.hpp
|
||||
Utils/Flashforge.cpp
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "slic3r/GUI/Tab.hpp"
|
||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||
#include "ParamsPanel.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "wx/utils.h"
|
||||
@@ -879,14 +878,11 @@ void MenuFactory::append_menu_item_rename(wxMenu* menu)
|
||||
menu->AppendSeparator();
|
||||
}
|
||||
|
||||
wxMenuItem* MenuFactory::append_menu_item_fix_through_netfabb(wxMenu* menu)
|
||||
wxMenuItem* MenuFactory::append_menu_item_fix_through_cgal(wxMenu* menu)
|
||||
{
|
||||
if (!is_windows10())
|
||||
return nullptr;
|
||||
|
||||
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix model"), "",
|
||||
[](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
|
||||
[]() {return plater()->can_fix_through_netfabb(); }, plater());
|
||||
[](wxCommandEvent&) { obj_list()->fix_through_cgal(); }, "", menu,
|
||||
[]() {return plater()->can_fix_through_cgal(); }, plater());
|
||||
|
||||
return menu_item;
|
||||
}
|
||||
@@ -1356,7 +1352,7 @@ void MenuFactory::create_common_object_menu(wxMenu* menu)
|
||||
// "Scale to print volume" makes a sense just for whole object
|
||||
append_menu_item_scale_selection_to_fit_print_volume(menu);
|
||||
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
}
|
||||
|
||||
@@ -1395,7 +1391,7 @@ void MenuFactory::create_extra_object_menu()
|
||||
// Object Clone
|
||||
append_menu_item_clone(&m_object_menu);
|
||||
// Object Repair
|
||||
append_menu_item_fix_through_netfabb(&m_object_menu);
|
||||
append_menu_item_fix_through_cgal(&m_object_menu);
|
||||
// Object Simplify
|
||||
append_menu_item_simplify(&m_object_menu);
|
||||
// Object Mesh Subdivision
|
||||
@@ -1452,7 +1448,7 @@ void MenuFactory::create_bbl_assemble_object_menu()
|
||||
// Delete
|
||||
append_menu_item_delete(&m_assemble_object_menu);
|
||||
// Object Repair
|
||||
append_menu_item_fix_through_netfabb(&m_assemble_object_menu);
|
||||
append_menu_item_fix_through_cgal(&m_assemble_object_menu);
|
||||
// Object Simplify
|
||||
append_menu_item_simplify(&m_assemble_object_menu);
|
||||
// Object Mesh Subdivision
|
||||
@@ -1482,7 +1478,7 @@ void MenuFactory::create_part_menu()
|
||||
append_menu_item_reload_from_disk(menu);
|
||||
append_menu_item_export_stl(menu);
|
||||
append_menu_item_export_drc(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
append_menu_item_merge_parts_to_single_part(menu);
|
||||
|
||||
@@ -1507,7 +1503,7 @@ void MenuFactory::create_text_part_menu()
|
||||
|
||||
append_menu_item_edit_text(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_item_center(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
@@ -1523,7 +1519,7 @@ void MenuFactory::create_svg_part_menu()
|
||||
|
||||
append_menu_item_edit_svg(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_items_mirror(menu);
|
||||
menu->AppendSeparator();
|
||||
@@ -1538,7 +1534,7 @@ void MenuFactory::create_bbl_part_menu()
|
||||
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_item_edit_text(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
append_menu_item_simplify(menu);
|
||||
append_menu_item_smooth_mesh(menu);
|
||||
append_menu_item_center(menu);
|
||||
@@ -1859,7 +1855,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
}
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_drop(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
//append_menu_item_simplify(menu);
|
||||
append_menu_item_delete(menu);
|
||||
menu->AppendSeparator();
|
||||
@@ -1884,7 +1880,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
||||
else {
|
||||
append_menu_item_center(menu);
|
||||
append_menu_item_drop(menu);
|
||||
append_menu_item_fix_through_netfabb(menu);
|
||||
append_menu_item_fix_through_cgal(menu);
|
||||
//append_menu_item_simplify(menu);
|
||||
append_menu_item_delete(menu);
|
||||
append_menu_items_convert_unit(menu);
|
||||
@@ -1922,7 +1918,7 @@ wxMenu* MenuFactory::assemble_multi_selection_menu()
|
||||
|
||||
wxMenu* menu = new MenuWithSeparators();
|
||||
append_menu_item_set_visible(menu);
|
||||
//append_menu_item_fix_through_netfabb(menu);
|
||||
//append_menu_item_fix_through_cgal(menu);
|
||||
//append_menu_item_simplify(menu);
|
||||
append_menu_item_delete(menu);
|
||||
menu->AppendSeparator();
|
||||
@@ -1972,7 +1968,7 @@ wxMenu* MenuFactory::assemble_object_menu()
|
||||
// Delete
|
||||
append_menu_item_delete(menu);
|
||||
//// Object Repair
|
||||
//append_menu_item_fix_through_netfabb(menu);
|
||||
//append_menu_item_fix_through_cgal(menu);
|
||||
//// Object Simplify
|
||||
//append_menu_item_simplify(menu);
|
||||
menu->AppendSeparator();
|
||||
|
||||
@@ -139,7 +139,7 @@ private:
|
||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_auto_drop(wxMenu* menu);
|
||||
void append_menu_item_rename(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_cgal(wxMenu* menu);
|
||||
//wxMenuItem* append_menu_item_simplify(wxMenu* menu);
|
||||
void append_menu_item_export_stl(wxMenu* menu, bool is_mulity_menu = false);
|
||||
void append_menu_item_export_drc(wxMenu* menu, bool is_mulity_menu = false);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <wx/utils.h>
|
||||
#include <wx/headerctrl.h>
|
||||
|
||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||
#include "slic3r/Utils/FixModelByCgal.hpp"
|
||||
#include "libslic3r/Format/bbs_3mf.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
@@ -578,7 +578,7 @@ MeshErrorsInfo ObjectList::get_mesh_errors_info(const int obj_idx, const int vol
|
||||
if (non_manifold_edges)
|
||||
*non_manifold_edges = stats.open_edges;
|
||||
|
||||
if (is_windows10() && !sidebar_info)
|
||||
if (!sidebar_info)
|
||||
tooltip += "\n" + _L("Click the icon to repair model object");
|
||||
|
||||
return { tooltip, get_warning_icon_name(stats) };
|
||||
@@ -1550,9 +1550,9 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
|
||||
}
|
||||
else if (col_num == colName)
|
||||
{
|
||||
if (is_windows10() && m_objects_model->HasWarningIcon(item) &&
|
||||
if (m_objects_model->HasWarningIcon(item) &&
|
||||
mouse_pos.x > 2 * wxGetApp().em_unit() && mouse_pos.x < 4 * wxGetApp().em_unit())
|
||||
fix_through_netfabb();
|
||||
fix_through_cgal();
|
||||
else if (evt_context_menu)
|
||||
show_context_menu(evt_context_menu); // show context menu for "Name" column too
|
||||
}
|
||||
@@ -5984,7 +5984,7 @@ void ObjectList::rename_item()
|
||||
update_name_in_model(item);
|
||||
}
|
||||
|
||||
void ObjectList::fix_through_netfabb()
|
||||
void ObjectList::fix_through_cgal()
|
||||
{
|
||||
// Do not fix anything when a gizmo is open. There might be issues with updates
|
||||
// and what is worse, the snapshot time would refer to the internal stack.
|
||||
@@ -6004,11 +6004,11 @@ void ObjectList::fix_through_netfabb()
|
||||
// clear selections from the non-broken models if any exists
|
||||
// and than fill names of models to repairing
|
||||
if (vol_idxs.empty()) {
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
#if !FIX_THROUGH_CGAL_ALWAYS
|
||||
for (int i = int(obj_idxs.size())-1; i >= 0; --i)
|
||||
if (object(obj_idxs[i])->get_repaired_errors_count() == 0)
|
||||
obj_idxs.erase(obj_idxs.begin()+i);
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||
for (int obj_idx : obj_idxs)
|
||||
if (object(obj_idx))
|
||||
model_names.push_back(object(obj_idx)->name);
|
||||
@@ -6016,11 +6016,11 @@ void ObjectList::fix_through_netfabb()
|
||||
else {
|
||||
ModelObject* obj = object(obj_idxs.front());
|
||||
if (obj) {
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
#if !FIX_THROUGH_CGAL_ALWAYS
|
||||
for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
|
||||
if (obj->get_repaired_errors_count(vol_idxs[i]) == 0)
|
||||
vol_idxs.erase(vol_idxs.begin() + i);
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||
for (int vol_idx : vol_idxs)
|
||||
model_names.push_back(obj->volumes[vol_idx]->name);
|
||||
}
|
||||
@@ -6049,13 +6049,18 @@ void ObjectList::fix_through_netfabb()
|
||||
}
|
||||
|
||||
plater->clear_before_change_mesh(obj_idx);
|
||||
const size_t volumes_before = object(obj_idx)->volumes.size();
|
||||
std::string res;
|
||||
if (!fix_model_by_win10_sdk_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
|
||||
if (!fix_model_with_cgal_gui(*(object(obj_idx)), vol_idx, progress_dlg, msg, res))
|
||||
return false;
|
||||
//wxGetApp().plater()->changed_mesh(obj_idx);
|
||||
object(obj_idx)->ensure_on_bed();
|
||||
plater->changed_mesh(obj_idx);
|
||||
|
||||
const size_t volumes_after = object(obj_idx)->volumes.size();
|
||||
if (volumes_after != volumes_before)
|
||||
add_volumes_to_object_in_list(obj_idx);
|
||||
|
||||
plater->get_partplate_list().notify_instance_update(obj_idx, 0);
|
||||
plater->sidebar().obj_list()->update_plate_values_for_items();
|
||||
|
||||
@@ -6079,10 +6084,10 @@ void ObjectList::fix_through_netfabb()
|
||||
if (vol_idxs.empty()) {
|
||||
int vol_idx{ -1 };
|
||||
for (int obj_idx : obj_idxs) {
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
#if !FIX_THROUGH_CGAL_ALWAYS
|
||||
if (object(obj_idx)->get_repaired_errors_count(vol_idx) == 0)
|
||||
continue;
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
|
||||
break;
|
||||
model_idx++;
|
||||
@@ -6115,7 +6120,7 @@ void ObjectList::fix_through_netfabb()
|
||||
}
|
||||
if (msg.IsEmpty())
|
||||
msg = _L("Repairing was canceled");
|
||||
plater->get_notification_manager()->push_notification(NotificationType::NetfabbFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, into_u8(msg));
|
||||
plater->get_notification_manager()->push_notification(NotificationType::CgalFinished, NotificationManager::NotificationLevel::PrintInfoShortNotificationLevel, into_u8(msg));
|
||||
}
|
||||
|
||||
void ObjectList::simplify()
|
||||
|
||||
@@ -37,7 +37,7 @@ typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
|
||||
|
||||
// Manifold mesh may contain self-intersections, so we want to always allow fixing the mesh.
|
||||
#define FIX_THROUGH_NETFABB_ALWAYS 1
|
||||
#define FIX_THROUGH_CGAL_ALWAYS 1
|
||||
|
||||
namespace GUI {
|
||||
struct ObjectVolumeID {
|
||||
@@ -430,7 +430,7 @@ public:
|
||||
void instances_to_separated_objects(const int obj_idx);
|
||||
void split_instances();
|
||||
void rename_item();
|
||||
void fix_through_netfabb();
|
||||
void fix_through_cgal();
|
||||
void simplify();
|
||||
void smooth_mesh();
|
||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||
|
||||
@@ -659,8 +659,7 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
|
||||
cut_with_groove ? cut.perform_with_groove(m_groove, m_rotate_matrix) :
|
||||
cut.perform_with_plane();
|
||||
// fix_non_manifold_edges
|
||||
#ifdef HAS_WIN10SDK
|
||||
if (is_windows10()) {
|
||||
{
|
||||
bool is_showed_dialog = false;
|
||||
bool user_fix_model = false;
|
||||
for (size_t i = 0; i < new_objects.size(); i++) {
|
||||
@@ -687,7 +686,7 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
|
||||
wxString msg = _L("Repairing model object");
|
||||
msg += ": " + from_u8(model_name) + "\n";
|
||||
std::string res;
|
||||
if (!fix_model_by_win10_sdk_gui(*model_object, vol_idx, progress_dlg, msg, res)) return false;
|
||||
if (!fix_model_with_cgal_gui(*model_object, vol_idx, progress_dlg, msg, res)) return false;
|
||||
return true;
|
||||
};
|
||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, true);
|
||||
@@ -700,7 +699,6 @@ void GLGizmoAdvancedCut::perform_cut(const Selection& selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// set offset for new_objects
|
||||
|
||||
// save cut_id to post update synchronization
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "imgui/imgui_internal.h"
|
||||
#include "slic3r/GUI/Field.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "FixModelByWin10.hpp"
|
||||
#include "FixModelByCgal.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -3340,8 +3340,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
cut.perform_with_plane();
|
||||
|
||||
// fix_non_manifold_edges
|
||||
#ifdef HAS_WIN10SDK
|
||||
if (is_windows10()) {
|
||||
{
|
||||
bool is_showed_dialog = false;
|
||||
bool user_fix_model = false;
|
||||
for (size_t i = 0; i < new_objects.size(); i++) {
|
||||
@@ -3368,7 +3367,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
wxString msg = _L("Repairing model object");
|
||||
msg += ": " + from_u8(model_name) + "\n";
|
||||
std::string res;
|
||||
if (!fix_model_by_win10_sdk_gui(*model_object, vol_idx, progress_dlg, msg, res)) return false;
|
||||
if (!fix_model_with_cgal_gui(*model_object, vol_idx, progress_dlg, msg, res)) return false;
|
||||
return true;
|
||||
};
|
||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, true);
|
||||
@@ -3381,7 +3380,6 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
check_objects_after_cut(new_objects);
|
||||
|
||||
// save cut_id to post update synchronization
|
||||
|
||||
@@ -126,8 +126,8 @@ enum class NotificationType
|
||||
SimplifySuggestion,
|
||||
// Change of text will change font to similar one on.
|
||||
UnknownFont,
|
||||
// information about netfabb is finished repairing model (blocking proccess)
|
||||
NetfabbFinished,
|
||||
// information about cgal is finished repairing model (blocking process)
|
||||
CgalFinished,
|
||||
// Short meesage to fill space between start and finish of export
|
||||
ExportOngoing,
|
||||
// Progressbar of download from prusaslicer://url
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "PrintHostDialogs.hpp"
|
||||
#include "../Utils/ASCIIFolding.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "SyncAmsInfoDialog.hpp"
|
||||
#include "../Utils/ASCIIFolding.hpp"
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Utils/Process.hpp"
|
||||
@@ -4731,7 +4730,7 @@ struct Plater::priv
|
||||
bool can_split_to_volumes() const;
|
||||
bool can_arrange() const;
|
||||
bool can_layers_editing() const;
|
||||
bool can_fix_through_netfabb() const;
|
||||
bool can_fix_through_cgal() const;
|
||||
bool can_simplify() const;
|
||||
bool can_smooth_mesh() const;
|
||||
bool can_set_instance_to_object() const;
|
||||
@@ -10240,10 +10239,10 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
|
||||
selection_changed();
|
||||
}
|
||||
|
||||
//BBS: repair model through netfabb
|
||||
//BBS: repair model through cgal
|
||||
void Plater::priv::on_repair_model(wxCommandEvent &event)
|
||||
{
|
||||
wxGetApp().obj_list()->fix_through_netfabb();
|
||||
wxGetApp().obj_list()->fix_through_cgal();
|
||||
}
|
||||
|
||||
void Plater::priv::on_filament_color_changed(wxCommandEvent &event)
|
||||
@@ -11217,15 +11216,15 @@ bool Plater::priv::can_delete_plate() const
|
||||
return q->get_partplate_list().get_plate_count() > 1;
|
||||
}
|
||||
|
||||
bool Plater::priv::can_fix_through_netfabb() const
|
||||
bool Plater::priv::can_fix_through_cgal() const
|
||||
{
|
||||
std::vector<int> obj_idxs, vol_idxs;
|
||||
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
|
||||
|
||||
#if FIX_THROUGH_NETFABB_ALWAYS
|
||||
#if FIX_THROUGH_CGAL_ALWAYS
|
||||
// Fixing always.
|
||||
return ! obj_idxs.empty() || ! vol_idxs.empty();
|
||||
#else // FIX_THROUGH_NETFABB_ALWAYS
|
||||
#else // FIX_THROUGH_CGAL_ALWAYS
|
||||
// Fixing only if the model is not manifold.
|
||||
if (vol_idxs.empty()) {
|
||||
for (auto obj_idx : obj_idxs)
|
||||
@@ -11239,7 +11238,7 @@ bool Plater::priv::can_fix_through_netfabb() const
|
||||
if (model.objects[obj_idx]->get_repaired_errors_count(vol_idx) > 0)
|
||||
return true;
|
||||
return false;
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||
}
|
||||
|
||||
bool Plater::priv::can_simplify() const
|
||||
@@ -17976,15 +17975,13 @@ void Plater::show_object_info()
|
||||
int non_manifold_edges = 0;
|
||||
auto mesh_errors = p->sidebar->obj_list()->get_mesh_errors_info(&info_manifold, &non_manifold_edges);
|
||||
|
||||
#ifndef __WINDOWS__
|
||||
if (non_manifold_edges > 0) {
|
||||
info_manifold += into_u8("\n" + _L("Tips:") + "\n" +_L("\"Fix Model\" feature is currently only on Windows. Please repair the model on Orca Slicer(windows) or CAD softwares."));
|
||||
}
|
||||
#endif //APPLE & LINUX
|
||||
if (non_manifold_edges > 0) {
|
||||
info_manifold += into_u8("\n" + _L("Tips:") + "\n" + _L("Use \"Fix Model\" to repair the mesh."));
|
||||
}
|
||||
|
||||
info_manifold = "<Error>" + info_manifold + "</Error>";
|
||||
info_text += into_u8(info_manifold);
|
||||
notify_manager->bbl_show_objectsinfo_notification(info_text, is_windows10()&&(non_manifold_edges > 0), !(p->current_panel == p->view3D));
|
||||
notify_manager->bbl_show_objectsinfo_notification(info_text, non_manifold_edges > 0, !(p->current_panel == p->view3D));
|
||||
}
|
||||
|
||||
bool Plater::show_publish_dialog(bool show)
|
||||
@@ -18161,7 +18158,7 @@ bool Plater::can_delete_plate() const { return p->can_delete_plate(); }
|
||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
||||
bool Plater::can_decrease_instances() const { return p->can_decrease_instances(); }
|
||||
bool Plater::can_set_instance_to_object() const { return p->can_set_instance_to_object(); }
|
||||
bool Plater::can_fix_through_netfabb() const { return p->can_fix_through_netfabb(); }
|
||||
bool Plater::can_fix_through_cgal() const { return p->can_fix_through_cgal(); }
|
||||
bool Plater::can_simplify() const { return p->can_simplify(); }
|
||||
bool Plater::can_smooth_mesh() const { return p->can_smooth_mesh(); }
|
||||
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
|
||||
|
||||
@@ -662,7 +662,7 @@ public:
|
||||
bool can_increase_instances() const;
|
||||
bool can_decrease_instances() const;
|
||||
bool can_set_instance_to_object() const;
|
||||
bool can_fix_through_netfabb() const;
|
||||
bool can_fix_through_cgal() const;
|
||||
bool can_simplify() const;
|
||||
bool can_smooth_mesh() const;
|
||||
bool can_split_to_objects() const;
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
#include "Tab.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "../Utils/ASCIIFolding.hpp"
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
#include "../Utils/ColorSpaceConvert.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
214
src/slic3r/Utils/FixModelByCgal.cpp
Normal file
214
src/slic3r/Utils/FixModelByCgal.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
#include "FixModelByCgal.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <condition_variable>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/MeshBoolean.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "../GUI/I18N.hpp"
|
||||
|
||||
// Orca: This file provides utilities for repairing 3D model meshes using the CGAL library, handling mesh splitting, merging, and boolean operations.
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
// Orca: Helper functions for analyzing mesh properties and transformations.
|
||||
|
||||
bool is_not_3dimensional_part(const TriangleMesh &mesh)
|
||||
{
|
||||
// Orca: Determines if a mesh is degenerate or represents a non-3dimensional part by checking volume and bounding box dimensions.
|
||||
if (mesh.its.indices.empty())
|
||||
return true;
|
||||
|
||||
indexed_triangle_set tmp = mesh.its;
|
||||
its_remove_degenerate_faces(tmp, true);
|
||||
if (tmp.indices.empty())
|
||||
return true;
|
||||
|
||||
const BoundingBoxf3 bbox = mesh.bounding_box();
|
||||
const Vec3d size = bbox.size();
|
||||
const double min_dim = std::min(size.x(), std::min(size.y(), size.z()));
|
||||
const double max_dim = std::max(size.x(), std::max(size.y(), size.z()));
|
||||
if (min_dim <= EPSILON)
|
||||
return true;
|
||||
|
||||
const double volume = std::abs(its_volume(mesh.its));
|
||||
const double bbox_volume = size.x() * size.y() * size.z();
|
||||
if (volume <= EPSILON)
|
||||
return true;
|
||||
|
||||
const double min_relative_thickness = 1e-6;
|
||||
const double min_volume_ratio = 1e-6;
|
||||
if (min_dim / max_dim <= min_relative_thickness)
|
||||
return true;
|
||||
if (bbox_volume > 0.0 && volume / bbox_volume <= min_volume_ratio)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Orca: Exception class for handling user-initiated cancellation of model repair operations.
|
||||
class RepairCanceledException : public std::exception {
|
||||
public:
|
||||
const char* what() const noexcept override { return "Model repair has been canceled"; }
|
||||
};
|
||||
|
||||
// Orca: Main function to repair model objects using CGAL, with progress dialog and cancellation support.
|
||||
// Returns false if fixing was canceled. fix_result contains error message if failed.
|
||||
bool fix_model_with_cgal_gui(ModelObject &model_object, int volume_idx, GUI::ProgressDialog &progress_dialog, const wxString &msg_header, std::string &fix_result)
|
||||
{
|
||||
// Orca: Synchronization primitives for progress updates between worker thread and GUI.
|
||||
std::mutex mtx;
|
||||
std::condition_variable condition;
|
||||
struct Progress {
|
||||
std::string message;
|
||||
int percent = 0;
|
||||
bool updated = false;
|
||||
} progress;
|
||||
|
||||
std::atomic<bool> canceled = false;
|
||||
std::atomic<bool> finished = false;
|
||||
|
||||
bool success = false;
|
||||
size_t ivolume = 0;
|
||||
|
||||
// Orca: Lambda for updating progress from worker thread.
|
||||
auto on_progress = [&mtx, &condition, &ivolume, &model_object, &progress](const char *msg, unsigned prcnt) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
progress.message = msg;
|
||||
const size_t total = std::max<size_t>(1, model_object.volumes.size());
|
||||
progress.percent = int(std::floor((float(prcnt) + float(ivolume) * 100.f) / float(total)));
|
||||
progress.updated = true;
|
||||
condition.notify_all();
|
||||
};
|
||||
|
||||
// Orca: Worker thread that performs the actual model repair operations.
|
||||
auto worker_thread = std::thread([&model_object, volume_idx, &ivolume, on_progress, &success, &canceled, &finished, &fix_result]() {
|
||||
try {
|
||||
size_t start_volume = volume_idx == -1 ? 0 : size_t(volume_idx);
|
||||
size_t end_volume = volume_idx == -1 ? std::numeric_limits<size_t>::max() : size_t(volume_idx);
|
||||
|
||||
for (ivolume = start_volume; ivolume < model_object.volumes.size(); ++ivolume) {
|
||||
if (volume_idx != -1 && ivolume > end_volume)
|
||||
break;
|
||||
if (canceled)
|
||||
throw RepairCanceledException();
|
||||
|
||||
on_progress(L("Repairing model object"), 10);
|
||||
|
||||
ModelVolume *volume = model_object.volumes[ivolume];
|
||||
|
||||
// Orca: Split splittable volumes into parts for individual processing.
|
||||
size_t parts_count = 1;
|
||||
if (volume->is_splittable()) {
|
||||
parts_count = volume->split(1);
|
||||
if (parts_count > 1) {
|
||||
const std::string msg = Slic3r::format(L("Split into %1% parts"), parts_count);
|
||||
on_progress(msg.c_str(), 10);
|
||||
}
|
||||
}
|
||||
|
||||
size_t part_end = std::min(ivolume + parts_count - 1, model_object.volumes.size() - 1);
|
||||
if (volume_idx != -1)
|
||||
end_volume = part_end;
|
||||
|
||||
size_t removed_parts = 0;
|
||||
for (size_t idx = part_end + 1; idx > ivolume; --idx) {
|
||||
const size_t part_idx = idx - 1;
|
||||
const ModelVolume *part_volume = model_object.volumes[part_idx];
|
||||
if (!is_not_3dimensional_part(part_volume->mesh()))
|
||||
continue;
|
||||
|
||||
model_object.delete_volume(part_idx);
|
||||
++removed_parts;
|
||||
if (part_end > 0)
|
||||
--part_end;
|
||||
else
|
||||
part_end = 0;
|
||||
if (volume_idx != -1)
|
||||
end_volume = part_end;
|
||||
}
|
||||
|
||||
if (removed_parts >= parts_count) {
|
||||
ivolume = part_end;
|
||||
on_progress(L("Repair finished"), 100);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t part_idx = ivolume; part_idx <= part_end && part_idx < model_object.volumes.size(); ++part_idx) {
|
||||
ModelVolume *part_volume = model_object.volumes[part_idx];
|
||||
TriangleMesh mesh = part_volume->mesh();
|
||||
if (its_num_open_edges(mesh.its) != 0) {
|
||||
std::string error;
|
||||
if (!MeshBoolean::cgal::repair(mesh, nullptr, &error))
|
||||
throw Slic3r::RuntimeError(error.empty() ? L("Repair failed") : error.c_str());
|
||||
|
||||
part_volume->set_mesh(std::move(mesh));
|
||||
part_volume->calculate_convex_hull();
|
||||
part_volume->invalidate_convex_hull_2d();
|
||||
part_volume->set_new_unique_id();
|
||||
}
|
||||
}
|
||||
|
||||
ivolume = part_end;
|
||||
|
||||
on_progress(L("Repair finished"), 100);
|
||||
}
|
||||
|
||||
model_object.invalidate_bounding_box();
|
||||
|
||||
if (ivolume > 0)
|
||||
--ivolume;
|
||||
on_progress(L("Repair finished"), 100);
|
||||
success = true;
|
||||
finished = true;
|
||||
} catch (RepairCanceledException &) {
|
||||
canceled = true;
|
||||
finished = true;
|
||||
on_progress(L("Repair canceled"), 100);
|
||||
} catch (std::exception &ex) {
|
||||
success = false;
|
||||
finished = true;
|
||||
fix_result = ex.what();
|
||||
on_progress(ex.what(), 100);
|
||||
}
|
||||
});
|
||||
|
||||
// Orca: Main GUI loop to update progress dialog and handle cancellation.
|
||||
while (!finished) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
condition.wait_for(lock, std::chrono::milliseconds(250), [&progress]{ return progress.updated; });
|
||||
|
||||
// Decrease progress percent slightly to avoid auto-closing.
|
||||
if (!progress_dialog.Update(progress.percent - 1, msg_header + _(progress.message)))
|
||||
canceled = true;
|
||||
else
|
||||
progress_dialog.Fit();
|
||||
|
||||
progress.updated = false;
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
// Nothing to show.
|
||||
} else if (success) {
|
||||
fix_result.clear();
|
||||
}
|
||||
|
||||
if (worker_thread.joinable())
|
||||
worker_thread.join();
|
||||
|
||||
return !canceled;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
18
src/slic3r/Utils/FixModelByCgal.hpp
Normal file
18
src/slic3r/Utils/FixModelByCgal.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef slic3r_GUI_Utils_FixModelByCgal_hpp_
|
||||
#define slic3r_GUI_Utils_FixModelByCgal_hpp_
|
||||
|
||||
#include <string>
|
||||
#include "../GUI/Widgets/ProgressDialog.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class ModelObject;
|
||||
class Print;
|
||||
|
||||
// Return false if fixing was canceled. fix_result is empty on success.
|
||||
extern bool fix_model_with_cgal_gui(ModelObject &model_object, int volume_idx, GUI::ProgressDialog &progress_dlg, const wxString &msg_header, std::string &fix_result);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_Utils_FixModelByCgal_hpp_ */
|
||||
@@ -1,447 +0,0 @@
|
||||
#ifdef HAS_WIN10SDK
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
#endif
|
||||
|
||||
// Windows Runtime
|
||||
#include <roapi.h>
|
||||
// for ComPtr
|
||||
#include <wrl/client.h>
|
||||
|
||||
// from C:/Program Files (x86)/Windows Kits/10/Include/10.0.17134.0/
|
||||
#include <winrt/robuffer.h>
|
||||
#include <winrt/windows.storage.provider.h>
|
||||
#include <winrt/windows.graphics.printing3d.h>
|
||||
|
||||
#include "FixModelByWin10.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <condition_variable>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Format/3mf.hpp"
|
||||
#include "../GUI/GUI.hpp"
|
||||
#include "../GUI/I18N.hpp"
|
||||
#include "../GUI/MsgDialog.hpp"
|
||||
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/progdlg.h>
|
||||
|
||||
extern "C"{
|
||||
// from rapi.h
|
||||
typedef HRESULT (__stdcall* FunctionRoInitialize)(int);
|
||||
typedef HRESULT (__stdcall* FunctionRoUninitialize)();
|
||||
typedef HRESULT (__stdcall* FunctionRoActivateInstance)(HSTRING activatableClassId, IInspectable **instance);
|
||||
typedef HRESULT (__stdcall* FunctionRoGetActivationFactory)(HSTRING activatableClassId, REFIID iid, void **factory);
|
||||
// from winstring.h
|
||||
typedef HRESULT (__stdcall* FunctionWindowsCreateString)(LPCWSTR sourceString, UINT32 length, HSTRING *string);
|
||||
typedef HRESULT (__stdcall* FunctionWindowsDelteString)(HSTRING string);
|
||||
}
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static std::string saving_failed_str = L("Saving objects into the 3MF failed.");
|
||||
|
||||
HMODULE s_hRuntimeObjectLibrary = nullptr;
|
||||
FunctionRoInitialize s_RoInitialize = nullptr;
|
||||
FunctionRoUninitialize s_RoUninitialize = nullptr;
|
||||
FunctionRoActivateInstance s_RoActivateInstance = nullptr;
|
||||
FunctionRoGetActivationFactory s_RoGetActivationFactory = nullptr;
|
||||
FunctionWindowsCreateString s_WindowsCreateString = nullptr;
|
||||
FunctionWindowsDelteString s_WindowsDeleteString = nullptr;
|
||||
|
||||
bool winrt_load_runtime_object_library()
|
||||
{
|
||||
if (s_hRuntimeObjectLibrary == nullptr)
|
||||
s_hRuntimeObjectLibrary = LoadLibrary(L"ComBase.dll");
|
||||
if (s_hRuntimeObjectLibrary != nullptr) {
|
||||
s_RoInitialize = (FunctionRoInitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoInitialize");
|
||||
s_RoUninitialize = (FunctionRoUninitialize) GetProcAddress(s_hRuntimeObjectLibrary, "RoUninitialize");
|
||||
s_RoActivateInstance = (FunctionRoActivateInstance) GetProcAddress(s_hRuntimeObjectLibrary, "RoActivateInstance");
|
||||
s_RoGetActivationFactory = (FunctionRoGetActivationFactory) GetProcAddress(s_hRuntimeObjectLibrary, "RoGetActivationFactory");
|
||||
s_WindowsCreateString = (FunctionWindowsCreateString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsCreateString");
|
||||
s_WindowsDeleteString = (FunctionWindowsDelteString) GetProcAddress(s_hRuntimeObjectLibrary, "WindowsDeleteString");
|
||||
}
|
||||
return s_RoInitialize && s_RoUninitialize && s_RoActivateInstance && s_WindowsCreateString && s_WindowsDeleteString;
|
||||
}
|
||||
|
||||
static HRESULT winrt_activate_instance(const std::wstring &class_name, IInspectable **pinst)
|
||||
{
|
||||
HSTRING hClassName;
|
||||
HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
|
||||
if (S_OK != hr)
|
||||
return hr;
|
||||
hr = (*s_RoActivateInstance)(hClassName, pinst);
|
||||
(*s_WindowsDeleteString)(hClassName);
|
||||
return hr;
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
static HRESULT winrt_activate_instance(const std::wstring &class_name, TYPE **pinst)
|
||||
{
|
||||
IInspectable *pinspectable = nullptr;
|
||||
HRESULT hr = winrt_activate_instance(class_name, &pinspectable);
|
||||
if (S_OK != hr)
|
||||
return hr;
|
||||
hr = pinspectable->QueryInterface(__uuidof(TYPE), (void**)pinst);
|
||||
pinspectable->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT winrt_get_activation_factory(const std::wstring &class_name, REFIID iid, void **pinst)
|
||||
{
|
||||
HSTRING hClassName;
|
||||
HRESULT hr = (*s_WindowsCreateString)(class_name.c_str(), class_name.size(), &hClassName);
|
||||
if (S_OK != hr)
|
||||
return hr;
|
||||
hr = (*s_RoGetActivationFactory)(hClassName, iid, pinst);
|
||||
(*s_WindowsDeleteString)(hClassName);
|
||||
return hr;
|
||||
}
|
||||
|
||||
template<typename TYPE>
|
||||
static HRESULT winrt_get_activation_factory(const std::wstring &class_name, TYPE **pinst)
|
||||
{
|
||||
return winrt_get_activation_factory(class_name, __uuidof(TYPE), reinterpret_cast<void**>(pinst));
|
||||
}
|
||||
|
||||
// To be called often to test whether to cancel the operation.
|
||||
typedef std::function<void ()> ThrowOnCancelFn;
|
||||
|
||||
template<typename T>
|
||||
static AsyncStatus winrt_async_await(const Microsoft::WRL::ComPtr<T> &asyncAction, ThrowOnCancelFn throw_on_cancel, int blocking_tick_ms = 100)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||
asyncAction.As(&asyncInfo);
|
||||
AsyncStatus status;
|
||||
// Ugly blocking loop until the RepairAsync call finishes.
|
||||
//FIXME replace with a callback.
|
||||
// https://social.msdn.microsoft.com/Forums/en-US/a5038fb4-b7b7-4504-969d-c102faa389fb/trying-to-block-an-async-operation-and-wait-for-a-particular-time?forum=vclanguage
|
||||
for (;;) {
|
||||
asyncInfo->get_Status(&status);
|
||||
if (status != AsyncStatus::Started)
|
||||
return status;
|
||||
throw_on_cancel();
|
||||
::Sleep(blocking_tick_ms);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT winrt_open_file_stream(
|
||||
const std::wstring &path,
|
||||
ABI::Windows::Storage::FileAccessMode mode,
|
||||
ABI::Windows::Storage::Streams::IRandomAccessStream **fileStream,
|
||||
ThrowOnCancelFn throw_on_cancel)
|
||||
{
|
||||
// Get the file factory.
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFileStatics> fileFactory;
|
||||
HRESULT hr = winrt_get_activation_factory(L"Windows.Storage.StorageFile", fileFactory.GetAddressOf());
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
// Open the file asynchronously.
|
||||
HSTRING hstr_path;
|
||||
hr = (*s_WindowsCreateString)(path.c_str(), path.size(), &hstr_path);
|
||||
if (FAILED(hr)) return hr;
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile*>> fileOpenAsync;
|
||||
hr = fileFactory->GetFileFromPathAsync(hstr_path, fileOpenAsync.GetAddressOf());
|
||||
if (FAILED(hr)) return hr;
|
||||
(*s_WindowsDeleteString)(hstr_path);
|
||||
|
||||
// Wait until the file gets open, get the actual file.
|
||||
AsyncStatus status = winrt_async_await(fileOpenAsync, throw_on_cancel);
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::IStorageFile> storageFile;
|
||||
if (status == AsyncStatus::Completed) {
|
||||
hr = fileOpenAsync->GetResults(storageFile.GetAddressOf());
|
||||
} else {
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||
hr = fileOpenAsync.As(&asyncInfo);
|
||||
if (FAILED(hr)) return hr;
|
||||
HRESULT err;
|
||||
hr = asyncInfo->get_ErrorCode(&err);
|
||||
return FAILED(hr) ? hr : err;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> fileStreamAsync;
|
||||
hr = storageFile->OpenAsync(mode, fileStreamAsync.GetAddressOf());
|
||||
if (FAILED(hr)) return hr;
|
||||
|
||||
status = winrt_async_await(fileStreamAsync, throw_on_cancel);
|
||||
if (status == AsyncStatus::Completed) {
|
||||
hr = fileStreamAsync->GetResults(fileStream);
|
||||
} else {
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||
hr = fileStreamAsync.As(&asyncInfo);
|
||||
if (FAILED(hr)) return hr;
|
||||
HRESULT err;
|
||||
hr = asyncInfo->get_ErrorCode(&err);
|
||||
if (!FAILED(hr))
|
||||
hr = err;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
bool is_windows10()
|
||||
{
|
||||
HKEY hKey;
|
||||
LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey);
|
||||
if (lRes == ERROR_SUCCESS) {
|
||||
WCHAR szBuffer[512];
|
||||
DWORD dwBufferSize = sizeof(szBuffer);
|
||||
lRes = RegQueryValueExW(hKey, L"ProductName", 0, nullptr, (LPBYTE)szBuffer, &dwBufferSize);
|
||||
if (lRes == ERROR_SUCCESS)
|
||||
return wcsncmp(szBuffer, L"Windows 10", 10) == 0;
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Progress function, to be called regularly to update the progress.
|
||||
typedef std::function<void (const char * /* message */, unsigned /* progress */)> ProgressFn;
|
||||
|
||||
void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path_dst, ProgressFn on_progress, ThrowOnCancelFn throw_on_cancel)
|
||||
{
|
||||
if (! is_windows10())
|
||||
throw Slic3r::RuntimeError(L("Only Windows 10 is supported."));
|
||||
|
||||
if (! winrt_load_runtime_object_library())
|
||||
throw Slic3r::RuntimeError(L("Failed to initialize the WinRT library."));
|
||||
|
||||
HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
|
||||
{
|
||||
on_progress(L("Exporting objects"), 20);
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> fileStream;
|
||||
hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel);
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3D3MFPackage> printing3d3mfpackage;
|
||||
hr = winrt_activate_instance(L"Windows.Graphics.Printing3D.Printing3D3MFPackage", printing3d3mfpackage.GetAddressOf());
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Graphics::Printing3D::Printing3DModel*>> modelAsync;
|
||||
hr = printing3d3mfpackage->LoadModelFromPackageAsync(fileStream.Get(), modelAsync.GetAddressOf());
|
||||
|
||||
AsyncStatus status = winrt_async_await(modelAsync, throw_on_cancel);
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Printing3D::IPrinting3DModel> model;
|
||||
if (status == AsyncStatus::Completed)
|
||||
hr = modelAsync->GetResults(model.GetAddressOf());
|
||||
else
|
||||
throw Slic3r::RuntimeError(L("Failed loading objects."));
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IVector<ABI::Windows::Graphics::Printing3D::Printing3DMesh*>> meshes;
|
||||
hr = model->get_Meshes(meshes.GetAddressOf());
|
||||
unsigned num_meshes = 0;
|
||||
hr = meshes->get_Size(&num_meshes);
|
||||
on_progress(L("Repairing object by Windows service"), 40);
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> repairAsync;
|
||||
hr = model->RepairAsync(repairAsync.GetAddressOf());
|
||||
status = winrt_async_await(repairAsync, throw_on_cancel);
|
||||
if (status != AsyncStatus::Completed)
|
||||
throw Slic3r::RuntimeError(L("Repair failed."));
|
||||
repairAsync->GetResults();
|
||||
|
||||
on_progress(L("Loading repaired objects"), 60);
|
||||
|
||||
// Verify the number of meshes returned after the repair action.
|
||||
meshes.Reset();
|
||||
hr = model->get_Meshes(meshes.GetAddressOf());
|
||||
hr = meshes->get_Size(&num_meshes);
|
||||
|
||||
// Save model to this class' Printing3D3MFPackage.
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncAction> saveToPackageAsync;
|
||||
hr = printing3d3mfpackage->SaveModelToPackageAsync(model.Get(), saveToPackageAsync.GetAddressOf());
|
||||
status = winrt_async_await(saveToPackageAsync, throw_on_cancel);
|
||||
if (status != AsyncStatus::Completed)
|
||||
throw Slic3r::RuntimeError(saving_failed_str);
|
||||
hr = saveToPackageAsync->GetResults();
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::Streams::IRandomAccessStream*>> generatorStreamAsync;
|
||||
hr = printing3d3mfpackage->SaveAsync(generatorStreamAsync.GetAddressOf());
|
||||
status = winrt_async_await(generatorStreamAsync, throw_on_cancel);
|
||||
if (status != AsyncStatus::Completed)
|
||||
throw Slic3r::RuntimeError(saving_failed_str);
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> generatorStream;
|
||||
hr = generatorStreamAsync->GetResults(generatorStream.GetAddressOf());
|
||||
|
||||
// Go to the beginning of the stream.
|
||||
generatorStream->Seek(0);
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IInputStream> inputStream;
|
||||
hr = generatorStream.As(&inputStream);
|
||||
|
||||
// Get the buffer factory.
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBufferFactory> bufferFactory;
|
||||
hr = winrt_get_activation_factory(L"Windows.Storage.Streams.Buffer", bufferFactory.GetAddressOf());
|
||||
|
||||
// Open the destination file.
|
||||
FILE *fout = boost::nowide::fopen(path_dst.c_str(), "wb");
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IBuffer> buffer;
|
||||
byte *buffer_ptr;
|
||||
bufferFactory->Create(65536 * 2048, buffer.GetAddressOf());
|
||||
{
|
||||
Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
|
||||
buffer.As(&bufferByteAccess);
|
||||
hr = bufferByteAccess->Buffer(&buffer_ptr);
|
||||
}
|
||||
uint32_t length;
|
||||
hr = buffer->get_Length(&length);
|
||||
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncOperationWithProgress<ABI::Windows::Storage::Streams::IBuffer*, UINT32>> asyncRead;
|
||||
for (;;) {
|
||||
hr = inputStream->ReadAsync(buffer.Get(), 65536 * 2048, ABI::Windows::Storage::Streams::InputStreamOptions_ReadAhead, asyncRead.GetAddressOf());
|
||||
status = winrt_async_await(asyncRead, throw_on_cancel);
|
||||
if (status != AsyncStatus::Completed)
|
||||
throw Slic3r::RuntimeError(saving_failed_str);
|
||||
hr = buffer->get_Length(&length);
|
||||
if (length == 0)
|
||||
break;
|
||||
fwrite(buffer_ptr, length, 1, fout);
|
||||
}
|
||||
fclose(fout);
|
||||
// Here all the COM objects will be released through the ComPtr destructors.
|
||||
}
|
||||
(*s_RoUninitialize)();
|
||||
}
|
||||
|
||||
class RepairCanceledException : public std::exception {
|
||||
public:
|
||||
const char* what() const throw() { return "Model repair has been canceled"; }
|
||||
};
|
||||
|
||||
// returt FALSE, if fixing was canceled
|
||||
// fix_result is empty, if fixing finished successfully
|
||||
// fix_result containes a message if fixing failed
|
||||
bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx, GUI::ProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::condition_variable condition;
|
||||
struct Progress {
|
||||
std::string message;
|
||||
int percent = 0;
|
||||
bool updated = false;
|
||||
} progress;
|
||||
std::atomic<bool> canceled = false;
|
||||
std::atomic<bool> finished = false;
|
||||
|
||||
std::vector<ModelVolume*> volumes;
|
||||
if (volume_idx == -1)
|
||||
volumes = model_object.volumes;
|
||||
else
|
||||
volumes.emplace_back(model_object.volumes[volume_idx]);
|
||||
|
||||
// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
|
||||
// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
|
||||
bool success = false;
|
||||
size_t ivolume = 0;
|
||||
auto on_progress = [&mtx, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
progress.message = msg;
|
||||
progress.percent = (int)floor((float(prcnt) + float(ivolume) * 100.f) / float(volumes.size()));
|
||||
progress.updated = true;
|
||||
condition.notify_all();
|
||||
};
|
||||
auto worker_thread = boost::thread([&model_object, &volumes, &ivolume, on_progress, &success, &canceled, &finished]() {
|
||||
try {
|
||||
std::vector<TriangleMesh> meshes_repaired;
|
||||
meshes_repaired.reserve(volumes.size());
|
||||
for (; ivolume < volumes.size(); ++ ivolume) {
|
||||
on_progress(L("Exporting objects"), 0);
|
||||
boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
path_src += ".3mf";
|
||||
Model model;
|
||||
ModelObject *mo = model.add_object();
|
||||
mo->add_volume(*volumes[ivolume]);
|
||||
|
||||
// We are about to save a 3mf, fix it by netfabb and load the fixed 3mf back.
|
||||
// store_3mf currently bakes the volume transformation into the mesh itself.
|
||||
// If we then loaded the repaired 3mf and pushed the mesh into the original ModelVolume
|
||||
// (which remembers the matrix the whole time), the transformation would be used twice.
|
||||
// We will therefore set the volume transform on the dummy ModelVolume to identity.
|
||||
mo->volumes.back()->set_transformation(Geometry::Transformation());
|
||||
|
||||
mo->add_instance();
|
||||
if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false, nullptr, false)) {
|
||||
boost::filesystem::remove(path_src);
|
||||
throw Slic3r::RuntimeError(L("Exporting 3MF file failed"));
|
||||
}
|
||||
model.clear_objects();
|
||||
model.clear_materials();
|
||||
boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
||||
path_dst += ".3mf";
|
||||
fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress,
|
||||
[&canceled]() { if (canceled) throw RepairCanceledException(); });
|
||||
boost::filesystem::remove(path_src);
|
||||
// PresetBundle bundle;
|
||||
on_progress(L("Loading repaired objects"), 80);
|
||||
DynamicPrintConfig config;
|
||||
ConfigSubstitutionContext config_substitutions{ ForwardCompatibilitySubstitutionRule::EnableSilent };
|
||||
bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), config, config_substitutions, &model, false);
|
||||
boost::filesystem::remove(path_dst);
|
||||
if (! loaded)
|
||||
throw Slic3r::RuntimeError(L("Import 3MF file failed"));
|
||||
if (model.objects.size() == 0)
|
||||
throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any object"));
|
||||
if (model.objects.size() > 1)
|
||||
throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one object"));
|
||||
if (model.objects.front()->volumes.size() == 0)
|
||||
throw Slic3r::RuntimeError(L("Repaired 3MF file does not contain any volume"));
|
||||
if (model.objects.front()->volumes.size() > 1)
|
||||
throw Slic3r::RuntimeError(L("Repaired 3MF file contains more than one volume"));
|
||||
meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh()));
|
||||
}
|
||||
for (size_t i = 0; i < volumes.size(); ++ i) {
|
||||
volumes[i]->set_mesh(std::move(meshes_repaired[i]));
|
||||
volumes[i]->calculate_convex_hull();
|
||||
volumes[i]->invalidate_convex_hull_2d();
|
||||
volumes[i]->set_new_unique_id();
|
||||
}
|
||||
model_object.invalidate_bounding_box();
|
||||
-- ivolume;
|
||||
on_progress(L("Repair finished"), 100);
|
||||
success = true;
|
||||
finished = true;
|
||||
} catch (RepairCanceledException & /* ex */) {
|
||||
canceled = true;
|
||||
finished = true;
|
||||
on_progress(L("Repair canceled"), 100);
|
||||
} catch (std::exception &ex) {
|
||||
success = false;
|
||||
finished = true;
|
||||
on_progress(ex.what(), 100);
|
||||
}
|
||||
});
|
||||
while (! finished) {
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
condition.wait_for(lock, std::chrono::milliseconds(250), [&progress]{ return progress.updated; });
|
||||
// decrease progress.percent value to avoid closing of the progress dialog
|
||||
if (!progress_dialog.Update(progress.percent-1, msg_header + _(progress.message)))
|
||||
canceled = true;
|
||||
else
|
||||
progress_dialog.Fit();
|
||||
progress.updated = false;
|
||||
}
|
||||
|
||||
if (canceled) {
|
||||
// Nothing to show.
|
||||
} else if (success) {
|
||||
fix_result = "";
|
||||
} else {
|
||||
fix_result = progress.message;
|
||||
}
|
||||
worker_thread.join();
|
||||
return !canceled;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* HAS_WIN10SDK */
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef slic3r_GUI_Utils_FixModelByWin10_hpp_
|
||||
#define slic3r_GUI_Utils_FixModelByWin10_hpp_
|
||||
|
||||
#include <string>
|
||||
#include "../GUI/Widgets/ProgressDialog.hpp"
|
||||
|
||||
class ProgressDialog;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class ModelObject;
|
||||
class Print;
|
||||
|
||||
#ifdef HAS_WIN10SDK
|
||||
|
||||
extern bool is_windows10();
|
||||
// returt false, if fixing was canceled
|
||||
extern bool fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx,GUI::ProgressDialog &progress_dlg, const wxString &msg_header, std::string &fix_result);
|
||||
|
||||
#else /* HAS_WIN10SDK */
|
||||
|
||||
inline bool is_windows10() { return false; }
|
||||
// returt false, if fixing was canceled
|
||||
inline bool fix_model_by_win10_sdk_gui(ModelObject &, int, GUI::ProgressDialog &, const wxString &, std::string &) { return false; }
|
||||
|
||||
#endif /* HAS_WIN10SDK */
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GUI_Utils_FixModelByWin10_hpp_ */
|
||||
Reference in New Issue
Block a user