mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-12 11:37:42 +00: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
|
- name: Build slicer Win
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
env:
|
# Orca: Removed Netfabb STL fixing service support in favor of CGAL.
|
||||||
WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
# env:
|
||||||
WindowsSDKVersion: '10.0.26100.0\'
|
# WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
||||||
|
# WindowsSDKVersion: '10.0.26100.0\'
|
||||||
run: .\build_release_vs.bat slicer
|
run: .\build_release_vs.bat slicer
|
||||||
|
|
||||||
- name: Create installer Win
|
- 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.
|
# 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
|
# We pick it from environment if it is not defined in another way
|
||||||
if(WIN32)
|
# ORCA: Removed Netfabb STL fixing service support in favor of CGAL.
|
||||||
if(NOT DEFINED WIN10SDK_PATH)
|
# if(WIN32)
|
||||||
if(DEFINED ENV{WIN10SDK_PATH})
|
# if(NOT DEFINED WIN10SDK_PATH)
|
||||||
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
# if(DEFINED ENV{WIN10SDK_PATH})
|
||||||
endif()
|
# set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||||
endif()
|
# endif()
|
||||||
if(DEFINED WIN10SDK_PATH)
|
# endif()
|
||||||
#BBS: modify win10sdk_path
|
# if(DEFINED WIN10SDK_PATH)
|
||||||
if (EXISTS "${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h")
|
# #BBS: modify win10sdk_path
|
||||||
set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}")
|
# if (EXISTS "${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h")
|
||||||
else()
|
# set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}")
|
||||||
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
# else()
|
||||||
message("${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
# message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||||
message("STL fixing by the Netfabb service will not be compiled")
|
# message("${WIN10SDK_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||||
unset(WIN10SDK_PATH)
|
# message("STL fixing by the Netfabb service will not be compiled")
|
||||||
endif()
|
# unset(WIN10SDK_PATH)
|
||||||
else()
|
# endif()
|
||||||
# Try to use the default Windows 10 SDK path.
|
# else()
|
||||||
if (DEFINED ENV{WindowsSdkDir} AND DEFINED ENV{WindowsSDKVersion})
|
# # Try to use the default Windows 10 SDK path.
|
||||||
set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
|
# if (DEFINED ENV{WindowsSdkDir} AND DEFINED ENV{WindowsSDKVersion})
|
||||||
else ()
|
# set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
|
||||||
set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.26100.0")
|
# else ()
|
||||||
endif ()
|
# set(WIN10SDK_INCLUDE_PATH "C:/Program Files (x86)/Windows Kits/10/Include/10.0.26100.0")
|
||||||
if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
|
# endif ()
|
||||||
message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
# if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
|
||||||
message("STL fixing by the Netfabb service will not be compiled")
|
# message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||||
unset(WIN10SDK_INCLUDE_PATH)
|
# message("STL fixing by the Netfabb service will not be compiled")
|
||||||
endif()
|
# unset(WIN10SDK_INCLUDE_PATH)
|
||||||
endif()
|
# endif()
|
||||||
if(WIN10SDK_INCLUDE_PATH)
|
# endif()
|
||||||
message("Building with Win10 Netfabb STL fixing service support")
|
# if(WIN10SDK_INCLUDE_PATH)
|
||||||
add_definitions(-DHAS_WIN10SDK)
|
# message("Building with Win10 Netfabb STL fixing service support")
|
||||||
include_directories(SYSTEM "${WIN10SDK_INCLUDE_PATH}")
|
# add_definitions(-DHAS_WIN10SDK)
|
||||||
else()
|
# include_directories(SYSTEM "${WIN10SDK_INCLUDE_PATH}")
|
||||||
message("Building without Win10 Netfabb STL fixing service support")
|
# else()
|
||||||
endif()
|
# message("Building without Win10 Netfabb STL fixing service support")
|
||||||
endif()
|
# endif()
|
||||||
|
# endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
message("OS X SDK Path: ${CMAKE_OSX_SYSROOT}")
|
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/orient_polygon_soup.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/remesh.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>
|
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||||
// BBS: for segment
|
// BBS: for segment
|
||||||
#include <CGAL/mesh_segmentation.h>
|
#include <CGAL/mesh_segmentation.h>
|
||||||
@@ -475,6 +475,92 @@ bool empty(const CGALMesh &mesh)
|
|||||||
return mesh.m.is_empty();
|
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)
|
CGALMeshPtr clone(const CGALMesh &m)
|
||||||
{
|
{
|
||||||
return CGALMeshPtr{new 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 does_bound_a_volume(const CGALMesh &mesh);
|
||||||
bool empty(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 {
|
namespace mcut {
|
||||||
|
|||||||
@@ -580,8 +580,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
Utils/ESP3D.hpp
|
Utils/ESP3D.hpp
|
||||||
Utils/FileHelp.cpp
|
Utils/FileHelp.cpp
|
||||||
Utils/FileHelp.hpp
|
Utils/FileHelp.hpp
|
||||||
Utils/FixModelByWin10.cpp
|
Utils/FixModelByCgal.cpp
|
||||||
Utils/FixModelByWin10.hpp
|
Utils/FixModelByCgal.hpp
|
||||||
Utils/FlashAir.cpp
|
Utils/FlashAir.cpp
|
||||||
Utils/FlashAir.hpp
|
Utils/FlashAir.hpp
|
||||||
Utils/Flashforge.cpp
|
Utils/Flashforge.cpp
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "slic3r/GUI/Tab.hpp"
|
#include "slic3r/GUI/Tab.hpp"
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
|
||||||
#include "ParamsPanel.hpp"
|
#include "ParamsPanel.hpp"
|
||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
#include "wx/utils.h"
|
#include "wx/utils.h"
|
||||||
@@ -879,14 +878,11 @@ void MenuFactory::append_menu_item_rename(wxMenu* menu)
|
|||||||
menu->AppendSeparator();
|
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"), "",
|
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _L("Fix model"), "",
|
||||||
[](wxCommandEvent&) { obj_list()->fix_through_netfabb(); }, "", menu,
|
[](wxCommandEvent&) { obj_list()->fix_through_cgal(); }, "", menu,
|
||||||
[]() {return plater()->can_fix_through_netfabb(); }, plater());
|
[]() {return plater()->can_fix_through_cgal(); }, plater());
|
||||||
|
|
||||||
return menu_item;
|
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
|
// "Scale to print volume" makes a sense just for whole object
|
||||||
append_menu_item_scale_selection_to_fit_print_volume(menu);
|
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);
|
append_menu_items_mirror(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1395,7 +1391,7 @@ void MenuFactory::create_extra_object_menu()
|
|||||||
// Object Clone
|
// Object Clone
|
||||||
append_menu_item_clone(&m_object_menu);
|
append_menu_item_clone(&m_object_menu);
|
||||||
// Object Repair
|
// Object Repair
|
||||||
append_menu_item_fix_through_netfabb(&m_object_menu);
|
append_menu_item_fix_through_cgal(&m_object_menu);
|
||||||
// Object Simplify
|
// Object Simplify
|
||||||
append_menu_item_simplify(&m_object_menu);
|
append_menu_item_simplify(&m_object_menu);
|
||||||
// Object Mesh Subdivision
|
// Object Mesh Subdivision
|
||||||
@@ -1452,7 +1448,7 @@ void MenuFactory::create_bbl_assemble_object_menu()
|
|||||||
// Delete
|
// Delete
|
||||||
append_menu_item_delete(&m_assemble_object_menu);
|
append_menu_item_delete(&m_assemble_object_menu);
|
||||||
// Object Repair
|
// Object Repair
|
||||||
append_menu_item_fix_through_netfabb(&m_assemble_object_menu);
|
append_menu_item_fix_through_cgal(&m_assemble_object_menu);
|
||||||
// Object Simplify
|
// Object Simplify
|
||||||
append_menu_item_simplify(&m_assemble_object_menu);
|
append_menu_item_simplify(&m_assemble_object_menu);
|
||||||
// Object Mesh Subdivision
|
// Object Mesh Subdivision
|
||||||
@@ -1482,7 +1478,7 @@ void MenuFactory::create_part_menu()
|
|||||||
append_menu_item_reload_from_disk(menu);
|
append_menu_item_reload_from_disk(menu);
|
||||||
append_menu_item_export_stl(menu);
|
append_menu_item_export_stl(menu);
|
||||||
append_menu_item_export_drc(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_items_mirror(menu);
|
||||||
append_menu_item_merge_parts_to_single_part(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_edit_text(menu);
|
||||||
append_menu_item_delete(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_simplify(menu);
|
||||||
append_menu_item_center(menu);
|
append_menu_item_center(menu);
|
||||||
append_menu_items_mirror(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_edit_svg(menu);
|
||||||
append_menu_item_delete(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_simplify(menu);
|
||||||
append_menu_items_mirror(menu);
|
append_menu_items_mirror(menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
@@ -1538,7 +1534,7 @@ void MenuFactory::create_bbl_part_menu()
|
|||||||
|
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
append_menu_item_edit_text(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_simplify(menu);
|
||||||
append_menu_item_smooth_mesh(menu);
|
append_menu_item_smooth_mesh(menu);
|
||||||
append_menu_item_center(menu);
|
append_menu_item_center(menu);
|
||||||
@@ -1859,7 +1855,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||||||
}
|
}
|
||||||
append_menu_item_center(menu);
|
append_menu_item_center(menu);
|
||||||
append_menu_item_drop(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_simplify(menu);
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
@@ -1884,7 +1880,7 @@ wxMenu* MenuFactory::multi_selection_menu()
|
|||||||
else {
|
else {
|
||||||
append_menu_item_center(menu);
|
append_menu_item_center(menu);
|
||||||
append_menu_item_drop(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_simplify(menu);
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
append_menu_items_convert_unit(menu);
|
append_menu_items_convert_unit(menu);
|
||||||
@@ -1922,7 +1918,7 @@ wxMenu* MenuFactory::assemble_multi_selection_menu()
|
|||||||
|
|
||||||
wxMenu* menu = new MenuWithSeparators();
|
wxMenu* menu = new MenuWithSeparators();
|
||||||
append_menu_item_set_visible(menu);
|
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_simplify(menu);
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
@@ -1972,7 +1968,7 @@ wxMenu* MenuFactory::assemble_object_menu()
|
|||||||
// Delete
|
// Delete
|
||||||
append_menu_item_delete(menu);
|
append_menu_item_delete(menu);
|
||||||
//// Object Repair
|
//// Object Repair
|
||||||
//append_menu_item_fix_through_netfabb(menu);
|
//append_menu_item_fix_through_cgal(menu);
|
||||||
//// Object Simplify
|
//// Object Simplify
|
||||||
//append_menu_item_simplify(menu);
|
//append_menu_item_simplify(menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ private:
|
|||||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_auto_drop(wxMenu* menu);
|
wxMenuItem* append_menu_item_auto_drop(wxMenu* menu);
|
||||||
void append_menu_item_rename(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);
|
//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_stl(wxMenu* menu, bool is_mulity_menu = false);
|
||||||
void append_menu_item_export_drc(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/utils.h>
|
||||||
#include <wx/headerctrl.h>
|
#include <wx/headerctrl.h>
|
||||||
|
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
#include "slic3r/Utils/FixModelByCgal.hpp"
|
||||||
#include "libslic3r/Format/bbs_3mf.hpp"
|
#include "libslic3r/Format/bbs_3mf.hpp"
|
||||||
#include "libslic3r/PrintConfig.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)
|
if (non_manifold_edges)
|
||||||
*non_manifold_edges = stats.open_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");
|
tooltip += "\n" + _L("Click the icon to repair model object");
|
||||||
|
|
||||||
return { tooltip, get_warning_icon_name(stats) };
|
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)
|
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())
|
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)
|
else if (evt_context_menu)
|
||||||
show_context_menu(evt_context_menu); // show context menu for "Name" column too
|
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);
|
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
|
// 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.
|
// 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
|
// clear selections from the non-broken models if any exists
|
||||||
// and than fill names of models to repairing
|
// and than fill names of models to repairing
|
||||||
if (vol_idxs.empty()) {
|
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)
|
for (int i = int(obj_idxs.size())-1; i >= 0; --i)
|
||||||
if (object(obj_idxs[i])->get_repaired_errors_count() == 0)
|
if (object(obj_idxs[i])->get_repaired_errors_count() == 0)
|
||||||
obj_idxs.erase(obj_idxs.begin()+i);
|
obj_idxs.erase(obj_idxs.begin()+i);
|
||||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||||
for (int obj_idx : obj_idxs)
|
for (int obj_idx : obj_idxs)
|
||||||
if (object(obj_idx))
|
if (object(obj_idx))
|
||||||
model_names.push_back(object(obj_idx)->name);
|
model_names.push_back(object(obj_idx)->name);
|
||||||
@@ -6016,11 +6016,11 @@ void ObjectList::fix_through_netfabb()
|
|||||||
else {
|
else {
|
||||||
ModelObject* obj = object(obj_idxs.front());
|
ModelObject* obj = object(obj_idxs.front());
|
||||||
if (obj) {
|
if (obj) {
|
||||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
#if !FIX_THROUGH_CGAL_ALWAYS
|
||||||
for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
|
for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
|
||||||
if (obj->get_repaired_errors_count(vol_idxs[i]) == 0)
|
if (obj->get_repaired_errors_count(vol_idxs[i]) == 0)
|
||||||
vol_idxs.erase(vol_idxs.begin() + i);
|
vol_idxs.erase(vol_idxs.begin() + i);
|
||||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||||
for (int vol_idx : vol_idxs)
|
for (int vol_idx : vol_idxs)
|
||||||
model_names.push_back(obj->volumes[vol_idx]->name);
|
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);
|
plater->clear_before_change_mesh(obj_idx);
|
||||||
|
const size_t volumes_before = object(obj_idx)->volumes.size();
|
||||||
std::string res;
|
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;
|
return false;
|
||||||
//wxGetApp().plater()->changed_mesh(obj_idx);
|
//wxGetApp().plater()->changed_mesh(obj_idx);
|
||||||
object(obj_idx)->ensure_on_bed();
|
object(obj_idx)->ensure_on_bed();
|
||||||
plater->changed_mesh(obj_idx);
|
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->get_partplate_list().notify_instance_update(obj_idx, 0);
|
||||||
plater->sidebar().obj_list()->update_plate_values_for_items();
|
plater->sidebar().obj_list()->update_plate_values_for_items();
|
||||||
|
|
||||||
@@ -6079,10 +6084,10 @@ void ObjectList::fix_through_netfabb()
|
|||||||
if (vol_idxs.empty()) {
|
if (vol_idxs.empty()) {
|
||||||
int vol_idx{ -1 };
|
int vol_idx{ -1 };
|
||||||
for (int obj_idx : obj_idxs) {
|
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)
|
if (object(obj_idx)->get_repaired_errors_count(vol_idx) == 0)
|
||||||
continue;
|
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))
|
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
|
||||||
break;
|
break;
|
||||||
model_idx++;
|
model_idx++;
|
||||||
@@ -6115,7 +6120,7 @@ void ObjectList::fix_through_netfabb()
|
|||||||
}
|
}
|
||||||
if (msg.IsEmpty())
|
if (msg.IsEmpty())
|
||||||
msg = _L("Repairing was canceled");
|
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()
|
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;
|
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.
|
// 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 {
|
namespace GUI {
|
||||||
struct ObjectVolumeID {
|
struct ObjectVolumeID {
|
||||||
@@ -430,7 +430,7 @@ public:
|
|||||||
void instances_to_separated_objects(const int obj_idx);
|
void instances_to_separated_objects(const int obj_idx);
|
||||||
void split_instances();
|
void split_instances();
|
||||||
void rename_item();
|
void rename_item();
|
||||||
void fix_through_netfabb();
|
void fix_through_cgal();
|
||||||
void simplify();
|
void simplify();
|
||||||
void smooth_mesh();
|
void smooth_mesh();
|
||||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
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_with_groove ? cut.perform_with_groove(m_groove, m_rotate_matrix) :
|
||||||
cut.perform_with_plane();
|
cut.perform_with_plane();
|
||||||
// fix_non_manifold_edges
|
// fix_non_manifold_edges
|
||||||
#ifdef HAS_WIN10SDK
|
{
|
||||||
if (is_windows10()) {
|
|
||||||
bool is_showed_dialog = false;
|
bool is_showed_dialog = false;
|
||||||
bool user_fix_model = false;
|
bool user_fix_model = false;
|
||||||
for (size_t i = 0; i < new_objects.size(); i++) {
|
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");
|
wxString msg = _L("Repairing model object");
|
||||||
msg += ": " + from_u8(model_name) + "\n";
|
msg += ": " + from_u8(model_name) + "\n";
|
||||||
std::string res;
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, 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
|
// set offset for new_objects
|
||||||
|
|
||||||
// save cut_id to post update synchronization
|
// save cut_id to post update synchronization
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#include "imgui/imgui_internal.h"
|
#include "imgui/imgui_internal.h"
|
||||||
#include "slic3r/GUI/Field.hpp"
|
#include "slic3r/GUI/Field.hpp"
|
||||||
#include "slic3r/GUI/MsgDialog.hpp"
|
#include "slic3r/GUI/MsgDialog.hpp"
|
||||||
#include "FixModelByWin10.hpp"
|
#include "FixModelByCgal.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
@@ -3340,8 +3340,7 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
|||||||
cut.perform_with_plane();
|
cut.perform_with_plane();
|
||||||
|
|
||||||
// fix_non_manifold_edges
|
// fix_non_manifold_edges
|
||||||
#ifdef HAS_WIN10SDK
|
{
|
||||||
if (is_windows10()) {
|
|
||||||
bool is_showed_dialog = false;
|
bool is_showed_dialog = false;
|
||||||
bool user_fix_model = false;
|
bool user_fix_model = false;
|
||||||
for (size_t i = 0; i < new_objects.size(); i++) {
|
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");
|
wxString msg = _L("Repairing model object");
|
||||||
msg += ": " + from_u8(model_name) + "\n";
|
msg += ": " + from_u8(model_name) + "\n";
|
||||||
std::string res;
|
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;
|
return true;
|
||||||
};
|
};
|
||||||
ProgressDialog progress_dlg(_L("Repairing model object"), "", 100, find_toplevel_parent(plater), wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT, 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);
|
check_objects_after_cut(new_objects);
|
||||||
|
|
||||||
// save cut_id to post update synchronization
|
// save cut_id to post update synchronization
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ enum class NotificationType
|
|||||||
SimplifySuggestion,
|
SimplifySuggestion,
|
||||||
// Change of text will change font to similar one on.
|
// Change of text will change font to similar one on.
|
||||||
UnknownFont,
|
UnknownFont,
|
||||||
// information about netfabb is finished repairing model (blocking proccess)
|
// information about cgal is finished repairing model (blocking process)
|
||||||
NetfabbFinished,
|
CgalFinished,
|
||||||
// Short meesage to fill space between start and finish of export
|
// Short meesage to fill space between start and finish of export
|
||||||
ExportOngoing,
|
ExportOngoing,
|
||||||
// Progressbar of download from prusaslicer://url
|
// Progressbar of download from prusaslicer://url
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "../Utils/ASCIIFolding.hpp"
|
#include "../Utils/ASCIIFolding.hpp"
|
||||||
#include "../Utils/PrintHost.hpp"
|
#include "../Utils/PrintHost.hpp"
|
||||||
#include "../Utils/FixModelByWin10.hpp"
|
|
||||||
#include "../Utils/UndoRedo.hpp"
|
#include "../Utils/UndoRedo.hpp"
|
||||||
#include "RemovableDriveManager.hpp"
|
#include "RemovableDriveManager.hpp"
|
||||||
#include "BitmapCache.hpp"
|
#include "BitmapCache.hpp"
|
||||||
|
|||||||
@@ -112,7 +112,6 @@
|
|||||||
#include "ConfigWizard.hpp"
|
#include "ConfigWizard.hpp"
|
||||||
#include "SyncAmsInfoDialog.hpp"
|
#include "SyncAmsInfoDialog.hpp"
|
||||||
#include "../Utils/ASCIIFolding.hpp"
|
#include "../Utils/ASCIIFolding.hpp"
|
||||||
#include "../Utils/FixModelByWin10.hpp"
|
|
||||||
#include "../Utils/UndoRedo.hpp"
|
#include "../Utils/UndoRedo.hpp"
|
||||||
#include "../Utils/PresetUpdater.hpp"
|
#include "../Utils/PresetUpdater.hpp"
|
||||||
#include "../Utils/Process.hpp"
|
#include "../Utils/Process.hpp"
|
||||||
@@ -4731,7 +4730,7 @@ struct Plater::priv
|
|||||||
bool can_split_to_volumes() const;
|
bool can_split_to_volumes() const;
|
||||||
bool can_arrange() const;
|
bool can_arrange() const;
|
||||||
bool can_layers_editing() const;
|
bool can_layers_editing() const;
|
||||||
bool can_fix_through_netfabb() const;
|
bool can_fix_through_cgal() const;
|
||||||
bool can_simplify() const;
|
bool can_simplify() const;
|
||||||
bool can_smooth_mesh() const;
|
bool can_smooth_mesh() const;
|
||||||
bool can_set_instance_to_object() const;
|
bool can_set_instance_to_object() const;
|
||||||
@@ -10240,10 +10239,10 @@ void Plater::priv::on_object_select(SimpleEvent& evt)
|
|||||||
selection_changed();
|
selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
//BBS: repair model through netfabb
|
//BBS: repair model through cgal
|
||||||
void Plater::priv::on_repair_model(wxCommandEvent &event)
|
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)
|
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;
|
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;
|
std::vector<int> obj_idxs, vol_idxs;
|
||||||
sidebar->obj_list()->get_selection_indexes(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.
|
// Fixing always.
|
||||||
return ! obj_idxs.empty() || ! vol_idxs.empty();
|
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.
|
// Fixing only if the model is not manifold.
|
||||||
if (vol_idxs.empty()) {
|
if (vol_idxs.empty()) {
|
||||||
for (auto obj_idx : obj_idxs)
|
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)
|
if (model.objects[obj_idx]->get_repaired_errors_count(vol_idx) > 0)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
#endif // FIX_THROUGH_CGAL_ALWAYS
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::priv::can_simplify() const
|
bool Plater::priv::can_simplify() const
|
||||||
@@ -17976,15 +17975,13 @@ void Plater::show_object_info()
|
|||||||
int non_manifold_edges = 0;
|
int non_manifold_edges = 0;
|
||||||
auto mesh_errors = p->sidebar->obj_list()->get_mesh_errors_info(&info_manifold, &non_manifold_edges);
|
auto mesh_errors = p->sidebar->obj_list()->get_mesh_errors_info(&info_manifold, &non_manifold_edges);
|
||||||
|
|
||||||
#ifndef __WINDOWS__
|
if (non_manifold_edges > 0) {
|
||||||
if (non_manifold_edges > 0) {
|
info_manifold += into_u8("\n" + _L("Tips:") + "\n" + _L("Use \"Fix Model\" to repair the mesh."));
|
||||||
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
|
|
||||||
|
|
||||||
info_manifold = "<Error>" + info_manifold + "</Error>";
|
info_manifold = "<Error>" + info_manifold + "</Error>";
|
||||||
info_text += into_u8(info_manifold);
|
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)
|
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_increase_instances() const { return p->can_increase_instances(); }
|
||||||
bool Plater::can_decrease_instances() const { return p->can_decrease_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_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_simplify() const { return p->can_simplify(); }
|
||||||
bool Plater::can_smooth_mesh() const { return p->can_smooth_mesh(); }
|
bool Plater::can_smooth_mesh() const { return p->can_smooth_mesh(); }
|
||||||
bool Plater::can_split_to_objects() const { return p->can_split_to_objects(); }
|
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_increase_instances() const;
|
||||||
bool can_decrease_instances() const;
|
bool can_decrease_instances() const;
|
||||||
bool can_set_instance_to_object() 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_simplify() const;
|
||||||
bool can_smooth_mesh() const;
|
bool can_smooth_mesh() const;
|
||||||
bool can_split_to_objects() const;
|
bool can_split_to_objects() const;
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
#include "Tab.hpp"
|
#include "Tab.hpp"
|
||||||
#include "ConfigWizard.hpp"
|
#include "ConfigWizard.hpp"
|
||||||
#include "../Utils/ASCIIFolding.hpp"
|
#include "../Utils/ASCIIFolding.hpp"
|
||||||
#include "../Utils/FixModelByWin10.hpp"
|
|
||||||
#include "../Utils/UndoRedo.hpp"
|
#include "../Utils/UndoRedo.hpp"
|
||||||
#include "../Utils/ColorSpaceConvert.hpp"
|
#include "../Utils/ColorSpaceConvert.hpp"
|
||||||
#include "BitmapCache.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