Merge branch 'master' into fs_emboss
# Conflicts: # src/libslic3r/Technologies.hpp # src/slic3r/GUI/GLCanvas3D.cpp # src/slic3r/GUI/GUI_App.cpp
This commit is contained in:
commit
37961c36e8
38 changed files with 6137 additions and 5655 deletions
|
@ -43,6 +43,14 @@ set(SLIC3R_GTK "2" CACHE STRING "GTK version to use with wxWidgets on Linux")
|
|||
|
||||
set(IS_CROSS_COMPILE FALSE)
|
||||
|
||||
if (SLIC3R_STATIC)
|
||||
# Prefer config scripts over find modules. This is helpful when building with
|
||||
# the static dependencies. Many libraries have their own export scripts
|
||||
# while having a Find<PkgName> module in standard cmake installation.
|
||||
# (e.g. CURL)
|
||||
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_FIND_FRAMEWORK LAST)
|
||||
set(CMAKE_FIND_APPBUNDLE LAST)
|
||||
|
@ -438,23 +446,6 @@ else()
|
|||
target_link_libraries(libcurl INTERFACE crypt32)
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL)
|
||||
if (NOT APPLE)
|
||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||
target_compile_definitions(libcurl INTERFACE CURL_STATICLIB)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# As of now, our build system produces a statically linked libcurl,
|
||||
# which links the OpenSSL library dynamically.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||
target_include_directories(libcurl INTERFACE ${OPENSSL_INCLUDE_DIR})
|
||||
target_link_libraries(libcurl INTERFACE ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## OPTIONAL packages
|
||||
|
||||
# Find eigen3 or use bundled version
|
||||
|
@ -472,6 +463,14 @@ include_directories(BEFORE SYSTEM ${EIGEN3_INCLUDE_DIR})
|
|||
|
||||
find_package(EXPAT REQUIRED)
|
||||
|
||||
add_library(libexpat INTERFACE)
|
||||
|
||||
if (TARGET EXPAT::EXPAT )
|
||||
target_link_libraries(libexpat INTERFACE EXPAT::EXPAT)
|
||||
elseif(TARGET expat::expat)
|
||||
target_link_libraries(libexpat INTERFACE expat::expat)
|
||||
endif ()
|
||||
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
set(OpenGL_GL_PREFERENCE "LEGACY")
|
||||
|
|
4
deps/Boost/Boost.cmake
vendored
4
deps/Boost/Boost.cmake
vendored
|
@ -75,7 +75,9 @@ file(TO_NATIVE_PATH ${DESTDIR}/usr/local/ _prefix)
|
|||
set(_boost_flags "")
|
||||
if (UNIX)
|
||||
set(_boost_flags "cflags=-fPIC;cxxflags=-fPIC")
|
||||
elseif(APPLE)
|
||||
endif ()
|
||||
|
||||
if(APPLE)
|
||||
set(_boost_flags
|
||||
"cflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET};"
|
||||
"cxxflags=-fPIC -mmacosx-version-min=${DEP_OSX_TARGET};"
|
||||
|
|
9
deps/CMakeLists.txt
vendored
9
deps/CMakeLists.txt
vendored
|
@ -179,7 +179,12 @@ include(CGAL/CGAL.cmake)
|
|||
include(NLopt/NLopt.cmake)
|
||||
|
||||
include(OpenSSL/OpenSSL.cmake)
|
||||
include(CURL/CURL.cmake)
|
||||
|
||||
set(CURL_PKG "")
|
||||
if (NOT CURL_FOUND)
|
||||
include(CURL/CURL.cmake)
|
||||
set(CURL_PKG dep_CURL)
|
||||
endif ()
|
||||
|
||||
include(JPEG/JPEG.cmake)
|
||||
include(TIFF/TIFF.cmake)
|
||||
|
@ -188,7 +193,7 @@ include(wxWidgets/wxWidgets.cmake)
|
|||
set(_dep_list
|
||||
dep_Boost
|
||||
dep_TBB
|
||||
dep_CURL
|
||||
${CURL_PKG}
|
||||
dep_wxWidgets
|
||||
dep_Cereal
|
||||
dep_NLopt
|
||||
|
|
14
deps/CURL/CURL.cmake
vendored
14
deps/CURL/CURL.cmake
vendored
|
@ -48,11 +48,13 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|||
)
|
||||
endif ()
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
set(_curl_static OFF)
|
||||
else()
|
||||
set(_curl_static ON)
|
||||
endif()
|
||||
set(_patch_command "")
|
||||
if (UNIX AND NOT APPLE)
|
||||
# On non-apple UNIX platforms, finding the location of OpenSSL certificates is necessary at runtime, as there is no standard location usable across platforms.
|
||||
# The OPENSSL_CERT_OVERRIDE flag is understood by PrusaSlicer and will trigger the search of certificates at initial application launch.
|
||||
# Then ask the user for consent about the correctness of the found location.
|
||||
set (_patch_command echo set_target_properties(CURL::libcurl PROPERTIES INTERFACE_COMPILE_DEFINITIONS OPENSSL_CERT_OVERRIDE) >> CMake/curl-config.cmake.in)
|
||||
endif ()
|
||||
|
||||
prusaslicer_add_cmake_project(CURL
|
||||
# GIT_REPOSITORY https://github.com/curl/curl.git
|
||||
|
@ -62,10 +64,10 @@ prusaslicer_add_cmake_project(CURL
|
|||
DEPENDS ${ZLIB_PKG}
|
||||
# PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&
|
||||
# ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/curl-mods.patch
|
||||
PATCH_COMMAND "${_patch_command}"
|
||||
CMAKE_ARGS
|
||||
-DBUILD_TESTING:BOOL=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DCURL_STATICLIB=${_curl_static}
|
||||
${_curl_platform_flags}
|
||||
)
|
||||
|
||||
|
|
5
deps/deps-macos.cmake
vendored
5
deps/deps-macos.cmake
vendored
|
@ -15,6 +15,11 @@ set(DEP_CMAKE_OPTS
|
|||
|
||||
include("deps-unix-common.cmake")
|
||||
|
||||
find_package(CURL QUIET)
|
||||
if (NOT CURL_FOUND)
|
||||
message(WARNING "No CURL dev package found in system, building static library. Mac SDK should include CURL from at least version 10.12. Check your SDK installation.")
|
||||
endif ()
|
||||
|
||||
|
||||
# ExternalProject_Add(dep_boost
|
||||
# EXCLUDE_FROM_ALL 1
|
||||
|
|
|
@ -970,10 +970,7 @@ renamed_from = "Creality ENDER-3 BLTouch"
|
|||
printer_model = ENDER3BLTOUCH
|
||||
|
||||
[printer:Creality Ender-3 Pro]
|
||||
inherits = *common*; *pauseprint*
|
||||
renamed_from = "Creality Ender-3 Pro"
|
||||
bed_shape = 5x0,215x0,215x220,5x220
|
||||
max_print_height = 250
|
||||
inherits = Creality Ender-3; *pauseprint*
|
||||
printer_model = ENDER3PRO
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3PRO\nPRINTER_HAS_BOWDEN
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ if (SLIC3R_GUI)
|
|||
string(REGEX MATCH "wxexpat" WX_EXPAT_BUILTIN ${wxWidgets_LIBRARIES})
|
||||
if (EXPAT_FOUND AND NOT WX_EXPAT_BUILTIN)
|
||||
list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat)
|
||||
list(APPEND wxWidgets_LIBRARIES EXPAT::EXPAT)
|
||||
list(APPEND wxWidgets_LIBRARIES libexpat)
|
||||
endif ()
|
||||
|
||||
# This is an issue in the new wxWidgets cmake build, doesn't deal with librt
|
||||
|
|
|
@ -446,6 +446,57 @@ namespace detail {
|
|||
}
|
||||
}
|
||||
|
||||
// Real-time collision detection, Ericson, Chapter 5
|
||||
template<typename Vector>
|
||||
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
|
||||
{
|
||||
using Scalar = typename Vector::Scalar;
|
||||
// Check if P in vertex region outside A
|
||||
Vector ab = b - a;
|
||||
Vector ac = c - a;
|
||||
Vector ap = p - a;
|
||||
Scalar d1 = ab.dot(ap);
|
||||
Scalar d2 = ac.dot(ap);
|
||||
if (d1 <= 0 && d2 <= 0)
|
||||
return a;
|
||||
// Check if P in vertex region outside B
|
||||
Vector bp = p - b;
|
||||
Scalar d3 = ab.dot(bp);
|
||||
Scalar d4 = ac.dot(bp);
|
||||
if (d3 >= 0 && d4 <= d3)
|
||||
return b;
|
||||
// Check if P in edge region of AB, if so return projection of P onto AB
|
||||
Scalar vc = d1*d4 - d3*d2;
|
||||
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
|
||||
Scalar v = d1 / (d1 - d3);
|
||||
return a + v * ab;
|
||||
}
|
||||
// Check if P in vertex region outside C
|
||||
Vector cp = p - c;
|
||||
Scalar d5 = ab.dot(cp);
|
||||
Scalar d6 = ac.dot(cp);
|
||||
if (d6 >= 0 && d5 <= d6)
|
||||
return c;
|
||||
// Check if P in edge region of AC, if so return projection of P onto AC
|
||||
Scalar vb = d5*d2 - d1*d6;
|
||||
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
|
||||
Scalar w = d2 / (d2 - d6);
|
||||
return a + w * ac;
|
||||
}
|
||||
// Check if P in edge region of BC, if so return projection of P onto BC
|
||||
Scalar va = d3*d6 - d5*d4;
|
||||
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
|
||||
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||
return b + w * (c - b);
|
||||
}
|
||||
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
||||
Scalar denom = Scalar(1.0) / (va + vb + vc);
|
||||
Scalar v = vb * denom;
|
||||
Scalar w = vc * denom;
|
||||
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
|
||||
};
|
||||
|
||||
|
||||
// Nothing to do with COVID-19 social distancing.
|
||||
template<typename AVertexType, typename AIndexedFaceType, typename ATreeType, typename AVectorType>
|
||||
struct IndexedTriangleSetDistancer {
|
||||
|
@ -453,74 +504,36 @@ namespace detail {
|
|||
using IndexedFaceType = AIndexedFaceType;
|
||||
using TreeType = ATreeType;
|
||||
using VectorType = AVectorType;
|
||||
using ScalarType = typename VectorType::Scalar;
|
||||
|
||||
const std::vector<VertexType> &vertices;
|
||||
const std::vector<IndexedFaceType> &faces;
|
||||
const TreeType &tree;
|
||||
|
||||
const VectorType origin;
|
||||
|
||||
inline VectorType closest_point_to_origin(size_t primitive_index,
|
||||
ScalarType& squared_distance){
|
||||
const auto &triangle = this->faces[primitive_index];
|
||||
VectorType closest_point = closest_point_to_triangle<VectorType>(origin,
|
||||
this->vertices[triangle(0)].template cast<ScalarType>(),
|
||||
this->vertices[triangle(1)].template cast<ScalarType>(),
|
||||
this->vertices[triangle(2)].template cast<ScalarType>());
|
||||
squared_distance = (origin - closest_point).squaredNorm();
|
||||
return closest_point;
|
||||
}
|
||||
};
|
||||
|
||||
// Real-time collision detection, Ericson, Chapter 5
|
||||
template<typename Vector>
|
||||
static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c)
|
||||
{
|
||||
using Scalar = typename Vector::Scalar;
|
||||
// Check if P in vertex region outside A
|
||||
Vector ab = b - a;
|
||||
Vector ac = c - a;
|
||||
Vector ap = p - a;
|
||||
Scalar d1 = ab.dot(ap);
|
||||
Scalar d2 = ac.dot(ap);
|
||||
if (d1 <= 0 && d2 <= 0)
|
||||
return a;
|
||||
// Check if P in vertex region outside B
|
||||
Vector bp = p - b;
|
||||
Scalar d3 = ab.dot(bp);
|
||||
Scalar d4 = ac.dot(bp);
|
||||
if (d3 >= 0 && d4 <= d3)
|
||||
return b;
|
||||
// Check if P in edge region of AB, if so return projection of P onto AB
|
||||
Scalar vc = d1*d4 - d3*d2;
|
||||
if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) {
|
||||
Scalar v = d1 / (d1 - d3);
|
||||
return a + v * ab;
|
||||
}
|
||||
// Check if P in vertex region outside C
|
||||
Vector cp = p - c;
|
||||
Scalar d5 = ab.dot(cp);
|
||||
Scalar d6 = ac.dot(cp);
|
||||
if (d6 >= 0 && d5 <= d6)
|
||||
return c;
|
||||
// Check if P in edge region of AC, if so return projection of P onto AC
|
||||
Scalar vb = d5*d2 - d1*d6;
|
||||
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
|
||||
Scalar w = d2 / (d2 - d6);
|
||||
return a + w * ac;
|
||||
}
|
||||
// Check if P in edge region of BC, if so return projection of P onto BC
|
||||
Scalar va = d3*d6 - d5*d4;
|
||||
if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
|
||||
Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||
return b + w * (c - b);
|
||||
}
|
||||
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
||||
Scalar denom = Scalar(1.0) / (va + vb + vc);
|
||||
Scalar v = vb * denom;
|
||||
Scalar w = vc * denom;
|
||||
return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
|
||||
};
|
||||
|
||||
template<typename IndexedTriangleSetDistancerType, typename Scalar>
|
||||
static inline Scalar squared_distance_to_indexed_triangle_set_recursive(
|
||||
IndexedTriangleSetDistancerType &distancer,
|
||||
template<typename IndexedPrimitivesDistancerType, typename Scalar>
|
||||
static inline Scalar squared_distance_to_indexed_primitives_recursive(
|
||||
IndexedPrimitivesDistancerType &distancer,
|
||||
size_t node_idx,
|
||||
Scalar low_sqr_d,
|
||||
Scalar up_sqr_d,
|
||||
size_t &i,
|
||||
Eigen::PlainObjectBase<typename IndexedTriangleSetDistancerType::VectorType> &c)
|
||||
Eigen::PlainObjectBase<typename IndexedPrimitivesDistancerType::VectorType> &c)
|
||||
{
|
||||
using Vector = typename IndexedTriangleSetDistancerType::VectorType;
|
||||
using Vector = typename IndexedPrimitivesDistancerType::VectorType;
|
||||
|
||||
if (low_sqr_d > up_sqr_d)
|
||||
return low_sqr_d;
|
||||
|
@ -538,13 +551,9 @@ namespace detail {
|
|||
assert(node.is_valid());
|
||||
if (node.is_leaf())
|
||||
{
|
||||
const auto &triangle = distancer.faces[node.idx];
|
||||
Vector c_candidate = closest_point_to_triangle<Vector>(
|
||||
distancer.origin,
|
||||
distancer.vertices[triangle(0)].template cast<Scalar>(),
|
||||
distancer.vertices[triangle(1)].template cast<Scalar>(),
|
||||
distancer.vertices[triangle(2)].template cast<Scalar>());
|
||||
set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate);
|
||||
Scalar sqr_dist;
|
||||
Vector c_candidate = distancer.closest_point_to_origin(node.idx, sqr_dist);
|
||||
set_min(sqr_dist, node.idx, c_candidate);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -561,7 +570,7 @@ namespace detail {
|
|||
{
|
||||
size_t i_left;
|
||||
Vector c_left = c;
|
||||
Scalar sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
|
||||
Scalar sqr_d_left = squared_distance_to_indexed_primitives_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left);
|
||||
set_min(sqr_d_left, i_left, c_left);
|
||||
looked_left = true;
|
||||
};
|
||||
|
@ -569,13 +578,13 @@ namespace detail {
|
|||
{
|
||||
size_t i_right;
|
||||
Vector c_right = c;
|
||||
Scalar sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
|
||||
Scalar sqr_d_right = squared_distance_to_indexed_primitives_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right);
|
||||
set_min(sqr_d_right, i_right, c_right);
|
||||
looked_right = true;
|
||||
};
|
||||
|
||||
// must look left or right if in box
|
||||
using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar;
|
||||
using BBoxScalar = typename IndexedPrimitivesDistancerType::TreeType::BoundingBox::Scalar;
|
||||
if (node_left.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
|
||||
look_left();
|
||||
if (node_right.bbox.contains(distancer.origin.template cast<BBoxScalar>()))
|
||||
|
@ -747,7 +756,7 @@ inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set(
|
|||
auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType>
|
||||
{ vertices, faces, tree, point };
|
||||
return tree.empty() ? Scalar(-1) :
|
||||
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
|
||||
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
// Decides if exists some triangle in defined radius on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
|
||||
|
@ -779,7 +788,7 @@ inline bool is_any_triangle_in_radius(
|
|||
return false;
|
||||
}
|
||||
|
||||
detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
|
||||
detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0), max_distance_squared, hit_idx, hit_point);
|
||||
|
||||
return hit_point.allFinite();
|
||||
}
|
||||
|
|
112
src/libslic3r/AABBTreeLines.hpp
Normal file
112
src/libslic3r/AABBTreeLines.hpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_
|
||||
#define SRC_LIBSLIC3R_AABBTREELINES_HPP_
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "libslic3r/AABBTreeIndirect.hpp"
|
||||
#include "libslic3r/Line.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace AABBTreeLines {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename ALineType, typename ATreeType, typename AVectorType>
|
||||
struct IndexedLinesDistancer {
|
||||
using LineType = ALineType;
|
||||
using TreeType = ATreeType;
|
||||
using VectorType = AVectorType;
|
||||
using ScalarType = typename VectorType::Scalar;
|
||||
|
||||
const std::vector<LineType> &lines;
|
||||
const TreeType &tree;
|
||||
|
||||
const VectorType origin;
|
||||
|
||||
inline VectorType closest_point_to_origin(size_t primitive_index,
|
||||
ScalarType &squared_distance) {
|
||||
VectorType nearest_point;
|
||||
const LineType &line = lines[primitive_index];
|
||||
squared_distance = line_alg::distance_to_squared(line, origin, &nearest_point);
|
||||
return nearest_point;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Build a balanced AABB Tree over a vector of float lines, balancing the tree
|
||||
// on centroids of the lines.
|
||||
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
||||
// during tree traversal.
|
||||
template<typename LineType>
|
||||
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
|
||||
const std::vector<LineType> &lines,
|
||||
//FIXME do we want to apply an epsilon?
|
||||
const float eps = 0)
|
||||
{
|
||||
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
||||
// using CoordType = typename TreeType::CoordType;
|
||||
using VectorType = typename TreeType::VectorType;
|
||||
using BoundingBox = typename TreeType::BoundingBox;
|
||||
|
||||
struct InputType {
|
||||
size_t idx() const {
|
||||
return m_idx;
|
||||
}
|
||||
const BoundingBox& bbox() const {
|
||||
return m_bbox;
|
||||
}
|
||||
const VectorType& centroid() const {
|
||||
return m_centroid;
|
||||
}
|
||||
|
||||
size_t m_idx;
|
||||
BoundingBox m_bbox;
|
||||
VectorType m_centroid;
|
||||
};
|
||||
|
||||
std::vector<InputType> input;
|
||||
input.reserve(lines.size());
|
||||
const VectorType veps(eps, eps);
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
const LineType &line = lines[i];
|
||||
InputType n;
|
||||
n.m_idx = i;
|
||||
n.m_centroid = (line.a + line.b) * 0.5;
|
||||
n.m_bbox = BoundingBox(line.a, line.a);
|
||||
n.m_bbox.extend(line.b);
|
||||
n.m_bbox.min() -= veps;
|
||||
n.m_bbox.max() += veps;
|
||||
input.emplace_back(n);
|
||||
}
|
||||
|
||||
TreeType out;
|
||||
out.build(std::move(input));
|
||||
return out;
|
||||
}
|
||||
|
||||
// Finding a closest line, its closest point and squared distance to the closest point
|
||||
// Returns squared distance to the closest point or -1 if the input is empty.
|
||||
template<typename LineType, typename TreeType, typename VectorType>
|
||||
inline typename VectorType::Scalar squared_distance_to_indexed_lines(
|
||||
const std::vector<LineType> &lines,
|
||||
const TreeType &tree,
|
||||
const VectorType &point,
|
||||
size_t &hit_idx_out,
|
||||
Eigen::PlainObjectBase<VectorType> &hit_point_out)
|
||||
{
|
||||
using Scalar = typename VectorType::Scalar;
|
||||
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>
|
||||
{ lines, tree, point };
|
||||
return tree.empty() ?
|
||||
Scalar(-1) :
|
||||
AABBTreeIndirect::detail::squared_distance_to_indexed_primitives_recursive(distancer, size_t(0), Scalar(0),
|
||||
std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SRC_LIBSLIC3R_AABBTREELINES_HPP_ */
|
|
@ -381,7 +381,7 @@ target_link_libraries(libslic3r
|
|||
boost_libs
|
||||
clipper
|
||||
nowide
|
||||
EXPAT::EXPAT
|
||||
libexpat
|
||||
glu-libtess
|
||||
qhull
|
||||
semver
|
||||
|
|
|
@ -21,9 +21,9 @@ void GeneratorDeleter::operator()(Generator *p) {
|
|||
delete p;
|
||||
}
|
||||
|
||||
GeneratorPtr build_generator(const PrintObject &print_object)
|
||||
GeneratorPtr build_generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
return GeneratorPtr(new Generator(print_object));
|
||||
return GeneratorPtr(new Generator(print_object, throw_on_cancel_callback));
|
||||
}
|
||||
|
||||
} // namespace Slic3r::FillAdaptive
|
||||
|
|
|
@ -14,7 +14,7 @@ class Generator;
|
|||
struct GeneratorDeleter { void operator()(Generator *p); };
|
||||
using GeneratorPtr = std::unique_ptr<Generator, GeneratorDeleter>;
|
||||
|
||||
GeneratorPtr build_generator(const PrintObject &print_object);
|
||||
GeneratorPtr build_generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
class Filler : public Slic3r::Fill
|
||||
{
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
namespace Slic3r::FillLightning {
|
||||
|
||||
Generator::Generator(const PrintObject &print_object)
|
||||
Generator::Generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
const PrintConfig &print_config = print_object.print()->config();
|
||||
const PrintObjectConfig &object_config = print_object.config();
|
||||
|
@ -47,11 +47,11 @@ Generator::Generator(const PrintObject &print_object)
|
|||
m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle));
|
||||
m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle));
|
||||
|
||||
generateInitialInternalOverhangs(print_object);
|
||||
generateTrees(print_object);
|
||||
generateInitialInternalOverhangs(print_object, throw_on_cancel_callback);
|
||||
generateTrees(print_object, throw_on_cancel_callback);
|
||||
}
|
||||
|
||||
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object)
|
||||
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
m_overhang_per_layer.resize(print_object.layers().size());
|
||||
// FIXME: It can be adjusted to improve bonding between infill and perimeters.
|
||||
|
@ -59,7 +59,8 @@ void Generator::generateInitialInternalOverhangs(const PrintObject &print_object
|
|||
|
||||
Polygons infill_area_above;
|
||||
//Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging.
|
||||
for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; layer_nr--) {
|
||||
for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; --layer_nr) {
|
||||
throw_on_cancel_callback();
|
||||
Polygons infill_area_here;
|
||||
for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions())
|
||||
for (const Surface& surface : layerm->fill_surfaces.surfaces)
|
||||
|
@ -80,7 +81,7 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const
|
|||
return m_lightning_layers[layer_id];
|
||||
}
|
||||
|
||||
void Generator::generateTrees(const PrintObject &print_object)
|
||||
void Generator::generateTrees(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
|
||||
{
|
||||
m_lightning_layers.resize(print_object.layers().size());
|
||||
// FIXME: It can be adjusted to improve bonding between infill and perimeters.
|
||||
|
@ -89,11 +90,13 @@ void Generator::generateTrees(const PrintObject &print_object)
|
|||
std::vector<Polygons> infill_outlines(print_object.layers().size(), Polygons());
|
||||
|
||||
// For-each layer from top to bottom:
|
||||
for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--)
|
||||
for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) {
|
||||
throw_on_cancel_callback();
|
||||
for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions())
|
||||
for (const Surface &surface : layerm->fill_surfaces.surfaces)
|
||||
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
|
||||
append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset));
|
||||
}
|
||||
|
||||
// For various operations its beneficial to quickly locate nearby features on the polygon:
|
||||
const size_t top_layer_id = print_object.layers().size() - 1;
|
||||
|
@ -102,6 +105,7 @@ void Generator::generateTrees(const PrintObject &print_object)
|
|||
|
||||
// For-each layer from top to bottom:
|
||||
for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) {
|
||||
throw_on_cancel_callback();
|
||||
Layer ¤t_lightning_layer = m_lightning_layers[layer_id];
|
||||
const Polygons ¤t_outlines = infill_outlines[layer_id];
|
||||
const BoundingBox ¤t_outlines_bbox = get_extents(current_outlines);
|
||||
|
@ -109,7 +113,7 @@ void Generator::generateTrees(const PrintObject &print_object)
|
|||
// register all trees propagated from the previous layer as to-be-reconnected
|
||||
std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
|
||||
|
||||
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
|
||||
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius, throw_on_cancel_callback);
|
||||
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
|
||||
|
||||
// Initialize trees for next lower layer from the current one.
|
||||
|
|
|
@ -43,9 +43,8 @@ public:
|
|||
* This generator will pre-compute things in preparation of generating
|
||||
* Lightning Infill for the infill areas in that mesh. The infill areas must
|
||||
* already be calculated at this point.
|
||||
* \param mesh The mesh to generate infill for.
|
||||
*/
|
||||
explicit Generator(const PrintObject &print_object);
|
||||
explicit Generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
/*!
|
||||
* Get a tree of paths generated for a certain layer of the mesh.
|
||||
|
@ -69,12 +68,12 @@ protected:
|
|||
* only when support is generated. For this pattern, we also need to
|
||||
* generate overhang areas for the inside of the model.
|
||||
*/
|
||||
void generateInitialInternalOverhangs(const PrintObject &print_object);
|
||||
void generateInitialInternalOverhangs(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
/*!
|
||||
* Calculate the tree structure of all layers.
|
||||
*/
|
||||
void generateTrees(const PrintObject &print_object);
|
||||
void generateTrees(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
|
||||
|
||||
float m_infill_extrusion_width;
|
||||
|
||||
|
|
|
@ -44,10 +44,12 @@ void Layer::generateNewTrees
|
|||
const BoundingBox& current_outlines_bbox,
|
||||
const EdgeGrid::Grid& outlines_locator,
|
||||
const coord_t supporting_radius,
|
||||
const coord_t wall_supporting_radius
|
||||
const coord_t wall_supporting_radius,
|
||||
const std::function<void()> &throw_on_cancel_callback
|
||||
)
|
||||
{
|
||||
DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang);
|
||||
throw_on_cancel_callback();
|
||||
|
||||
SparseNodeGrid tree_node_locator;
|
||||
fillLocator(tree_node_locator, current_outlines_bbox);
|
||||
|
@ -56,6 +58,7 @@ void Layer::generateNewTrees
|
|||
// Determine next point from tree/outline areas via distance-field
|
||||
Point unsupported_location;
|
||||
while (distance_field.tryGetNextPoint(&unsupported_location)) {
|
||||
throw_on_cancel_callback();
|
||||
GroundingLocation grounding_loc = getBestGroundingLocation(
|
||||
unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
|
||||
|
||||
|
@ -126,9 +129,10 @@ GroundingLocation Layer::getBestGroundingLocation
|
|||
if (contour.size() > 2) {
|
||||
Point prev = contour.points.back();
|
||||
for (const Point &p2 : contour.points) {
|
||||
if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) {
|
||||
Point closest_point;
|
||||
if (double d = line_alg::distance_to_squared(Line{prev, p2}, unsupported_location, &closest_point); d < d2) {
|
||||
d2 = d;
|
||||
node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast<coord_t>();
|
||||
node_location = closest_point;
|
||||
}
|
||||
prev = p2;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
const BoundingBox& current_outlines_bbox,
|
||||
const EdgeGrid::Grid& outline_locator,
|
||||
coord_t supporting_radius,
|
||||
coord_t wall_supporting_radius
|
||||
coord_t wall_supporting_radius,
|
||||
const std::function<void()> &throw_on_cancel_callback
|
||||
);
|
||||
|
||||
/*! Determine & connect to connection point in tree/outline.
|
||||
|
|
|
@ -180,7 +180,11 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG
|
|||
} visitor { outline_locator, a.cast<double>(), b.cast<double>() };
|
||||
|
||||
outline_locator.visit_cells_intersecting_line(a, b, visitor);
|
||||
return visitor.d2min < double(within_max_dist) * double(within_max_dist);
|
||||
if (visitor.d2min < double(within_max_dist) * double(within_max_dist)) {
|
||||
result = Point(visitor.intersection_pt);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts)
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <cstdlib> // adds size_t (without std::)
|
||||
|
||||
template<typename T, typename IndexSetter, typename LessPredicate, const bool ResetIndexWhenRemoved = false>
|
||||
class MutablePriorityQueue
|
||||
|
@ -21,7 +24,7 @@ public:
|
|||
MutablePriorityQueue& operator=(MutablePriorityQueue &&) = default;
|
||||
|
||||
// This class modifies the outside data through the m_index_setter
|
||||
// and thus it should not be copied. The semantics are similar to std::unique_ptr
|
||||
// and thus it should not be copied. The semantics is similar to std::unique_ptr
|
||||
MutablePriorityQueue(const MutablePriorityQueue &) = delete;
|
||||
MutablePriorityQueue& operator=(const MutablePriorityQueue &) = delete;
|
||||
|
||||
|
@ -38,6 +41,7 @@ public:
|
|||
bool empty() const { return m_heap.empty(); }
|
||||
T& operator[](std::size_t idx) noexcept { return m_heap[idx]; }
|
||||
const T& operator[](std::size_t idx) const noexcept { return m_heap[idx]; }
|
||||
static constexpr size_t invalid_id() { return std::numeric_limits<size_t>::max(); }
|
||||
|
||||
using iterator = typename std::vector<T>::iterator;
|
||||
using const_iterator = typename std::vector<T>::const_iterator;
|
||||
|
@ -66,14 +70,10 @@ MutablePriorityQueue<T, IndexSetter, LessPredicate, ResetIndexWhenRemoved> make_
|
|||
template<class T, class LessPredicate, class IndexSetter, const bool ResetIndexWhenRemoved>
|
||||
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter, ResetIndexWhenRemoved>::clear()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
for (size_t idx = 0; idx < m_heap.size(); ++ idx)
|
||||
// Mark as removed from the queue.
|
||||
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap[idx], this->invalid_id());
|
||||
}
|
||||
m_heap.clear();
|
||||
}
|
||||
|
@ -100,13 +100,9 @@ template<class T, class LessPredicate, class IndexSetter, const bool ResetIndexW
|
|||
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter, ResetIndexWhenRemoved>::pop()
|
||||
{
|
||||
assert(! m_heap.empty());
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
// Mark as removed from the queue.
|
||||
m_index_setter(m_heap.front(), std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap.front(), this->invalid_id());
|
||||
}
|
||||
if (m_heap.size() > 1) {
|
||||
m_heap.front() = m_heap.back();
|
||||
|
@ -121,13 +117,10 @@ template<class T, class LessPredicate, class IndexSetter, const bool ResetIndexW
|
|||
inline void MutablePriorityQueue<T, LessPredicate, IndexSetter, ResetIndexWhenRemoved>::remove(size_t idx)
|
||||
{
|
||||
assert(idx < m_heap.size());
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
// Mark as removed from the queue.
|
||||
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap[idx], this->invalid_id());
|
||||
}
|
||||
if (idx + 1 == m_heap.size()) {
|
||||
m_heap.pop_back();
|
||||
|
@ -274,6 +267,14 @@ public:
|
|||
{}
|
||||
~MutableSkipHeapPriorityQueue() { clear(); }
|
||||
|
||||
MutableSkipHeapPriorityQueue(MutableSkipHeapPriorityQueue &&) = default;
|
||||
MutableSkipHeapPriorityQueue &operator=(MutableSkipHeapPriorityQueue &&) = default;
|
||||
|
||||
// This class modifies the outside data through the m_index_setter
|
||||
// and thus it should not be copied. The semantics is similar to std::unique_ptr
|
||||
MutableSkipHeapPriorityQueue(const MutableSkipHeapPriorityQueue &) = delete;
|
||||
MutableSkipHeapPriorityQueue &operator=(const MutableSkipHeapPriorityQueue &) = delete;
|
||||
|
||||
void clear();
|
||||
// Reserve one unused element per miniheap.
|
||||
void reserve(size_t cnt) { m_heap.reserve(cnt + ((cnt + (address::block_size - 1)) / (address::block_size - 1))); }
|
||||
|
@ -285,9 +286,12 @@ public:
|
|||
void update(size_t idx) { assert(! address::is_padding(idx)); T item = m_heap[idx]; remove(idx); push(item); }
|
||||
// There is one padding element storead at each miniheap, thus lower the number of elements by the number of miniheaps.
|
||||
size_t size() const noexcept { return m_heap.size() - (m_heap.size() + address::block_size - 1) / address::block_size; }
|
||||
// Number of heap elements including padding. heap_size() >= size().
|
||||
size_t heap_size() const noexcept { return m_heap.size(); }
|
||||
bool empty() const { return m_heap.empty(); }
|
||||
T& operator[](std::size_t idx) noexcept { assert(! address::is_padding(idx)); return m_heap[idx]; }
|
||||
const T& operator[](std::size_t idx) const noexcept { assert(! address::is_padding(idx)); return m_heap[idx]; }
|
||||
static constexpr size_t invalid_id() { return std::numeric_limits<size_t>::max(); }
|
||||
|
||||
protected:
|
||||
void update_heap_up(size_t top, size_t bottom);
|
||||
|
@ -317,15 +321,11 @@ MutableSkipHeapPriorityQueue<T, IndexSetter, LessPredicate, BlockSize, ResetInde
|
|||
template<class T, class LessPredicate, class IndexSetter, std::size_t blocking, const bool ResetIndexWhenRemoved>
|
||||
inline void MutableSkipHeapPriorityQueue<T, LessPredicate, IndexSetter, blocking, ResetIndexWhenRemoved>::clear()
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
for (size_t idx = 0; idx < m_heap.size(); ++ idx)
|
||||
// Mark as removed from the queue.
|
||||
if (! address::is_padding(idx))
|
||||
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap[idx], this->invalid_id());
|
||||
}
|
||||
m_heap.clear();
|
||||
}
|
||||
|
@ -356,13 +356,9 @@ template<class T, class LessPredicate, class IndexSetter, std::size_t blocking,
|
|||
inline void MutableSkipHeapPriorityQueue<T, LessPredicate, IndexSetter, blocking, ResetIndexWhenRemoved>::pop()
|
||||
{
|
||||
assert(! m_heap.empty());
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
// Mark as removed from the queue.
|
||||
m_index_setter(m_heap[1], std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap[1], this->invalid_id());
|
||||
}
|
||||
// Zero'th element is padding, thus non-empty queue must have at least two elements.
|
||||
if (m_heap.size() > 2) {
|
||||
|
@ -379,13 +375,9 @@ inline void MutableSkipHeapPriorityQueue<T, LessPredicate, IndexSetter, blocking
|
|||
{
|
||||
assert(idx < m_heap.size());
|
||||
assert(! address::is_padding(idx));
|
||||
#ifdef NDEBUG
|
||||
// Only mark as removed from the queue in release mode, if configured so.
|
||||
if (ResetIndexWhenRemoved)
|
||||
#endif /* NDEBUG */
|
||||
{
|
||||
if constexpr (ResetIndexWhenRemoved) {
|
||||
// Mark as removed from the queue.
|
||||
m_index_setter(m_heap[idx], std::numeric_limits<size_t>::max());
|
||||
m_index_setter(m_heap[idx], this->invalid_id());
|
||||
}
|
||||
if (idx + 1 == m_heap.size()) {
|
||||
this->pop_back();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "BuildVolume.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
|
@ -387,7 +388,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
|
||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - BuildVolume::BedEpsilon)),
|
||||
jtRound, scale_(0.1)).front());
|
||||
}
|
||||
// Make a copy, so it may be rotated for instances.
|
||||
|
|
|
@ -464,7 +464,7 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data()
|
|||
break;
|
||||
}
|
||||
|
||||
return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr();
|
||||
return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this), [this]() -> void { this->throw_if_canceled(); }) : FillLightning::GeneratorPtr();
|
||||
}
|
||||
|
||||
void PrintObject::clear_layers()
|
||||
|
|
|
@ -195,7 +195,15 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
|
|||
}
|
||||
|
||||
// Initialize a heap of end points sorted by the lowest distance to the next valid point of a path.
|
||||
auto queue = make_mutable_priority_queue<EndPoint*, false>(
|
||||
auto queue = make_mutable_priority_queue<EndPoint*,
|
||||
#ifndef NDEBUG
|
||||
// In debug mode, reset indices when removing an item from the queue for debugging purposes.
|
||||
true
|
||||
#else // NDEBUG
|
||||
// In release mode, don't reset indices when removing an item from the queue.
|
||||
false
|
||||
#endif // NDEBUG
|
||||
>(
|
||||
[](EndPoint *ep, size_t idx){ ep->heap_idx = idx; },
|
||||
[](EndPoint *l, EndPoint *r){ return l->distance_out < r->distance_out; });
|
||||
queue.reserve(end_points.size() * 2 - 1);
|
||||
|
@ -213,7 +221,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
|
|||
assert(ep.chain_id == 0);
|
||||
} else {
|
||||
// End point is NOT on the heap, therefore it is part of the output path.
|
||||
assert(ep.heap_idx == std::numeric_limits<size_t>::max());
|
||||
assert(ep.heap_idx == queue.invalid_id());
|
||||
assert(ep.chain_id != 0);
|
||||
if (&ep == first_point) {
|
||||
assert(ep.edge_out == nullptr);
|
||||
|
@ -222,7 +230,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy_constrained_reversals
|
|||
// Detect loops.
|
||||
for (EndPoint *pt = &ep; pt != nullptr;) {
|
||||
// Out of queue. It is a final point.
|
||||
assert(pt->heap_idx == std::numeric_limits<size_t>::max());
|
||||
assert(pt->heap_idx == queue.invalid_id());
|
||||
EndPoint *pt_other = &end_points[(pt - &end_points.front()) ^ 1];
|
||||
if (pt_other->heap_idx < queue.size())
|
||||
// The other side of this segment is undecided yet.
|
||||
|
|
|
@ -91,6 +91,8 @@
|
|||
#define ENABLE_USED_FILAMENT_POST_PROCESS (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable gizmo grabbers to share common models
|
||||
#define ENABLE_GIZMO_GRABBER_REFACTOR (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Disable association to 3mf and stl files if the application is run on Windows 8 or later
|
||||
#define ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER (1 && ENABLE_2_5_0_ALPHA1)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -312,10 +312,6 @@ if (SLIC3R_STATIC)
|
|||
target_compile_definitions(libslic3r_gui PUBLIC -DwxDEBUG_LEVEL=0)
|
||||
endif()
|
||||
|
||||
if (SLIC3R_STATIC AND NOT SLIC3R_STATIC_EXCLUDE_CURL AND UNIX AND NOT APPLE)
|
||||
target_compile_definitions(libslic3r_gui PRIVATE OPENSSL_CERT_OVERRIDE)
|
||||
endif ()
|
||||
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -386,14 +386,14 @@ void GCodeViewer::SequentialView::Marker::render()
|
|||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, std::vector<size_t> &&lines_ends)
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t>& lines_ends)
|
||||
{
|
||||
assert(! m_file.is_open());
|
||||
if (m_file.is_open())
|
||||
return;
|
||||
|
||||
m_filename = filename;
|
||||
m_lines_ends = std::move(lines_ends);
|
||||
m_lines_ends = lines_ends;
|
||||
|
||||
m_selected_line_id = 0;
|
||||
m_last_lines_size = 0;
|
||||
|
@ -771,9 +771,7 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr
|
|||
// release gpu memory, if used
|
||||
reset();
|
||||
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename,
|
||||
// Stealing out lines_ends should be safe because this gcode_result is processed only once (see the 1st if in this function).
|
||||
std::move(const_cast<std::vector<size_t>&>(gcode_result.lines_ends)));
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
|
||||
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7355,12 +7355,13 @@ bool GLCanvas3D::_is_any_volume_outside() const
|
|||
void GLCanvas3D::_update_selection_from_hover()
|
||||
{
|
||||
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
|
||||
bool selection_changed = false;
|
||||
|
||||
if (m_hover_volume_idxs.empty()) {
|
||||
if (!ctrl_pressed && m_rectangle_selection.get_state() == GLSelectionRectangle::EState::Select)
|
||||
if (!ctrl_pressed && m_rectangle_selection.get_state() == GLSelectionRectangle::EState::Select) {
|
||||
selection_changed = ! m_selection.is_empty();
|
||||
m_selection.remove_all();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GLSelectionRectangle::EState state = m_rectangle_selection.get_state();
|
||||
|
@ -7373,7 +7374,6 @@ void GLCanvas3D::_update_selection_from_hover()
|
|||
}
|
||||
}
|
||||
|
||||
bool selection_changed = false;
|
||||
#if ENABLE_NEW_RECTANGLE_SELECTION
|
||||
if (!m_rectangle_selection.is_empty()) {
|
||||
#endif // ENABLE_NEW_RECTANGLE_SELECTION
|
||||
|
|
|
@ -1204,10 +1204,17 @@ bool GUI_App::on_init_inner()
|
|||
|
||||
if (is_editor()) {
|
||||
#ifdef __WXMSW__
|
||||
if (app_config->get("associate_3mf") == "1")
|
||||
associate_3mf_files();
|
||||
if (app_config->get("associate_stl") == "1")
|
||||
associate_stl_files();
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
// file association is not possible anymore starting with Win 8
|
||||
if (wxPlatformInfo::Get().GetOSMajorVersion() < 8) {
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
if (app_config->get("associate_3mf") == "1")
|
||||
associate_3mf_files();
|
||||
if (app_config->get("associate_stl") == "1")
|
||||
associate_stl_files();
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
}
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
#endif // __WXMSW__
|
||||
|
||||
preset_updater = new PresetUpdater();
|
||||
|
@ -1245,8 +1252,15 @@ bool GUI_App::on_init_inner()
|
|||
}
|
||||
else {
|
||||
#ifdef __WXMSW__
|
||||
if (app_config->get("associate_gcode") == "1")
|
||||
associate_gcode_files();
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
// file association is not possible anymore starting with Win 8
|
||||
if (wxPlatformInfo::Get().GetOSMajorVersion() < 8) {
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
if (app_config->get("associate_gcode") == "1")
|
||||
associate_gcode_files();
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
}
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
#endif // __WXMSW__
|
||||
}
|
||||
|
||||
|
@ -2419,16 +2433,23 @@ void GUI_App::open_preferences(const std::string& highlight_option /*= std::stri
|
|||
this->plater_->refresh_print();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (is_editor()) {
|
||||
if (app_config->get("associate_3mf") == "1")
|
||||
associate_3mf_files();
|
||||
if (app_config->get("associate_stl") == "1")
|
||||
associate_stl_files();
|
||||
}
|
||||
else {
|
||||
if (app_config->get("associate_gcode") == "1")
|
||||
associate_gcode_files();
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
// file association is not possible anymore starting with Win 8
|
||||
if (wxPlatformInfo::Get().GetOSMajorVersion() < 8) {
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
if (is_editor()) {
|
||||
if (app_config->get("associate_3mf") == "1")
|
||||
associate_3mf_files();
|
||||
if (app_config->get("associate_stl") == "1")
|
||||
associate_stl_files();
|
||||
}
|
||||
else {
|
||||
if (app_config->get("associate_gcode") == "1")
|
||||
associate_gcode_files();
|
||||
}
|
||||
#if ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
}
|
||||
#endif // ENABLE_REMOVE_ASSOCIATION_TO_FILE_FOR_WINDOWS_8_AND_LATER
|
||||
#endif // _WIN32
|
||||
|
||||
if (mainframe->preferences_dialog->settings_layout_changed()) {
|
||||
|
|
|
@ -145,7 +145,7 @@ void GLGizmoSimplify::add_simplify_suggestion_notification(
|
|||
for (size_t object_id : big_ids) {
|
||||
std::string t = GUI::format(_L(
|
||||
"Processing model '%1%' with more than 1M triangles "
|
||||
"could be slow. It is highly recommend to reduce "
|
||||
"could be slow. It is highly recommended to reduce "
|
||||
"amount of triangles."), objects[object_id]->name);
|
||||
std::string hypertext = _u8L("Simplify model");
|
||||
|
||||
|
|
|
@ -101,16 +101,18 @@ void RotoptimizeJob::finalize(bool canceled, std::exception_ptr &eptr)
|
|||
|
||||
auto trmatrix = oi->get_transformation().get_matrix();
|
||||
Polygon trchull = o->convex_hull_2d(trmatrix);
|
||||
|
||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||
double phi = rotbb.angle_to_X();
|
||||
|
||||
// The box should be landscape
|
||||
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
||||
|
||||
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
||||
|
||||
oi->set_rotation(rt);
|
||||
|
||||
if (!trchull.empty()) {
|
||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||
double phi = rotbb.angle_to_X();
|
||||
|
||||
// The box should be landscape
|
||||
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
||||
|
||||
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
||||
|
||||
oi->set_rotation(rt);
|
||||
}
|
||||
}
|
||||
|
||||
// Correct the z offset of the object which was corrupted be
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -141,7 +141,7 @@ static bool check_internet_connection_win()
|
|||
{
|
||||
bool internet = true; // return true if COM object creation fails.
|
||||
|
||||
if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) == S_OK) {
|
||||
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
|
||||
{
|
||||
CComPtr<INetworkListManager> pNLM;
|
||||
if (pNLM.CoCreateInstance(CLSID_NetworkListManager) == S_OK) {
|
||||
|
|
|
@ -1,449 +1,449 @@
|
|||
#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 {
|
||||
|
||||
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("fix_model_by_win10_sdk called on non Windows 10 system");
|
||||
|
||||
if (! winrt_load_runtime_object_library())
|
||||
throw Slic3r::RuntimeError("Failed to initialize the WinRT library.");
|
||||
|
||||
HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
|
||||
{
|
||||
on_progress(L("Exporting source model"), 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 the input model."));
|
||||
|
||||
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 model by the Netfabb 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("Mesh repair failed."));
|
||||
repairAsync->GetResults();
|
||||
|
||||
on_progress(L("Loading repaired model"), 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(L("Saving mesh into the 3MF container failed."));
|
||||
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(L("Saving mesh into the 3MF container failed."));
|
||||
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");
|
||||
try {
|
||||
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(L("Saving mesh into the 3MF container failed."));
|
||||
hr = buffer->get_Length(&length);
|
||||
if (length == 0)
|
||||
break;
|
||||
fwrite(buffer_ptr, length, 1, fout);
|
||||
}
|
||||
} catch (...) {
|
||||
fclose(fout);
|
||||
throw;
|
||||
}
|
||||
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, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
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 = [&mutex, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
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 source model"), 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("Export of a temporary 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 model"), 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 of the repaired 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]->set_new_unique_id();
|
||||
}
|
||||
model_object.invalidate_bounding_box();
|
||||
-- ivolume;
|
||||
on_progress(L("Model repair finished"), 100);
|
||||
success = true;
|
||||
finished = true;
|
||||
} catch (RepairCanceledException & /* ex */) {
|
||||
canceled = true;
|
||||
finished = true;
|
||||
on_progress(L("Model repair canceled"), 100);
|
||||
} catch (std::exception &ex) {
|
||||
success = false;
|
||||
finished = true;
|
||||
on_progress(ex.what(), 100);
|
||||
}
|
||||
});
|
||||
while (! finished) {
|
||||
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 */
|
||||
#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 {
|
||||
|
||||
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("fix_model_by_win10_sdk called on non Windows 10 system");
|
||||
|
||||
if (! winrt_load_runtime_object_library())
|
||||
throw Slic3r::RuntimeError("Failed to initialize the WinRT library.");
|
||||
|
||||
HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED);
|
||||
{
|
||||
on_progress(L("Exporting source model"), 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 the input model."));
|
||||
|
||||
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 model by the Netfabb 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("Mesh repair failed."));
|
||||
repairAsync->GetResults();
|
||||
|
||||
on_progress(L("Loading repaired model"), 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(L("Saving mesh into the 3MF container failed."));
|
||||
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(L("Saving mesh into the 3MF container failed."));
|
||||
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");
|
||||
try {
|
||||
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(L("Saving mesh into the 3MF container failed."));
|
||||
hr = buffer->get_Length(&length);
|
||||
if (length == 0)
|
||||
break;
|
||||
fwrite(buffer_ptr, length, 1, fout);
|
||||
}
|
||||
} catch (...) {
|
||||
fclose(fout);
|
||||
throw;
|
||||
}
|
||||
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, wxProgressDialog& progress_dialog, const wxString& msg_header, std::string& fix_result)
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
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 = [&mutex, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) {
|
||||
std::lock_guard<std::mutex> lk(mutex);
|
||||
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 source model"), 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("Export of a temporary 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 model"), 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 of the repaired 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]->set_new_unique_id();
|
||||
}
|
||||
model_object.invalidate_bounding_box();
|
||||
-- ivolume;
|
||||
on_progress(L("Model repair finished"), 100);
|
||||
success = true;
|
||||
finished = true;
|
||||
} catch (RepairCanceledException & /* ex */) {
|
||||
canceled = true;
|
||||
finished = true;
|
||||
on_progress(L("Model repair canceled"), 100);
|
||||
} catch (std::exception &ex) {
|
||||
success = false;
|
||||
finished = true;
|
||||
on_progress(ex.what(), 100);
|
||||
}
|
||||
});
|
||||
while (! finished) {
|
||||
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 */
|
||||
|
|
|
@ -181,7 +181,7 @@ Http::priv::~priv()
|
|||
|
||||
bool Http::priv::ca_file_supported(::CURL *curl)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
bool res = false;
|
||||
#else
|
||||
bool res = true;
|
||||
|
@ -194,6 +194,7 @@ bool Http::priv::ca_file_supported(::CURL *curl)
|
|||
if (::curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls) == CURLE_OK) {
|
||||
if (tls->backend == CURLSSLBACKEND_SCHANNEL || tls->backend == CURLSSLBACKEND_DARWINSSL) {
|
||||
// With Windows and OS X native SSL support, cert files cannot be set
|
||||
// DK: OSX is now not building CURL and links system one, thus we do not know which backend is installed. Still, false will be returned since the ifdef at the begining if this function.
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include <libslic3r/AABBTreeIndirect.hpp>
|
||||
#include <libslic3r/AABBTreeLines.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
|
@ -58,3 +59,287 @@ TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndi
|
|||
REQUIRE(closest_point.y() == Approx(0.5));
|
||||
REQUIRE(closest_point.z() == Approx(1.));
|
||||
}
|
||||
|
||||
TEST_CASE("Creating a several 2d lines, testing closest point query", "[AABBIndirect]")
|
||||
{
|
||||
std::vector<Linef> lines { };
|
||||
lines.push_back(Linef(Vec2d(0.0, 0.0), Vec2d(1.0, 0.0)));
|
||||
lines.push_back(Linef(Vec2d(1.0, 0.0), Vec2d(1.0, 1.0)));
|
||||
lines.push_back(Linef(Vec2d(1.0, 1.0), Vec2d(0.0, 1.0)));
|
||||
lines.push_back(Linef(Vec2d(0.0, 1.0), Vec2d(0.0, 0.0)));
|
||||
|
||||
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
|
||||
|
||||
size_t hit_idx_out;
|
||||
Vec2d hit_point_out;
|
||||
auto sqr_dist = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, Vec2d(0.0, 0.0), hit_idx_out,
|
||||
hit_point_out);
|
||||
REQUIRE(sqr_dist == Approx(0.0));
|
||||
REQUIRE((hit_idx_out == 0 || hit_idx_out == 3));
|
||||
REQUIRE(hit_point_out.x() == Approx(0.0));
|
||||
REQUIRE(hit_point_out.y() == Approx(0.0));
|
||||
|
||||
sqr_dist = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, Vec2d(1.5, 0.5), hit_idx_out,
|
||||
hit_point_out);
|
||||
REQUIRE(sqr_dist == Approx(0.25));
|
||||
REQUIRE(hit_idx_out == 1);
|
||||
REQUIRE(hit_point_out.x() == Approx(1.0));
|
||||
REQUIRE(hit_point_out.y() == Approx(0.5));
|
||||
}
|
||||
|
||||
#if 0
|
||||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
#include <ratio>
|
||||
#include <chrono>
|
||||
TEST_CASE("AABBTreeLines vs SignedDistanceGrid time Benchmark", "[AABBIndirect]")
|
||||
{
|
||||
std::vector<Points> lines { Points { } };
|
||||
std::vector<Linef> linesf { };
|
||||
Vec2d prevf { };
|
||||
|
||||
// NOTE: max coord value of the lines is approx 83 mm
|
||||
for (int r = 1; r < 1000; ++r) {
|
||||
lines[0].push_back(Point::new_scale(Vec2d(exp(0.005f * r) * cos(r), exp(0.005f * r) * cos(r))));
|
||||
linesf.emplace_back(prevf, Vec2d(exp(0.005f * r) * cos(r), exp(0.005f * r) * cos(r)));
|
||||
prevf = linesf.back().b;
|
||||
}
|
||||
|
||||
int build_num = 10000;
|
||||
using namespace std::chrono;
|
||||
{
|
||||
std::cout << "building the tree " << build_num << " times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (int i = 0; i < build_num; ++i) {
|
||||
volatile auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(linesf);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
|
||||
}
|
||||
{
|
||||
std::cout << "building the grid res 1mm ONLY " << build_num/100 << " !!! times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (int i = 0; i < build_num/100; ++i) {
|
||||
EdgeGrid::Grid grid { };
|
||||
grid.create(lines, scaled(1.0), true);
|
||||
grid.calculate_sdf();
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
}
|
||||
{
|
||||
std::cout << "building the grid res 10mm " << build_num << " times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (int i = 0; i < build_num; ++i) {
|
||||
EdgeGrid::Grid grid { };
|
||||
grid.create(lines, scaled(10.0), true);
|
||||
grid.calculate_sdf();
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
}
|
||||
|
||||
EdgeGrid::Grid grid10 { };
|
||||
grid10.create(lines, scaled(10.0), true);
|
||||
coord_t query10_res = scaled(10.0);
|
||||
grid10.calculate_sdf();
|
||||
|
||||
EdgeGrid::Grid grid1 { };
|
||||
grid1.create(lines, scaled(1.0), true);
|
||||
coord_t query1_res = scaled(1.0);
|
||||
grid1.calculate_sdf();
|
||||
|
||||
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(linesf);
|
||||
|
||||
int query_num = 10000;
|
||||
Points query_points { };
|
||||
std::vector<Vec2d> query_pointsf { };
|
||||
for (int x = 0; x < query_num; ++x) {
|
||||
Vec2d qp { rand() / (double(RAND_MAX) + 1.0f) * 200.0 - 100.0, rand() / (double(RAND_MAX) + 1.0f) * 200.0
|
||||
- 100.0 };
|
||||
query_pointsf.push_back(qp);
|
||||
query_points.push_back(Point::new_scale(qp));
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "querying tree " << query_num << " times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (const Vec2d &qp : query_pointsf) {
|
||||
size_t hit_idx_out;
|
||||
Vec2d hit_point_out;
|
||||
AABBTreeLines::squared_distance_to_indexed_lines(linesf, tree, qp, hit_idx_out, hit_point_out);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "querying grid res 1mm " << query_num << " times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid1.closest_point_signed_distance(qp, query1_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "querying grid res 10mm " << query_num << " times..." << std::endl;
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid10.closest_point_signed_distance(qp, query10_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << "It took " << time_span.count() << " seconds." << std::endl << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Test build and queries together - same number of contour points and query points" << std::endl << std::endl;
|
||||
|
||||
std::vector<int> point_counts { 100, 300, 500, 1000, 3000 };
|
||||
for (auto count : point_counts) {
|
||||
|
||||
std::vector<Points> lines { Points { } };
|
||||
std::vector<Linef> linesf { };
|
||||
Vec2d prevf { };
|
||||
Points query_points { };
|
||||
std::vector<Vec2d> query_pointsf { };
|
||||
|
||||
for (int x = 0; x < count; ++x) {
|
||||
Vec2d cp { rand() / (double(RAND_MAX) + 1.0f) * 200.0 - 100.0, rand() / (double(RAND_MAX) + 1.0f) * 200.0
|
||||
- 100.0 };
|
||||
lines[0].push_back(Point::new_scale(cp));
|
||||
linesf.emplace_back(prevf, cp);
|
||||
prevf = linesf.back().b;
|
||||
|
||||
Vec2d qp { rand() / (double(RAND_MAX) + 1.0f) * 200.0 - 100.0, rand() / (double(RAND_MAX) + 1.0f) * 200.0
|
||||
- 100.0 };
|
||||
query_pointsf.push_back(qp);
|
||||
query_points.push_back(Point::new_scale(qp));
|
||||
}
|
||||
|
||||
std::cout << "Test for point count: " << count << std::endl;
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(linesf);
|
||||
for (const Vec2d &qp : query_pointsf) {
|
||||
size_t hit_idx_out;
|
||||
Vec2d hit_point_out;
|
||||
AABBTreeLines::squared_distance_to_indexed_lines(linesf, tree, qp, hit_idx_out, hit_point_out);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Tree took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
EdgeGrid::Grid grid1 { };
|
||||
grid1.create(lines, scaled(1.0), true);
|
||||
coord_t query1_res = scaled(1.0);
|
||||
grid1.calculate_sdf();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid1.closest_point_signed_distance(qp, query1_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Grid 1mm took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
EdgeGrid::Grid grid10 { };
|
||||
grid10.create(lines, scaled(10.0), true);
|
||||
coord_t query10_res = scaled(10.0);
|
||||
grid10.calculate_sdf();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid10.closest_point_signed_distance(qp, query10_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Grid 10mm took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::cout << "Test build and queries together - same number of contour points and query points" << std::endl <<
|
||||
"And with limited contour edge length to 4mm " << std::endl;
|
||||
for (auto count : point_counts) {
|
||||
|
||||
std::vector<Points> lines { Points { } };
|
||||
std::vector<Linef> linesf { };
|
||||
Vec2d prevf { };
|
||||
Points query_points { };
|
||||
std::vector<Vec2d> query_pointsf { };
|
||||
|
||||
for (int x = 0; x < count; ++x) {
|
||||
Vec2d cp { rand() / (double(RAND_MAX) + 1.0f) * 200.0 - 100.0, rand() / (double(RAND_MAX) + 1.0f) * 200.0
|
||||
- 100.0 };
|
||||
Vec2d contour = prevf + cp.normalized()*4.0; // limits the cnotour edge len to 4mm
|
||||
lines[0].push_back(Point::new_scale(contour));
|
||||
linesf.emplace_back(prevf, contour);
|
||||
prevf = linesf.back().b;
|
||||
|
||||
Vec2d qp { rand() / (double(RAND_MAX) + 1.0f) * 200.0 - 100.0, rand() / (double(RAND_MAX) + 1.0f) * 200.0
|
||||
- 100.0 };
|
||||
query_pointsf.push_back(qp);
|
||||
query_points.push_back(Point::new_scale(qp));
|
||||
}
|
||||
|
||||
std::cout << "Test for point count: " << count << std::endl;
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(linesf);
|
||||
for (const Vec2d &qp : query_pointsf) {
|
||||
size_t hit_idx_out;
|
||||
Vec2d hit_point_out;
|
||||
AABBTreeLines::squared_distance_to_indexed_lines(linesf, tree, qp, hit_idx_out, hit_point_out);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Tree took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
EdgeGrid::Grid grid1 { };
|
||||
grid1.create(lines, scaled(1.0), true);
|
||||
coord_t query1_res = scaled(1.0);
|
||||
grid1.calculate_sdf();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid1.closest_point_signed_distance(qp, query1_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Grid 1mm took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
{
|
||||
high_resolution_clock::time_point t1 = high_resolution_clock::now();
|
||||
EdgeGrid::Grid grid10 { };
|
||||
grid10.create(lines, scaled(10.0), true);
|
||||
coord_t query10_res = scaled(10.0);
|
||||
grid10.calculate_sdf();
|
||||
for (const Point &qp : query_points) {
|
||||
volatile auto dist = grid10.closest_point_signed_distance(qp, query10_res);
|
||||
}
|
||||
high_resolution_clock::time_point t2 = high_resolution_clock::now();
|
||||
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
|
||||
std::cout << " Grid 10mm took " << time_span.count() << " seconds." << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -54,18 +54,9 @@ TEST_CASE("astar algorithm test over 3D point grid", "[AStar]") {
|
|||
|
||||
auto pgrid = point_grid(ex_seq, vol, {0.1f, 0.1f, 0.1f});
|
||||
|
||||
size_t target = pgrid.point_count() - 1;
|
||||
|
||||
std::cout << "Tracing route to " << pgrid.get_coord(target).transpose() << std::endl;
|
||||
PointGridTracer pgt{pgrid, pgrid.point_count() - 1};
|
||||
std::vector<size_t> out;
|
||||
bool found = astar::search_route(pgt, size_t(0), std::back_inserter(out));
|
||||
|
||||
std::cout << "Route taken: ";
|
||||
for (size_t i : out) {
|
||||
std::cout << "(" << pgrid.get_coord(i).transpose() << ") ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
REQUIRE(found);
|
||||
}
|
||||
|
|
|
@ -325,9 +325,9 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
|
|||
double disp_w = 120.96;
|
||||
double disp_h = 68.04;
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t cntr = 0;
|
||||
#endif
|
||||
//#ifndef NDEBUG
|
||||
// size_t cntr = 0;
|
||||
//#endif
|
||||
for (ExPolygons &layer : layers) {
|
||||
auto rst = create_raster(res, disp_w, disp_h);
|
||||
|
||||
|
@ -335,11 +335,11 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
|
|||
rst.draw(island);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out);
|
||||
out << rst.encode(sla::PNGRasterEncoder{});
|
||||
out.close();
|
||||
#endif
|
||||
//#ifndef NDEBUG
|
||||
// std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out);
|
||||
// out << rst.encode(sla::PNGRasterEncoder{});
|
||||
// out.close();
|
||||
//#endif
|
||||
|
||||
ExPolygons layer_ = sla::raster_to_polygons(rst);
|
||||
// float delta = scaled(std::min(rst.pixel_dimensions().h_mm,
|
||||
|
@ -347,19 +347,19 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
|
|||
|
||||
// layer_ = expolygons_simplify(layer_, delta);
|
||||
|
||||
#ifndef NDEBUG
|
||||
SVG svg(objname + std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)}));
|
||||
svg.draw(layer_);
|
||||
svg.draw(layer, "green");
|
||||
svg.Close();
|
||||
#endif
|
||||
//#ifndef NDEBUG
|
||||
// SVG svg(objname + std::to_string(cntr) + ".svg", BoundingBox(Point{0, 0}, Point{scaled(disp_w), scaled(disp_h)}));
|
||||
// svg.draw(layer_);
|
||||
// svg.draw(layer, "green");
|
||||
// svg.Close();
|
||||
//#endif
|
||||
|
||||
double layera = 0., layera_ = 0.;
|
||||
for (auto &p : layer) layera += p.area();
|
||||
for (auto &p : layer_) layera_ += p.area();
|
||||
#ifndef NDEBUG
|
||||
std::cout << cntr++ << std::endl;
|
||||
#endif
|
||||
//#ifndef NDEBUG
|
||||
// std::cout << cntr++ << std::endl;
|
||||
//#endif
|
||||
double diff = std::abs(layera_ - layera);
|
||||
REQUIRE((diff <= 0.1 * layera || diff < scaled<double>(1.) * scaled<double>(1.)));
|
||||
|
||||
|
|
|
@ -346,25 +346,36 @@ TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]"
|
|||
size_t id;
|
||||
float val;
|
||||
};
|
||||
size_t count = 50000;
|
||||
static constexpr const size_t count = 50000;
|
||||
std::vector<size_t> idxs(count, {0});
|
||||
std::vector<bool> dels(count, false);
|
||||
auto q = make_miniheap_mutable_priority_queue<MyValue, 16, true>(
|
||||
[&](MyValue &v, size_t idx) {
|
||||
idxs[v.id] = idx;
|
||||
},
|
||||
[&idxs](MyValue &v, size_t idx) { idxs[v.id] = idx; },
|
||||
[](MyValue &l, MyValue &r) { return l.val < r.val; });
|
||||
q.reserve(count);
|
||||
for (size_t id = 0; id < count; id++) {
|
||||
MyValue mv{ id, rand() / 100.f };
|
||||
q.push(mv);
|
||||
using QueueType = decltype(q);
|
||||
THEN("Skip queue has 0th element unused, 1st element is the top of the queue.") {
|
||||
CHECK(QueueType::address::is_padding(0));
|
||||
CHECK(!QueueType::address::is_padding(1));
|
||||
}
|
||||
q.reserve(count);
|
||||
for (size_t id = 0; id < count; ++ id)
|
||||
q.push({ id, rand() / 100.f });
|
||||
MyValue v = q.top(); // copy
|
||||
THEN("Element at the top of the queue has a valid ID.") {
|
||||
CHECK(v.id >= 0);
|
||||
CHECK(v.id < count);
|
||||
}
|
||||
THEN("Element at the top of the queue has its position stored in idxs.") {
|
||||
CHECK(idxs[v.id] == 1);
|
||||
}
|
||||
MyValue it = q.top(); // copy
|
||||
q.pop();
|
||||
// is valid id (no initial value default value)
|
||||
CHECK(it.id != 0);
|
||||
// is first item in queue valid value
|
||||
CHECK(idxs[0] != std::numeric_limits<size_t>::max());
|
||||
THEN("Element removed from the queue has its position in idxs reset to invalid.") {
|
||||
CHECK(idxs[v.id] == q.invalid_id());
|
||||
}
|
||||
THEN("Element was removed from the queue, new top of the queue has its index set correctly.") {
|
||||
CHECK(q.top().id >= 0);
|
||||
CHECK(q.top().id < count);
|
||||
CHECK(idxs[q.top().id] == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]")
|
||||
|
@ -382,23 +393,23 @@ TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]")
|
|||
q.reserve(count);
|
||||
|
||||
auto rand_val = [&]()->float { return (rand() % 53) / 10.f; };
|
||||
size_t ord = 0;
|
||||
for (size_t id = 0; id < count; id++) {
|
||||
MyValue mv;
|
||||
mv.id = ord++;
|
||||
mv.val = rand_val();
|
||||
q.push(mv);
|
||||
}
|
||||
for (size_t id = 0; id < count; ++ id)
|
||||
q.push({ id, rand_val() });
|
||||
|
||||
auto check = [&]()->bool{
|
||||
for (size_t i = 0; i < idxs.size(); ++i) {
|
||||
if (dels[i]) continue;
|
||||
size_t qid = idxs[i];
|
||||
if (qid > 3*count) {
|
||||
return false;
|
||||
}
|
||||
MyValue &mv = q[qid];
|
||||
if (mv.id != i) {
|
||||
return false; // ERROR
|
||||
if (dels[i]) {
|
||||
if (idxs[i] != q.invalid_id())
|
||||
return false; // ERROR
|
||||
} else {
|
||||
size_t qid = idxs[i];
|
||||
if (qid >= q.heap_size()) {
|
||||
return false; // ERROR
|
||||
}
|
||||
MyValue &mv = q[qid];
|
||||
if (mv.id != i) {
|
||||
return false; // ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -406,6 +417,7 @@ TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]")
|
|||
|
||||
CHECK(check()); // initial check
|
||||
|
||||
// Generate an element ID of an elmenet, which was not yet deleted, thus it is still valid.
|
||||
auto get_valid_id = [&]()->int {
|
||||
int id = 0;
|
||||
do {
|
||||
|
@ -413,23 +425,28 @@ TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]")
|
|||
} while (dels[id]);
|
||||
return id;
|
||||
};
|
||||
|
||||
// Remove first 100 elements from the queue of 5000 elements, cross-validate indices.
|
||||
// Re-enter every 20th element back to the queue.
|
||||
for (size_t i = 0; i < 100; i++) {
|
||||
MyValue it = q.top(); // copy
|
||||
MyValue v = q.top(); // copy
|
||||
q.pop();
|
||||
dels[it.id] = true;
|
||||
dels[v.id] = true;
|
||||
CHECK(check());
|
||||
if (i % 20 == 0) {
|
||||
it.val = rand_val();
|
||||
q.push(it);
|
||||
dels[it.id] = false;
|
||||
v.val = rand_val();
|
||||
q.push(v);
|
||||
dels[v.id] = false;
|
||||
CHECK(check());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove some valid element from the queue.
|
||||
int id = get_valid_id();
|
||||
CHECK(idxs[id] != q.invalid_id());
|
||||
q.remove(idxs[id]);
|
||||
dels[id] = true;
|
||||
CHECK(check());
|
||||
// and change 5 random elements and reorder them in the queue.
|
||||
for (size_t j = 0; j < 5; j++) {
|
||||
int id = get_valid_id();
|
||||
size_t qid = idxs[id];
|
||||
|
|
Loading…
Reference in a new issue