Merge branch 'master' into tm_sla_printer_mirror
This commit is contained in:
commit
b4ef812d32
2
Build.PL
2
Build.PL
@ -16,6 +16,8 @@ my %prereqs = qw(
|
||||
ExtUtils::MakeMaker 6.80
|
||||
ExtUtils::ParseXS 3.22
|
||||
ExtUtils::XSpp 0
|
||||
ExtUtils::XSpp::Cmd 0
|
||||
ExtUtils::CppGuess 0
|
||||
ExtUtils::Typemaps 0
|
||||
ExtUtils::Typemaps::Basic 0
|
||||
File::Basename 0
|
||||
|
@ -60,7 +60,7 @@ if (MSVC)
|
||||
# /bigobj (Increase Number of Sections in .Obj file)
|
||||
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
||||
# Generate symbols at every build target, even for the release.
|
||||
add_compile_options(-bigobj -Zm316 /Zi)
|
||||
add_compile_options(-bigobj -Zm520 /Zi)
|
||||
endif ()
|
||||
|
||||
# Display and check CMAKE_PREFIX_PATH
|
||||
@ -240,15 +240,34 @@ if(NOT WIN32)
|
||||
set(MINIMUM_BOOST_VERSION "1.64.0")
|
||||
endif()
|
||||
find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS system filesystem thread log locale regex)
|
||||
if(Boost_FOUND)
|
||||
# include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||
if (APPLE)
|
||||
# BOOST_ASIO_DISABLE_KQUEUE : prevents a Boost ASIO bug on OS X: https://svn.boost.org/trac/boost/ticket/5339
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE)
|
||||
endif()
|
||||
if(NOT SLIC3R_STATIC)
|
||||
add_definitions(-DBOOST_LOG_DYN_LINK)
|
||||
endif()
|
||||
|
||||
add_library(boost_libs INTERFACE)
|
||||
add_library(boost_headeronly INTERFACE)
|
||||
|
||||
if (APPLE)
|
||||
# BOOST_ASIO_DISABLE_KQUEUE : prevents a Boost ASIO bug on OS X: https://svn.boost.org/trac/boost/ticket/5339
|
||||
target_compile_definitions(boost_headeronly INTERFACE BOOST_ASIO_DISABLE_KQUEUE)
|
||||
endif()
|
||||
|
||||
if(NOT SLIC3R_STATIC)
|
||||
target_compile_definitions(boost_headeronly INTERFACE BOOST_LOG_DYN_LINK)
|
||||
endif()
|
||||
|
||||
if(TARGET Boost::system)
|
||||
message(STATUS "Boost::boost exists")
|
||||
target_link_libraries(boost_headeronly INTERFACE Boost::boost)
|
||||
target_link_libraries(boost_libs INTERFACE
|
||||
boost_headeronly # includes the custom compile definitions as well
|
||||
Boost::system
|
||||
Boost::filesystem
|
||||
Boost::thread
|
||||
Boost::log
|
||||
Boost::locale
|
||||
Boost::regex
|
||||
)
|
||||
else()
|
||||
target_include_directories(boost_headeronly INTERFACE ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(boost_libs INTERFACE boost_headeronly ${Boost_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Find and configure intel-tbb
|
||||
|
2
deps/CMakeLists.txt
vendored
2
deps/CMakeLists.txt
vendored
@ -84,6 +84,7 @@ if (MSVC)
|
||||
dep_wxwidgets
|
||||
dep_gtest
|
||||
dep_nlopt
|
||||
# dep_qhull # Experimental
|
||||
dep_zlib # on Windows we still need zlib
|
||||
)
|
||||
|
||||
@ -97,6 +98,7 @@ else()
|
||||
dep_wxwidgets
|
||||
dep_gtest
|
||||
dep_nlopt
|
||||
dep_qhull
|
||||
)
|
||||
|
||||
endif()
|
||||
|
13
deps/deps-unix-common.cmake
vendored
13
deps/deps-unix-common.cmake
vendored
@ -32,3 +32,16 @@ ExternalProject_Add(dep_nlopt
|
||||
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
||||
${DEP_CMAKE_OPTS}
|
||||
)
|
||||
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
ExternalProject_Add(dep_qhull
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/qhull/qhull/archive/v7.2.1.tar.gz"
|
||||
URL_HASH SHA256=6fc251e0b75467e00943bfb7191e986fce0e1f8f6f0251f9c6ce5a843821ea78
|
||||
CMAKE_ARGS
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
||||
${DEP_CMAKE_OPTS}
|
||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch
|
||||
)
|
||||
|
54
deps/deps-windows.cmake
vendored
54
deps/deps-windows.cmake
vendored
@ -1,21 +1,34 @@
|
||||
|
||||
# https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html
|
||||
if (MSVC_VERSION EQUAL 1800)
|
||||
# 1800 = VS 12.0 (v120 toolset)
|
||||
set(DEP_VS_VER "12")
|
||||
set(DEP_BOOST_TOOLSET "msvc-12.0")
|
||||
elseif (MSVC_VERSION EQUAL 1900)
|
||||
# 1900 = VS 14.0 (v140 toolset)
|
||||
set(DEP_VS_VER "14")
|
||||
set(DEP_BOOST_TOOLSET "msvc-14.0")
|
||||
elseif (MSVC_VERSION GREATER 1900)
|
||||
elseif (MSVC_VERSION LESS 1920)
|
||||
# 1910-1919 = VS 15.0 (v141 toolset)
|
||||
set(DEP_VS_VER "15")
|
||||
set(DEP_BOOST_TOOLSET "msvc-14.1")
|
||||
elseif (MSVC_VERSION LESS 1930)
|
||||
# 1920-1929 = VS 16.0 (v142 toolset)
|
||||
set(DEP_VS_VER "16")
|
||||
set(DEP_BOOST_TOOLSET "msvc-14.2")
|
||||
else ()
|
||||
message(FATAL_ERROR "Unsupported MSVC version")
|
||||
endif ()
|
||||
|
||||
if (${DEPS_BITS} EQUAL 32)
|
||||
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER}")
|
||||
set(DEP_PLATFORM "Win32")
|
||||
else ()
|
||||
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER} Win64")
|
||||
if (DEP_VS_VER LESS 16)
|
||||
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER} Win64")
|
||||
else ()
|
||||
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER}")
|
||||
endif ()
|
||||
set(DEP_PLATFORM "x64")
|
||||
endif ()
|
||||
|
||||
|
||||
@ -28,8 +41,8 @@ endif ()
|
||||
|
||||
ExternalProject_Add(dep_boost
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz"
|
||||
URL_HASH SHA256=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60
|
||||
URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz"
|
||||
URL_HASH SHA256=882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND bootstrap.bat
|
||||
BUILD_COMMAND b2.exe
|
||||
@ -57,6 +70,7 @@ ExternalProject_Add(dep_tbb
|
||||
URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz"
|
||||
URL_HASH SHA256=0545cb6033bd1873fcae3ea304def720a380a88292726943ae3b9b207f322efe
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_DEBUG_POSTFIX=_debug
|
||||
-DTBB_BUILD_SHARED=OFF
|
||||
@ -81,6 +95,7 @@ ExternalProject_Add(dep_gtest
|
||||
URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz"
|
||||
URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
||||
CMAKE_ARGS
|
||||
-DBUILD_GMOCK=OFF
|
||||
-Dgtest_force_shared_crt=ON
|
||||
@ -105,6 +120,7 @@ ExternalProject_Add(dep_nlopt
|
||||
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
|
||||
URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
||||
CMAKE_ARGS
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DNLOPT_PYTHON=OFF
|
||||
@ -133,6 +149,7 @@ ExternalProject_Add(dep_zlib
|
||||
URL "https://zlib.net/zlib-1.2.11.tar.xz"
|
||||
URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
||||
CMAKE_ARGS
|
||||
-DSKIP_INSTALL_FILES=ON # Prevent installation of man pages et al.
|
||||
"-DINSTALL_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}\\fallout" # I found no better way of preventing zlib from creating & installing DLLs :-/
|
||||
@ -199,6 +216,33 @@ if (${DEP_DEBUG})
|
||||
)
|
||||
endif ()
|
||||
|
||||
find_package(Git REQUIRED)
|
||||
|
||||
ExternalProject_Add(dep_qhull
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/qhull/qhull/archive/v7.2.1.tar.gz"
|
||||
URL_HASH SHA256=6fc251e0b75467e00943bfb7191e986fce0e1f8f6f0251f9c6ce5a843821ea78
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DCMAKE_DEBUG_POSTFIX=d
|
||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch
|
||||
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_qhull BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_qhull build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /m /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if (${DEPS_BITS} EQUAL 32)
|
||||
set(DEP_WXWIDGETS_TARGET "")
|
||||
|
121
deps/qhull-mods.patch
vendored
Normal file
121
deps/qhull-mods.patch
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
From a31ae4781a4afa60e21c70e5b4ae784bcd447c8a Mon Sep 17 00:00:00 2001
|
||||
From: tamasmeszaros <meszaros.q@gmail.com>
|
||||
Date: Thu, 6 Jun 2019 15:41:43 +0200
|
||||
Subject: [PATCH] prusa-slicer changes
|
||||
|
||||
---
|
||||
CMakeLists.txt | 44 +++++++++++++++++++++++++++++++++++---
|
||||
Config.cmake.in | 2 ++
|
||||
src/libqhull_r/qhull_r-exports.def | 2 ++
|
||||
src/libqhull_r/user_r.h | 2 +-
|
||||
4 files changed, 46 insertions(+), 4 deletions(-)
|
||||
create mode 100644 Config.cmake.in
|
||||
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index 59dff41..20c2ec5 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -61,7 +61,7 @@
|
||||
# $DateTime: 2016/01/18 19:29:17 $$Author: bbarber $
|
||||
|
||||
project(qhull)
|
||||
-cmake_minimum_required(VERSION 2.6)
|
||||
+cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
# Define qhull_VERSION in CMakeLists.txt, Makefile, qhull-exports.def, qhull_p-exports.def, qhull_r-exports.def, qhull-warn.pri
|
||||
set(qhull_VERSION2 "2015.2 2016/01/18") # not used, See global.c, global_r.c, rbox.c, rbox_r.c
|
||||
@@ -610,10 +610,48 @@ add_test(NAME user_eg3
|
||||
# Define install
|
||||
# ---------------------------------------
|
||||
|
||||
-install(TARGETS ${qhull_TARGETS_INSTALL}
|
||||
+install(TARGETS ${qhull_TARGETS_INSTALL} EXPORT QhullTargets
|
||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
- ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
|
||||
+ ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
||||
+ INCLUDES DESTINATION include)
|
||||
+
|
||||
+include(CMakePackageConfigHelpers)
|
||||
+
|
||||
+write_basic_package_version_file(
|
||||
+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake"
|
||||
+ VERSION ${qhull_VERSION}
|
||||
+ COMPATIBILITY AnyNewerVersion
|
||||
+)
|
||||
+
|
||||
+export(EXPORT QhullTargets
|
||||
+ FILE "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullTargets.cmake"
|
||||
+ NAMESPACE Qhull::
|
||||
+)
|
||||
+
|
||||
+configure_file(Config.cmake.in
|
||||
+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake"
|
||||
+ @ONLY
|
||||
+)
|
||||
+
|
||||
+set(ConfigPackageLocation lib/cmake/Qhull)
|
||||
+install(EXPORT QhullTargets
|
||||
+ FILE
|
||||
+ QhullTargets.cmake
|
||||
+ NAMESPACE
|
||||
+ Qhull::
|
||||
+ DESTINATION
|
||||
+ ${ConfigPackageLocation}
|
||||
+)
|
||||
+install(
|
||||
+ FILES
|
||||
+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfig.cmake"
|
||||
+ "${CMAKE_CURRENT_BINARY_DIR}/Qhull/QhullConfigVersion.cmake"
|
||||
+ DESTINATION
|
||||
+ ${ConfigPackageLocation}
|
||||
+ COMPONENT
|
||||
+ Devel
|
||||
+)
|
||||
|
||||
install(FILES ${libqhull_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
|
||||
install(FILES ${libqhull_DOC} DESTINATION ${INCLUDE_INSTALL_DIR}/libqhull)
|
||||
diff --git a/Config.cmake.in b/Config.cmake.in
|
||||
new file mode 100644
|
||||
index 0000000..bc92bfe
|
||||
--- /dev/null
|
||||
+++ b/Config.cmake.in
|
||||
@@ -0,0 +1,2 @@
|
||||
+include("${CMAKE_CURRENT_LIST_DIR}/QhullTargets.cmake")
|
||||
+
|
||||
diff --git a/src/libqhull_r/qhull_r-exports.def b/src/libqhull_r/qhull_r-exports.def
|
||||
index 325d57c..72f6ad0 100644
|
||||
--- a/src/libqhull_r/qhull_r-exports.def
|
||||
+++ b/src/libqhull_r/qhull_r-exports.def
|
||||
@@ -185,6 +185,7 @@ qh_memsetup
|
||||
qh_memsize
|
||||
qh_memstatistics
|
||||
qh_memtotal
|
||||
+qh_memcheck
|
||||
qh_merge_degenredundant
|
||||
qh_merge_nonconvex
|
||||
qh_mergecycle
|
||||
@@ -372,6 +373,7 @@ qh_settruncate
|
||||
qh_setunique
|
||||
qh_setvoronoi_all
|
||||
qh_setzero
|
||||
+qh_setendpointer
|
||||
qh_sharpnewfacets
|
||||
qh_skipfacet
|
||||
qh_skipfilename
|
||||
diff --git a/src/libqhull_r/user_r.h b/src/libqhull_r/user_r.h
|
||||
index fc105b9..7cca65a 100644
|
||||
--- a/src/libqhull_r/user_r.h
|
||||
+++ b/src/libqhull_r/user_r.h
|
||||
@@ -139,7 +139,7 @@ Code flags --
|
||||
REALfloat = 1 all numbers are 'float' type
|
||||
= 0 all numbers are 'double' type
|
||||
*/
|
||||
-#define REALfloat 0
|
||||
+#define REALfloat 1
|
||||
|
||||
#if (REALfloat == 1)
|
||||
#define realT float
|
||||
--
|
||||
2.16.2.windows.1
|
||||
|
BIN
resources/icons/mirroring_off.png
Normal file
BIN
resources/icons/mirroring_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 589 B |
BIN
resources/icons/mirroring_on.png
Normal file
BIN
resources/icons/mirroring_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 600 B |
BIN
resources/icons/mirroring_transparent.png
Normal file
BIN
resources/icons/mirroring_transparent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 93 B |
@ -15,6 +15,7 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
|
||||
#define INTENSITY_AMBIENT 0.3
|
||||
|
||||
uniform mat4 volume_world_matrix;
|
||||
uniform float object_max_z;
|
||||
|
||||
// x = tainted, y = specular;
|
||||
varying vec2 intensity;
|
||||
@ -42,6 +43,12 @@ void main()
|
||||
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
// Scaled to widths of the Z texture.
|
||||
object_z = (volume_world_matrix * gl_Vertex).z;
|
||||
if (object_max_z > 0.0)
|
||||
// when rendering the overlay
|
||||
object_z = object_max_z * gl_MultiTexCoord0.y;
|
||||
else
|
||||
// when rendering the volumes
|
||||
object_z = (volume_world_matrix * gl_Vertex).z;
|
||||
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
@ -17,9 +17,6 @@ add_subdirectory(semver)
|
||||
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
||||
add_subdirectory(libnest2d)
|
||||
|
||||
include_directories(${LIBDIR}/qhull/src)
|
||||
#message(STATUS ${LIBDIR}/qhull/src)
|
||||
|
||||
add_subdirectory(libslic3r)
|
||||
|
||||
if (SLIC3R_GUI)
|
||||
@ -142,7 +139,7 @@ if (MSVC)
|
||||
target_compile_definitions(PrusaSlicer_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
|
||||
add_dependencies(PrusaSlicer_app_gui PrusaSlicer)
|
||||
set_target_properties(PrusaSlicer_app_gui PROPERTIES OUTPUT_NAME "prusa-slicer")
|
||||
target_include_directories(PrusaSlicer_app_gui SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(PrusaSlicer_app_gui PRIVATE boost_headeronly)
|
||||
|
||||
add_executable(PrusaSlicer_app_console PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc)
|
||||
# Generate debug symbols even in release mode.
|
||||
@ -150,7 +147,7 @@ if (MSVC)
|
||||
target_compile_definitions(PrusaSlicer_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE)
|
||||
add_dependencies(PrusaSlicer_app_console PrusaSlicer)
|
||||
set_target_properties(PrusaSlicer_app_console PROPERTIES OUTPUT_NAME "prusa-slicer-console")
|
||||
target_include_directories(PrusaSlicer_app_console SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(PrusaSlicer_app_console PRIVATE boost_headeronly)
|
||||
endif ()
|
||||
|
||||
# Link the resources dir to where Slic3r GUI expects it
|
||||
|
@ -7,10 +7,13 @@
|
||||
#include <Windows.h>
|
||||
#include <wchar.h>
|
||||
#ifdef SLIC3R_GUI
|
||||
extern "C"
|
||||
{
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
#endif /* WIN32 */
|
||||
|
||||
@ -241,8 +244,7 @@ int CLI::run(int argc, char **argv)
|
||||
} else if (opt_key == "cut" || opt_key == "cut_x" || opt_key == "cut_y") {
|
||||
std::vector<Model> new_models;
|
||||
for (auto &model : m_models) {
|
||||
model.repair();
|
||||
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
|
||||
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
|
||||
size_t num_objects = model.objects.size();
|
||||
for (size_t i = 0; i < num_objects; ++ i) {
|
||||
|
||||
@ -301,8 +303,9 @@ int CLI::run(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
} else if (opt_key == "repair") {
|
||||
for (auto &model : m_models)
|
||||
model.repair();
|
||||
// Models are repaired by default.
|
||||
//for (auto &model : m_models)
|
||||
// model.repair();
|
||||
} else {
|
||||
boost::nowide::cerr << "error: option not implemented yet: " << opt_key << std::endl;
|
||||
return 1;
|
||||
|
@ -8,10 +8,13 @@
|
||||
#include <wchar.h>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
extern "C"
|
||||
{
|
||||
// Let the NVIDIA and AMD know we want to use their graphics card
|
||||
// on a dual graphics card system.
|
||||
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
|
||||
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||
}
|
||||
#endif /* SLIC3R_GUI */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -11,4 +11,4 @@ add_library(admesh STATIC
|
||||
util.cpp
|
||||
)
|
||||
|
||||
target_include_directories(admesh SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(admesh PRIVATE boost_headeronly)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,271 +25,214 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// Boost pool: Don't use mutexes to synchronize memory allocation.
|
||||
#define BOOST_POOL_NO_MT
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag);
|
||||
static void reverse_facet(stl_file *stl, int facet_num)
|
||||
{
|
||||
++ stl->stats.facets_reversed;
|
||||
|
||||
static void
|
||||
stl_reverse_facet(stl_file *stl, int facet_num) {
|
||||
stl_vertex tmp_vertex;
|
||||
/* int tmp_neighbor;*/
|
||||
int neighbor[3];
|
||||
int vnot[3];
|
||||
int neighbor[3] = { stl->neighbors_start[facet_num].neighbor[0], stl->neighbors_start[facet_num].neighbor[1], stl->neighbors_start[facet_num].neighbor[2] };
|
||||
int vnot[3] = { stl->neighbors_start[facet_num].which_vertex_not[0], stl->neighbors_start[facet_num].which_vertex_not[1], stl->neighbors_start[facet_num].which_vertex_not[2] };
|
||||
|
||||
stl->stats.facets_reversed += 1;
|
||||
// reverse the facet
|
||||
stl_vertex tmp_vertex = stl->facet_start[facet_num].vertex[0];
|
||||
stl->facet_start[facet_num].vertex[0] = stl->facet_start[facet_num].vertex[1];
|
||||
stl->facet_start[facet_num].vertex[1] = tmp_vertex;
|
||||
|
||||
neighbor[0] = stl->neighbors_start[facet_num].neighbor[0];
|
||||
neighbor[1] = stl->neighbors_start[facet_num].neighbor[1];
|
||||
neighbor[2] = stl->neighbors_start[facet_num].neighbor[2];
|
||||
vnot[0] = stl->neighbors_start[facet_num].which_vertex_not[0];
|
||||
vnot[1] = stl->neighbors_start[facet_num].which_vertex_not[1];
|
||||
vnot[2] = stl->neighbors_start[facet_num].which_vertex_not[2];
|
||||
// fix the vnots of the neighboring facets
|
||||
if (neighbor[0] != -1)
|
||||
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] = (stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
|
||||
if (neighbor[1] != -1)
|
||||
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] = (stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
|
||||
if (neighbor[2] != -1)
|
||||
stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] = (stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
|
||||
|
||||
/* reverse the facet */
|
||||
tmp_vertex = stl->facet_start[facet_num].vertex[0];
|
||||
stl->facet_start[facet_num].vertex[0] =
|
||||
stl->facet_start[facet_num].vertex[1];
|
||||
stl->facet_start[facet_num].vertex[1] = tmp_vertex;
|
||||
// swap the neighbors of the facet that is being reversed
|
||||
stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
|
||||
stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
|
||||
|
||||
/* fix the vnots of the neighboring facets */
|
||||
if(neighbor[0] != -1)
|
||||
stl->neighbors_start[neighbor[0]].which_vertex_not[(vnot[0] + 1) % 3] =
|
||||
(stl->neighbors_start[neighbor[0]].
|
||||
which_vertex_not[(vnot[0] + 1) % 3] + 3) % 6;
|
||||
if(neighbor[1] != -1)
|
||||
stl->neighbors_start[neighbor[1]].which_vertex_not[(vnot[1] + 1) % 3] =
|
||||
(stl->neighbors_start[neighbor[1]].
|
||||
which_vertex_not[(vnot[1] + 1) % 3] + 4) % 6;
|
||||
if(neighbor[2] != -1)
|
||||
stl->neighbors_start[neighbor[2]].which_vertex_not[(vnot[2] + 1) % 3] =
|
||||
(stl->neighbors_start[neighbor[2]].
|
||||
which_vertex_not[(vnot[2] + 1) % 3] + 2) % 6;
|
||||
// swap the vnots of the facet that is being reversed
|
||||
stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
|
||||
stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
|
||||
|
||||
/* swap the neighbors of the facet that is being reversed */
|
||||
stl->neighbors_start[facet_num].neighbor[1] = neighbor[2];
|
||||
stl->neighbors_start[facet_num].neighbor[2] = neighbor[1];
|
||||
|
||||
/* swap the vnots of the facet that is being reversed */
|
||||
stl->neighbors_start[facet_num].which_vertex_not[1] = vnot[2];
|
||||
stl->neighbors_start[facet_num].which_vertex_not[2] = vnot[1];
|
||||
|
||||
/* reverse the values of the vnots of the facet that is being reversed */
|
||||
stl->neighbors_start[facet_num].which_vertex_not[0] =
|
||||
(stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
|
||||
stl->neighbors_start[facet_num].which_vertex_not[1] =
|
||||
(stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
|
||||
stl->neighbors_start[facet_num].which_vertex_not[2] =
|
||||
(stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
|
||||
// reverse the values of the vnots of the facet that is being reversed
|
||||
stl->neighbors_start[facet_num].which_vertex_not[0] = (stl->neighbors_start[facet_num].which_vertex_not[0] + 3) % 6;
|
||||
stl->neighbors_start[facet_num].which_vertex_not[1] = (stl->neighbors_start[facet_num].which_vertex_not[1] + 3) % 6;
|
||||
stl->neighbors_start[facet_num].which_vertex_not[2] = (stl->neighbors_start[facet_num].which_vertex_not[2] + 3) % 6;
|
||||
}
|
||||
|
||||
void
|
||||
stl_fix_normal_directions(stl_file *stl) {
|
||||
char *norm_sw;
|
||||
/* int edge_num;*/
|
||||
/* int vnot;*/
|
||||
int checked = 0;
|
||||
int facet_num;
|
||||
/* int next_facet;*/
|
||||
int i;
|
||||
int j;
|
||||
struct stl_normal {
|
||||
int facet_num;
|
||||
struct stl_normal *next;
|
||||
};
|
||||
struct stl_normal *head;
|
||||
struct stl_normal *tail;
|
||||
struct stl_normal *newn;
|
||||
struct stl_normal *temp;
|
||||
// Returns true if the normal was flipped.
|
||||
static bool check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag)
|
||||
{
|
||||
stl_facet *facet = &stl->facet_start[facet_num];
|
||||
|
||||
int* reversed_ids;
|
||||
int reversed_count = 0;
|
||||
int id;
|
||||
int force_exit = 0;
|
||||
stl_normal normal;
|
||||
stl_calculate_normal(normal, facet);
|
||||
stl_normalize_vector(normal);
|
||||
stl_normal normal_dif = (normal - facet->normal).cwiseAbs();
|
||||
|
||||
if (stl->error) return;
|
||||
const float eps = 0.001f;
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
// Normal is within tolerance. It is not really necessary to change the values here, but just for consistency, I will.
|
||||
facet->normal = normal;
|
||||
return false;
|
||||
}
|
||||
|
||||
// this may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
|
||||
if (stl->stats.number_of_facets == 0) return;
|
||||
stl_normal test_norm = facet->normal;
|
||||
stl_normalize_vector(test_norm);
|
||||
normal_dif = (normal - test_norm).cwiseAbs();
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
// The normal is not within tolerance, but direction is OK.
|
||||
if (normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
++ stl->stats.normals_fixed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize linked list. */
|
||||
head = (struct stl_normal*)malloc(sizeof(struct stl_normal));
|
||||
if(head == NULL) perror("stl_fix_normal_directions");
|
||||
tail = (struct stl_normal*)malloc(sizeof(struct stl_normal));
|
||||
if(tail == NULL) perror("stl_fix_normal_directions");
|
||||
head->next = tail;
|
||||
tail->next = tail;
|
||||
|
||||
/* Initialize list that keeps track of already fixed facets. */
|
||||
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
|
||||
if(norm_sw == NULL) perror("stl_fix_normal_directions");
|
||||
|
||||
/* Initialize list that keeps track of reversed facets. */
|
||||
reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int));
|
||||
if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids");
|
||||
|
||||
facet_num = 0;
|
||||
/* If normal vector is not within tolerance and backwards:
|
||||
Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
|
||||
of it being wrong randomly are low if most of the triangles are right: */
|
||||
if (stl_check_normal_vector(stl, 0, 0) == 2) {
|
||||
stl_reverse_facet(stl, 0);
|
||||
reversed_ids[reversed_count++] = 0;
|
||||
}
|
||||
|
||||
/* Say that we've fixed this facet: */
|
||||
norm_sw[facet_num] = 1;
|
||||
checked++;
|
||||
|
||||
for(;;) {
|
||||
/* Add neighbors_to_list.
|
||||
Add unconnected neighbors to the list:a */
|
||||
for(j = 0; j < 3; j++) {
|
||||
/* Reverse the neighboring facets if necessary. */
|
||||
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
|
||||
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
|
||||
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
|
||||
/* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */
|
||||
for (id = reversed_count - 1; id >= 0; --id) {
|
||||
stl_reverse_facet(stl, reversed_ids[id]);
|
||||
}
|
||||
force_exit = 1;
|
||||
break;
|
||||
} else {
|
||||
stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
|
||||
reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If this edge of the facet is connected: */
|
||||
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||
/* If we haven't fixed this facet yet, add it to the list: */
|
||||
if(norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
|
||||
/* Add node to beginning of list. */
|
||||
newn = (struct stl_normal*)malloc(sizeof(struct stl_normal));
|
||||
if(newn == NULL) perror("stl_fix_normal_directions");
|
||||
newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
|
||||
newn->next = head->next;
|
||||
head->next = newn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* an error occourred, quit the for loop and exit */
|
||||
if (force_exit) break;
|
||||
|
||||
/* Get next facet to fix from top of list. */
|
||||
if(head->next != tail) {
|
||||
facet_num = head->next->facet_num;
|
||||
if(norm_sw[facet_num] != 1) { /* If facet is in list mutiple times */
|
||||
norm_sw[facet_num] = 1; /* Record this one as being fixed. */
|
||||
checked++;
|
||||
}
|
||||
temp = head->next; /* Delete this facet from the list. */
|
||||
head->next = head->next->next;
|
||||
free(temp);
|
||||
} else { /* if we ran out of facets to fix: */
|
||||
/* All of the facets in this part have been fixed. */
|
||||
stl->stats.number_of_parts += 1;
|
||||
if(checked >= stl->stats.number_of_facets) {
|
||||
/* All of the facets have been checked. Bail out. */
|
||||
break;
|
||||
} else {
|
||||
/* There is another part here. Find it and continue. */
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
if(norm_sw[i] == 0) {
|
||||
/* This is the first facet of the next part. */
|
||||
facet_num = i;
|
||||
if(stl_check_normal_vector(stl, i, 0) == 2) {
|
||||
stl_reverse_facet(stl, i);
|
||||
reversed_ids[reversed_count++] = i;
|
||||
}
|
||||
|
||||
norm_sw[facet_num] = 1;
|
||||
checked++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(head);
|
||||
free(tail);
|
||||
free(reversed_ids);
|
||||
free(norm_sw);
|
||||
test_norm *= -1.f;
|
||||
normal_dif = (normal - test_norm).cwiseAbs();
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
// The normal is not within tolerance and backwards.
|
||||
if (normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
++ stl->stats.normals_fixed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
++ stl->stats.normals_fixed;
|
||||
}
|
||||
// Status is unknown.
|
||||
return false;
|
||||
}
|
||||
|
||||
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
|
||||
/* Returns 0 if the normal is within tolerance */
|
||||
/* Returns 1 if the normal is not within tolerance, but direction is OK */
|
||||
/* Returns 2 if the normal is not within tolerance and backwards */
|
||||
/* Returns 4 if the status is unknown. */
|
||||
void stl_fix_normal_directions(stl_file *stl)
|
||||
{
|
||||
// This may happen for malformed models, see: https://github.com/prusa3d/PrusaSlicer/issues/2209
|
||||
if (stl->stats.number_of_facets == 0)
|
||||
return;
|
||||
|
||||
stl_facet *facet;
|
||||
struct stl_normal {
|
||||
int facet_num;
|
||||
stl_normal *next;
|
||||
};
|
||||
|
||||
facet = &stl->facet_start[facet_num];
|
||||
// Initialize linked list.
|
||||
boost::object_pool<stl_normal> pool;
|
||||
stl_normal *head = pool.construct();
|
||||
stl_normal *tail = pool.construct();
|
||||
head->next = tail;
|
||||
tail->next = tail;
|
||||
|
||||
stl_normal normal;
|
||||
stl_calculate_normal(normal, facet);
|
||||
stl_normalize_vector(normal);
|
||||
stl_normal normal_dif = (normal - facet->normal).cwiseAbs();
|
||||
// Initialize list that keeps track of already fixed facets.
|
||||
std::vector<char> norm_sw(stl->stats.number_of_facets, 0);
|
||||
// Initialize list that keeps track of reversed facets.
|
||||
std::vector<int> reversed_ids(stl->stats.number_of_facets, 0);
|
||||
|
||||
const float eps = 0.001f;
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
/* It is not really necessary to change the values here */
|
||||
/* but just for consistency, I will. */
|
||||
facet->normal = normal;
|
||||
return 0;
|
||||
}
|
||||
int facet_num = 0;
|
||||
int reversed_count = 0;
|
||||
// If normal vector is not within tolerance and backwards:
|
||||
// Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
|
||||
// of it being wrong randomly are low if most of the triangles are right:
|
||||
if (check_normal_vector(stl, 0, 0)) {
|
||||
reverse_facet(stl, 0);
|
||||
reversed_ids[reversed_count ++] = 0;
|
||||
}
|
||||
|
||||
stl_normal test_norm = facet->normal;
|
||||
stl_normalize_vector(test_norm);
|
||||
normal_dif = (normal - test_norm).cwiseAbs();
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
if(normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
stl->stats.normals_fixed += 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// Say that we've fixed this facet:
|
||||
norm_sw[facet_num] = 1;
|
||||
int checked = 1;
|
||||
|
||||
test_norm *= -1.f;
|
||||
normal_dif = (normal - test_norm).cwiseAbs();
|
||||
if (normal_dif(0) < eps && normal_dif(1) < eps && normal_dif(2) < eps) {
|
||||
// Facet is backwards.
|
||||
if(normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
stl->stats.normals_fixed += 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
if(normal_fix_flag) {
|
||||
facet->normal = normal;
|
||||
stl->stats.normals_fixed += 1;
|
||||
}
|
||||
return 4;
|
||||
for (;;) {
|
||||
// Add neighbors_to_list. Add unconnected neighbors to the list.
|
||||
bool force_exit = false;
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
// Reverse the neighboring facets if necessary.
|
||||
if (stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
|
||||
// If the facet has a neighbor that is -1, it means that edge isn't shared by another facet
|
||||
if (stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
|
||||
// trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206)
|
||||
for (int id = reversed_count - 1; id >= 0; -- id)
|
||||
reverse_facet(stl, reversed_ids[id]);
|
||||
force_exit = true;
|
||||
break;
|
||||
}
|
||||
reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
|
||||
reversed_ids[reversed_count ++] = stl->neighbors_start[facet_num].neighbor[j];
|
||||
}
|
||||
}
|
||||
// If this edge of the facet is connected:
|
||||
if (stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||
// If we haven't fixed this facet yet, add it to the list:
|
||||
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] != 1) {
|
||||
// Add node to beginning of list.
|
||||
stl_normal *newn = pool.construct();
|
||||
newn->facet_num = stl->neighbors_start[facet_num].neighbor[j];
|
||||
newn->next = head->next;
|
||||
head->next = newn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// an error occourred, quit the for loop and exit
|
||||
if (force_exit)
|
||||
break;
|
||||
|
||||
// Get next facet to fix from top of list.
|
||||
if (head->next != tail) {
|
||||
facet_num = head->next->facet_num;
|
||||
if (norm_sw[facet_num] != 1) { // If facet is in list mutiple times
|
||||
norm_sw[facet_num] = 1; // Record this one as being fixed.
|
||||
++ checked;
|
||||
}
|
||||
stl_normal *temp = head->next; // Delete this facet from the list.
|
||||
head->next = head->next->next;
|
||||
// pool.destroy(temp);
|
||||
} else { // If we ran out of facets to fix: All of the facets in this part have been fixed.
|
||||
++ stl->stats.number_of_parts;
|
||||
if (checked >= stl->stats.number_of_facets)
|
||||
// All of the facets have been checked. Bail out.
|
||||
break;
|
||||
// There is another part here. Find it and continue.
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
if (norm_sw[i] == 0) {
|
||||
// This is the first facet of the next part.
|
||||
facet_num = i;
|
||||
if (check_normal_vector(stl, i, 0)) {
|
||||
reverse_facet(stl, i);
|
||||
reversed_ids[reversed_count++] = i;
|
||||
}
|
||||
norm_sw[facet_num] = 1;
|
||||
++ checked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pool.destroy(head);
|
||||
// pool.destroy(tail);
|
||||
}
|
||||
|
||||
void stl_fix_normal_values(stl_file *stl) {
|
||||
int i;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
stl_check_normal_vector(stl, i, 1);
|
||||
}
|
||||
void stl_fix_normal_values(stl_file *stl)
|
||||
{
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
check_normal_vector(stl, i, 1);
|
||||
}
|
||||
|
||||
void stl_reverse_all_facets(stl_file *stl)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
stl_normal normal;
|
||||
for(int i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
stl_reverse_facet(stl, i);
|
||||
stl_calculate_normal(normal, &stl->facet_start[i]);
|
||||
stl_normalize_vector(normal);
|
||||
stl->facet_start[i].normal = normal;
|
||||
}
|
||||
stl_normal normal;
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
reverse_facet(stl, i);
|
||||
stl_calculate_normal(normal, &stl->facet_start[i]);
|
||||
stl_normalize_vector(normal);
|
||||
stl->facet_start[i].normal = normal;
|
||||
}
|
||||
}
|
||||
|
@ -23,242 +23,237 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
void
|
||||
stl_invalidate_shared_vertices(stl_file *stl) {
|
||||
if (stl->error) return;
|
||||
void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its)
|
||||
{
|
||||
// 3 indices to vertex per face
|
||||
its.indices.assign(stl->stats.number_of_facets, stl_triangle_vertex_indices(-1, -1, -1));
|
||||
// Shared vertices (3D coordinates)
|
||||
its.vertices.clear();
|
||||
its.vertices.reserve(stl->stats.number_of_facets / 2);
|
||||
|
||||
if (stl->v_indices != NULL) {
|
||||
free(stl->v_indices);
|
||||
stl->v_indices = NULL;
|
||||
}
|
||||
if (stl->v_shared != NULL) {
|
||||
free(stl->v_shared);
|
||||
stl->v_shared = NULL;
|
||||
}
|
||||
// A degenerate mesh may contain loops: Traversing a fan will end up in an endless loop
|
||||
// while never reaching the starting face. To avoid these endless loops, traversed faces at each fan traversal
|
||||
// are marked with a unique fan_traversal_stamp.
|
||||
unsigned int fan_traversal_stamp = 0;
|
||||
std::vector<unsigned int> fan_traversal_facet_visited(stl->stats.number_of_facets, 0);
|
||||
|
||||
for (uint32_t facet_idx = 0; facet_idx < stl->stats.number_of_facets; ++ facet_idx) {
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
if (its.indices[facet_idx][j] != -1)
|
||||
// Shared vertex was already assigned.
|
||||
continue;
|
||||
// Create a new shared vertex.
|
||||
its.vertices.emplace_back(stl->facet_start[facet_idx].vertex[j]);
|
||||
// Traverse the fan around the j-th vertex of the i-th face, assign the newly created shared vertex index to all the neighboring triangles in the triangle fan.
|
||||
int facet_in_fan_idx = facet_idx;
|
||||
bool edge_direction = false;
|
||||
bool traversal_reversed = false;
|
||||
int vnot = (j + 2) % 3;
|
||||
// Increase the
|
||||
++ fan_traversal_stamp;
|
||||
for (;;) {
|
||||
// Next edge on facet_in_fan_idx to be traversed. The edge is indexed by its starting vertex index.
|
||||
int next_edge = 0;
|
||||
// Vertex index in facet_in_fan_idx, which is being pivoted around, and which is being assigned a new shared vertex.
|
||||
int pivot_vertex = 0;
|
||||
if (vnot > 2) {
|
||||
// The edge of facet_in_fan_idx opposite to vnot is equally oriented, therefore
|
||||
// the neighboring facet is flipped.
|
||||
if (! edge_direction) {
|
||||
pivot_vertex = (vnot + 2) % 3;
|
||||
next_edge = pivot_vertex;
|
||||
} else {
|
||||
pivot_vertex = (vnot + 1) % 3;
|
||||
next_edge = vnot % 3;
|
||||
}
|
||||
edge_direction = ! edge_direction;
|
||||
} else {
|
||||
// The neighboring facet is correctly oriented.
|
||||
if (! edge_direction) {
|
||||
pivot_vertex = (vnot + 1) % 3;
|
||||
next_edge = vnot;
|
||||
} else {
|
||||
pivot_vertex = (vnot + 2) % 3;
|
||||
next_edge = pivot_vertex;
|
||||
}
|
||||
}
|
||||
its.indices[facet_in_fan_idx][pivot_vertex] = its.vertices.size() - 1;
|
||||
fan_traversal_facet_visited[facet_in_fan_idx] = fan_traversal_stamp;
|
||||
|
||||
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
|
||||
int next_facet = stl->neighbors_start[facet_in_fan_idx].neighbor[next_edge];
|
||||
if (next_facet == -1) {
|
||||
// No neighbor going in the current direction.
|
||||
if (traversal_reversed) {
|
||||
// Went to one limit, then turned back and reached the other limit. Quit the fan traversal.
|
||||
break;
|
||||
} else {
|
||||
// Reached the first limit. Now try to reverse and traverse up to the other limit.
|
||||
edge_direction = true;
|
||||
vnot = (j + 1) % 3;
|
||||
traversal_reversed = true;
|
||||
facet_in_fan_idx = facet_idx;
|
||||
}
|
||||
} else if (next_facet == facet_idx) {
|
||||
// Traversed a closed fan all around.
|
||||
// assert(! traversal_reversed);
|
||||
break;
|
||||
} else if (next_facet >= (int)stl->stats.number_of_facets) {
|
||||
// The mesh is not valid!
|
||||
// assert(false);
|
||||
break;
|
||||
} else if (fan_traversal_facet_visited[next_facet] == fan_traversal_stamp) {
|
||||
// Traversed a closed fan all around, but did not reach the starting face.
|
||||
// This indicates an invalid geometry (non-manifold).
|
||||
//assert(false);
|
||||
break;
|
||||
} else {
|
||||
// Continue traversal.
|
||||
// next_edge is an index of the starting vertex of the edge, not an index of the opposite vertex to the edge!
|
||||
vnot = stl->neighbors_start[facet_in_fan_idx].which_vertex_not[next_edge];
|
||||
facet_in_fan_idx = next_facet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
stl_generate_shared_vertices(stl_file *stl) {
|
||||
int i;
|
||||
int j;
|
||||
int first_facet;
|
||||
int direction;
|
||||
int facet_num;
|
||||
int vnot;
|
||||
int next_edge;
|
||||
int pivot_vertex;
|
||||
int next_facet;
|
||||
int reversed;
|
||||
bool its_write_off(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
/* Open the file */
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
fprintf(fp, "OFF\n");
|
||||
fprintf(fp, "%d %d 0\n", (int)its.vertices.size(), (int)its.indices.size());
|
||||
for (int i = 0; i < its.vertices.size(); ++ i)
|
||||
fprintf(fp, "\t%f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
|
||||
for (uint32_t i = 0; i < its.indices.size(); ++ i)
|
||||
fprintf(fp, "\t3 %d %d %d\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* make sure this function is idempotent and does not leak memory */
|
||||
stl_invalidate_shared_vertices(stl);
|
||||
bool its_write_vrml(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
/* Open the file */
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_vrml: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
stl->v_indices = (v_indices_struct*)
|
||||
calloc(stl->stats.number_of_facets, sizeof(v_indices_struct));
|
||||
if(stl->v_indices == NULL) perror("stl_generate_shared_vertices");
|
||||
stl->v_shared = (stl_vertex*)
|
||||
calloc((stl->stats.number_of_facets / 2), sizeof(stl_vertex));
|
||||
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
|
||||
stl->stats.shared_malloced = stl->stats.number_of_facets / 2;
|
||||
stl->stats.shared_vertices = 0;
|
||||
fprintf(fp, "#VRML V1.0 ascii\n\n");
|
||||
fprintf(fp, "Separator {\n");
|
||||
fprintf(fp, "\tDEF STLShape ShapeHints {\n");
|
||||
fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
|
||||
fprintf(fp, "\t\tfaceType CONVEX\n");
|
||||
fprintf(fp, "\t\tshapeType SOLID\n");
|
||||
fprintf(fp, "\t\tcreaseAngle 0.0\n");
|
||||
fprintf(fp, "\t}\n");
|
||||
fprintf(fp, "\tDEF STLModel Separator {\n");
|
||||
fprintf(fp, "\t\tDEF STLColor Material {\n");
|
||||
fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
|
||||
fprintf(fp, "\t\t\tpoint [\n");
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
stl->v_indices[i].vertex[0] = -1;
|
||||
stl->v_indices[i].vertex[1] = -1;
|
||||
stl->v_indices[i].vertex[2] = -1;
|
||||
}
|
||||
int i = 0;
|
||||
for (; i + 1 < its.vertices.size(); ++ i)
|
||||
fprintf(fp, "\t\t\t\t%f %f %f,\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
|
||||
fprintf(fp, "\t\t\t\t%f %f %f]\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
|
||||
fprintf(fp, "\t\t\tcoordIndex [\n");
|
||||
|
||||
for (size_t i = 0; i + 1 < its.indices.size(); ++ i)
|
||||
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
|
||||
fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", its.indices[i][0], its.indices[i][1], its.indices[i][2]);
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t}\n");
|
||||
fprintf(fp, "}\n");
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool its_write_obj(const indexed_triangle_set &its, const char *file)
|
||||
{
|
||||
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_obj: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < its.vertices.size(); ++ i)
|
||||
fprintf(fp, "v %f %f %f\n", its.vertices[i](0), its.vertices[i](1), its.vertices[i](2));
|
||||
for (size_t i = 0; i < its.indices.size(); ++ i)
|
||||
fprintf(fp, "f %d %d %d\n", its.indices[i][0]+1, its.indices[i][1]+1, its.indices[i][2]+1);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
first_facet = i;
|
||||
for(j = 0; j < 3; j++) {
|
||||
if(stl->v_indices[i].vertex[j] != -1) {
|
||||
continue;
|
||||
}
|
||||
if(stl->stats.shared_vertices == stl->stats.shared_malloced) {
|
||||
stl->stats.shared_malloced += 1024;
|
||||
stl->v_shared = (stl_vertex*)realloc(stl->v_shared,
|
||||
stl->stats.shared_malloced * sizeof(stl_vertex));
|
||||
if(stl->v_shared == NULL) perror("stl_generate_shared_vertices");
|
||||
}
|
||||
// Check validity of the mesh, assert on error.
|
||||
bool stl_validate(const stl_file *stl, const indexed_triangle_set &its)
|
||||
{
|
||||
assert(! stl->facet_start.empty());
|
||||
assert(stl->facet_start.size() == stl->stats.number_of_facets);
|
||||
assert(stl->neighbors_start.size() == stl->stats.number_of_facets);
|
||||
assert(stl->facet_start.size() == stl->neighbors_start.size());
|
||||
assert(! stl->neighbors_start.empty());
|
||||
assert((its.indices.empty()) == (its.vertices.empty()));
|
||||
assert(stl->stats.number_of_facets > 0);
|
||||
assert(its.vertices.empty() || its.indices.size() == stl->stats.number_of_facets);
|
||||
|
||||
stl->v_shared[stl->stats.shared_vertices] =
|
||||
stl->facet_start[i].vertex[j];
|
||||
|
||||
direction = 0;
|
||||
reversed = 0;
|
||||
facet_num = i;
|
||||
vnot = (j + 2) % 3;
|
||||
|
||||
for(;;) {
|
||||
if(vnot > 2) {
|
||||
if(direction == 0) {
|
||||
pivot_vertex = (vnot + 2) % 3;
|
||||
next_edge = pivot_vertex;
|
||||
direction = 1;
|
||||
} else {
|
||||
pivot_vertex = (vnot + 1) % 3;
|
||||
next_edge = vnot % 3;
|
||||
direction = 0;
|
||||
}
|
||||
} else {
|
||||
if(direction == 0) {
|
||||
pivot_vertex = (vnot + 1) % 3;
|
||||
next_edge = vnot;
|
||||
} else {
|
||||
pivot_vertex = (vnot + 2) % 3;
|
||||
next_edge = pivot_vertex;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
// Verify validity of neighborship data.
|
||||
for (int facet_idx = 0; facet_idx < (int)stl->stats.number_of_facets; ++ facet_idx) {
|
||||
const stl_neighbors &nbr = stl->neighbors_start[facet_idx];
|
||||
const int *vertices = its.indices.empty() ? nullptr : its.indices[facet_idx].data();
|
||||
for (int nbr_idx = 0; nbr_idx < 3; ++ nbr_idx) {
|
||||
int nbr_face = stl->neighbors_start[facet_idx].neighbor[nbr_idx];
|
||||
assert(nbr_face < (int)stl->stats.number_of_facets);
|
||||
if (nbr_face != -1) {
|
||||
int nbr_vnot = nbr.which_vertex_not[nbr_idx];
|
||||
assert(nbr_vnot >= 0 && nbr_vnot < 6);
|
||||
// Neighbor of the neighbor is the original face.
|
||||
assert(stl->neighbors_start[nbr_face].neighbor[(nbr_vnot + 1) % 3] == facet_idx);
|
||||
int vnot_back = stl->neighbors_start[nbr_face].which_vertex_not[(nbr_vnot + 1) % 3];
|
||||
assert(vnot_back >= 0 && vnot_back < 6);
|
||||
assert((nbr_vnot < 3) == (vnot_back < 3));
|
||||
assert(vnot_back % 3 == (nbr_idx + 2) % 3);
|
||||
if (vertices != nullptr) {
|
||||
// Has shared vertices.
|
||||
if (nbr_vnot < 3) {
|
||||
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are correctly oriented.
|
||||
assert((its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[nbr_idx]));
|
||||
} else {
|
||||
// Faces facet_idx and nbr_face share two vertices accross the common edge. Faces are incorrectly oriented, one of them is flipped.
|
||||
assert((its.indices[nbr_face][(nbr_vnot + 2) % 3] == vertices[(nbr_idx + 1) % 3] && its.indices[nbr_face][(nbr_vnot + 1) % 3] == vertices[nbr_idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stl->v_indices[facet_num].vertex[pivot_vertex] =
|
||||
stl->stats.shared_vertices;
|
||||
|
||||
next_facet = stl->neighbors_start[facet_num].neighbor[next_edge];
|
||||
if(next_facet == -1) {
|
||||
if(reversed) {
|
||||
break;
|
||||
} else {
|
||||
direction = 1;
|
||||
vnot = (j + 1) % 3;
|
||||
reversed = 1;
|
||||
facet_num = first_facet;
|
||||
}
|
||||
} else if(next_facet != first_facet) {
|
||||
vnot = stl->neighbors_start[facet_num].
|
||||
which_vertex_not[next_edge];
|
||||
facet_num = next_facet;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
stl->stats.shared_vertices += 1;
|
||||
}
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_off(stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "OFF\n");
|
||||
fprintf(fp, "%d %d 0\n",
|
||||
stl->stats.shared_vertices, stl->stats.number_of_facets);
|
||||
|
||||
for(i = 0; i < stl->stats.shared_vertices; i++) {
|
||||
fprintf(fp, "\t%f %f %f\n",
|
||||
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
|
||||
}
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
fprintf(fp, "\t3 %d %d %d\n", stl->v_indices[i].vertex[0],
|
||||
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_vrml(stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "#VRML V1.0 ascii\n\n");
|
||||
fprintf(fp, "Separator {\n");
|
||||
fprintf(fp, "\tDEF STLShape ShapeHints {\n");
|
||||
fprintf(fp, "\t\tvertexOrdering COUNTERCLOCKWISE\n");
|
||||
fprintf(fp, "\t\tfaceType CONVEX\n");
|
||||
fprintf(fp, "\t\tshapeType SOLID\n");
|
||||
fprintf(fp, "\t\tcreaseAngle 0.0\n");
|
||||
fprintf(fp, "\t}\n");
|
||||
fprintf(fp, "\tDEF STLModel Separator {\n");
|
||||
fprintf(fp, "\t\tDEF STLColor Material {\n");
|
||||
fprintf(fp, "\t\t\temissiveColor 0.700000 0.700000 0.000000\n");
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t\tDEF STLVertices Coordinate3 {\n");
|
||||
fprintf(fp, "\t\t\tpoint [\n");
|
||||
|
||||
for(i = 0; i < (stl->stats.shared_vertices - 1); i++) {
|
||||
fprintf(fp, "\t\t\t\t%f %f %f,\n",
|
||||
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
|
||||
}
|
||||
fprintf(fp, "\t\t\t\t%f %f %f]\n",
|
||||
stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t\tDEF STLTriangles IndexedFaceSet {\n");
|
||||
fprintf(fp, "\t\t\tcoordIndex [\n");
|
||||
|
||||
for(i = 0; i < (stl->stats.number_of_facets - 1); i++) {
|
||||
fprintf(fp, "\t\t\t\t%d, %d, %d, -1,\n", stl->v_indices[i].vertex[0],
|
||||
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
|
||||
}
|
||||
fprintf(fp, "\t\t\t\t%d, %d, %d, -1]\n", stl->v_indices[i].vertex[0],
|
||||
stl->v_indices[i].vertex[1], stl->v_indices[i].vertex[2]);
|
||||
fprintf(fp, "\t\t}\n");
|
||||
fprintf(fp, "\t}\n");
|
||||
fprintf(fp, "}\n");
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void stl_write_obj (stl_file *stl, const char *file) {
|
||||
int i;
|
||||
FILE* fp;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == NULL) {
|
||||
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < stl->stats.shared_vertices; i++) {
|
||||
fprintf(fp, "v %f %f %f\n", stl->v_shared[i](0), stl->v_shared[i](1), stl->v_shared[i](2));
|
||||
}
|
||||
for (i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
fprintf(fp, "f %d %d %d\n", stl->v_indices[i].vertex[0]+1, stl->v_indices[i].vertex[1]+1, stl->v_indices[i].vertex[2]+1);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
// Check validity of the mesh, assert on error.
|
||||
bool stl_validate(const stl_file *stl)
|
||||
{
|
||||
indexed_triangle_set its;
|
||||
return stl_validate(stl, its);
|
||||
}
|
||||
|
287
src/admesh/stl.h
287
src/admesh/stl.h
@ -27,6 +27,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
// Size of the binary STL header, free form.
|
||||
@ -40,22 +41,23 @@
|
||||
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_vertex;
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> stl_normal;
|
||||
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> stl_triangle_vertex_indices;
|
||||
static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect");
|
||||
static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect");
|
||||
|
||||
struct stl_facet {
|
||||
stl_normal normal;
|
||||
stl_vertex vertex[3];
|
||||
char extra[2];
|
||||
stl_normal normal;
|
||||
stl_vertex vertex[3];
|
||||
char extra[2];
|
||||
|
||||
stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) {
|
||||
stl_facet out;
|
||||
out.normal = rot * this->normal;
|
||||
out.vertex[0] = rot * this->vertex[0];
|
||||
out.vertex[1] = rot * this->vertex[1];
|
||||
out.vertex[2] = rot * this->vertex[2];
|
||||
return out;
|
||||
}
|
||||
stl_facet rotated(const Eigen::Quaternion<float, Eigen::DontAlign> &rot) const {
|
||||
stl_facet out;
|
||||
out.normal = rot * this->normal;
|
||||
out.vertex[0] = rot * this->vertex[0];
|
||||
out.vertex[1] = rot * this->vertex[1];
|
||||
out.vertex[2] = rot * this->vertex[2];
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#define SIZEOF_STL_FACET 50
|
||||
@ -67,104 +69,94 @@ static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrec
|
||||
|
||||
typedef enum {binary, ascii, inmemory} stl_type;
|
||||
|
||||
typedef struct {
|
||||
stl_vertex p1;
|
||||
stl_vertex p2;
|
||||
int facet_number;
|
||||
} stl_edge;
|
||||
struct stl_neighbors {
|
||||
stl_neighbors() { reset(); }
|
||||
void reset() {
|
||||
neighbor[0] = -1;
|
||||
neighbor[1] = -1;
|
||||
neighbor[2] = -1;
|
||||
which_vertex_not[0] = -1;
|
||||
which_vertex_not[1] = -1;
|
||||
which_vertex_not[2] = -1;
|
||||
}
|
||||
int num_neighbors_missing() const { return (this->neighbor[0] == -1) + (this->neighbor[1] == -1) + (this->neighbor[2] == -1); }
|
||||
int num_neighbors() const { return 3 - this->num_neighbors_missing(); }
|
||||
|
||||
typedef struct stl_hash_edge {
|
||||
// Key of a hash edge: sorted vertices of the edge.
|
||||
uint32_t key[6];
|
||||
// Compare two keys.
|
||||
bool operator==(const stl_hash_edge &rhs) { return memcmp(key, rhs.key, sizeof(key)) == 0; }
|
||||
bool operator!=(const stl_hash_edge &rhs) { return ! (*this == rhs); }
|
||||
int hash(int M) const { return ((key[0] / 11 + key[1] / 7 + key[2] / 3) ^ (key[3] / 11 + key[4] / 7 + key[5] / 3)) % M; }
|
||||
// Index of a facet owning this edge.
|
||||
int facet_number;
|
||||
// Index of this edge inside the facet with an index of facet_number.
|
||||
// If this edge is stored backwards, which_edge is increased by 3.
|
||||
int which_edge;
|
||||
struct stl_hash_edge *next;
|
||||
} stl_hash_edge;
|
||||
// Index of a neighbor facet.
|
||||
int neighbor[3];
|
||||
// Index of an opposite vertex at the neighbor face.
|
||||
char which_vertex_not[3];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
// Index of a neighbor facet.
|
||||
int neighbor[3];
|
||||
// Index of an opposite vertex at the neighbor face.
|
||||
char which_vertex_not[3];
|
||||
} stl_neighbors;
|
||||
struct stl_stats {
|
||||
stl_stats() { this->reset(); }
|
||||
void reset() { memset(this, 0, sizeof(stl_stats)); this->volume = -1.0; }
|
||||
char header[81];
|
||||
stl_type type;
|
||||
uint32_t number_of_facets;
|
||||
stl_vertex max;
|
||||
stl_vertex min;
|
||||
stl_vertex size;
|
||||
float bounding_diameter;
|
||||
float shortest_edge;
|
||||
float volume;
|
||||
int connected_edges;
|
||||
int connected_facets_1_edge;
|
||||
int connected_facets_2_edge;
|
||||
int connected_facets_3_edge;
|
||||
int facets_w_1_bad_edge;
|
||||
int facets_w_2_bad_edge;
|
||||
int facets_w_3_bad_edge;
|
||||
int original_num_facets;
|
||||
int edges_fixed;
|
||||
int degenerate_facets;
|
||||
int facets_removed;
|
||||
int facets_added;
|
||||
int facets_reversed;
|
||||
int backwards_edges;
|
||||
int normals_fixed;
|
||||
int number_of_parts;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int vertex[3];
|
||||
} v_indices_struct;
|
||||
struct stl_file {
|
||||
stl_file() {}
|
||||
|
||||
typedef struct {
|
||||
char header[81];
|
||||
stl_type type;
|
||||
uint32_t number_of_facets;
|
||||
stl_vertex max;
|
||||
stl_vertex min;
|
||||
stl_vertex size;
|
||||
float bounding_diameter;
|
||||
float shortest_edge;
|
||||
float volume;
|
||||
unsigned number_of_blocks;
|
||||
int connected_edges;
|
||||
int connected_facets_1_edge;
|
||||
int connected_facets_2_edge;
|
||||
int connected_facets_3_edge;
|
||||
int facets_w_1_bad_edge;
|
||||
int facets_w_2_bad_edge;
|
||||
int facets_w_3_bad_edge;
|
||||
int original_num_facets;
|
||||
int edges_fixed;
|
||||
int degenerate_facets;
|
||||
int facets_removed;
|
||||
int facets_added;
|
||||
int facets_reversed;
|
||||
int backwards_edges;
|
||||
int normals_fixed;
|
||||
int number_of_parts;
|
||||
int malloced;
|
||||
int freed;
|
||||
int facets_malloced;
|
||||
int collisions;
|
||||
int shared_vertices;
|
||||
int shared_malloced;
|
||||
} stl_stats;
|
||||
void clear() {
|
||||
this->facet_start.clear();
|
||||
this->neighbors_start.clear();
|
||||
this->stats.reset();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
FILE *fp;
|
||||
stl_facet *facet_start;
|
||||
stl_hash_edge **heads;
|
||||
stl_hash_edge *tail;
|
||||
int M;
|
||||
stl_neighbors *neighbors_start;
|
||||
v_indices_struct *v_indices;
|
||||
stl_vertex *v_shared;
|
||||
stl_stats stats;
|
||||
char error;
|
||||
} stl_file;
|
||||
std::vector<stl_facet> facet_start;
|
||||
std::vector<stl_neighbors> neighbors_start;
|
||||
// Statistics
|
||||
stl_stats stats;
|
||||
};
|
||||
|
||||
struct indexed_triangle_set
|
||||
{
|
||||
indexed_triangle_set() {}
|
||||
|
||||
extern void stl_open(stl_file *stl, const char *file);
|
||||
extern void stl_close(stl_file *stl);
|
||||
void clear() { indices.clear(); vertices.clear(); }
|
||||
|
||||
std::vector<stl_triangle_vertex_indices> indices;
|
||||
std::vector<stl_vertex> vertices;
|
||||
//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
|
||||
//std::vector<stl_normal> normals
|
||||
};
|
||||
|
||||
extern bool stl_open(stl_file *stl, const char *file);
|
||||
extern void stl_stats_out(stl_file *stl, FILE *file, char *input_file);
|
||||
extern void stl_print_neighbors(stl_file *stl, char *file);
|
||||
extern void stl_put_little_int(FILE *fp, int value_in);
|
||||
extern void stl_put_little_float(FILE *fp, float value_in);
|
||||
extern void stl_write_ascii(stl_file *stl, const char *file, const char *label);
|
||||
extern void stl_write_binary(stl_file *stl, const char *file, const char *label);
|
||||
extern void stl_write_binary_block(stl_file *stl, FILE *fp);
|
||||
extern bool stl_print_neighbors(stl_file *stl, char *file);
|
||||
extern bool stl_write_ascii(stl_file *stl, const char *file, const char *label);
|
||||
extern bool stl_write_binary(stl_file *stl, const char *file, const char *label);
|
||||
extern void stl_check_facets_exact(stl_file *stl);
|
||||
extern void stl_check_facets_nearby(stl_file *stl, float tolerance);
|
||||
extern void stl_remove_unconnected_facets(stl_file *stl);
|
||||
extern void stl_write_vertex(stl_file *stl, int facet, int vertex);
|
||||
extern void stl_write_facet(stl_file *stl, char *label, int facet);
|
||||
extern void stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge);
|
||||
extern void stl_write_neighbor(stl_file *stl, int facet);
|
||||
extern void stl_write_quad_object(stl_file *stl, char *file);
|
||||
extern bool stl_write_quad_object(stl_file *stl, char *file);
|
||||
extern void stl_verify_neighbors(stl_file *stl);
|
||||
extern void stl_fill_holes(stl_file *stl);
|
||||
extern void stl_fix_normal_directions(stl_file *stl);
|
||||
@ -186,36 +178,30 @@ extern void stl_get_size(stl_file *stl);
|
||||
template<typename T>
|
||||
extern void stl_transform(stl_file *stl, T *trafo3x4)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
|
||||
stl_facet &face = stl->facet_start[i_face];
|
||||
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
|
||||
stl_vertex &v_dst = face.vertex[i_vertex];
|
||||
stl_vertex v_src = v_dst;
|
||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
|
||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
||||
}
|
||||
stl_vertex &v_dst = face.normal;
|
||||
stl_vertex v_src = v_dst;
|
||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2));
|
||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2));
|
||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2));
|
||||
}
|
||||
|
||||
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
|
||||
stl_facet &face = stl->facet_start[i_face];
|
||||
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
|
||||
stl_vertex &v_dst = face.vertex[i_vertex];
|
||||
stl_vertex v_src = v_dst;
|
||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
|
||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
||||
}
|
||||
stl_vertex &v_dst = face.normal;
|
||||
stl_vertex v_src = v_dst;
|
||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2));
|
||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2));
|
||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2));
|
||||
}
|
||||
|
||||
stl_get_size(stl);
|
||||
stl_get_size(stl);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
|
||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++i) {
|
||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
stl_facet &f = stl->facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
||||
@ -228,10 +214,7 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Aff
|
||||
template<typename T>
|
||||
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++i) {
|
||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
stl_facet &f = stl->facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++j)
|
||||
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
||||
@ -241,13 +224,43 @@ inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::Don
|
||||
stl_get_size(stl);
|
||||
}
|
||||
|
||||
extern void stl_open_merge(stl_file *stl, char *file);
|
||||
extern void stl_invalidate_shared_vertices(stl_file *stl);
|
||||
extern void stl_generate_shared_vertices(stl_file *stl);
|
||||
extern void stl_write_obj(stl_file *stl, const char *file);
|
||||
extern void stl_write_off(stl_file *stl, const char *file);
|
||||
extern void stl_write_dxf(stl_file *stl, const char *file, char *label);
|
||||
extern void stl_write_vrml(stl_file *stl, const char *file);
|
||||
|
||||
template<typename T>
|
||||
extern void its_transform(indexed_triangle_set &its, T *trafo3x4)
|
||||
{
|
||||
for (stl_vertex &v_dst : its.vertices) {
|
||||
stl_vertex v_src = v_dst;
|
||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2) + trafo3x4[3]);
|
||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
|
||||
{
|
||||
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
|
||||
for (stl_vertex &v : its.vertices)
|
||||
v = (t * v.template cast<T>()).template cast<float>().eval();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void its_transform(indexed_triangle_set &its, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
|
||||
{
|
||||
for (stl_vertex &v : its.vertices)
|
||||
v = (m * v.template cast<T>()).template cast<float>().eval();
|
||||
}
|
||||
|
||||
extern void its_rotate_x(indexed_triangle_set &its, float angle);
|
||||
extern void its_rotate_y(indexed_triangle_set &its, float angle);
|
||||
extern void its_rotate_z(indexed_triangle_set &its, float angle);
|
||||
|
||||
extern void stl_generate_shared_vertices(stl_file *stl, indexed_triangle_set &its);
|
||||
extern bool its_write_obj(const indexed_triangle_set &its, const char *file);
|
||||
extern bool its_write_off(const indexed_triangle_set &its, const char *file);
|
||||
extern bool its_write_vrml(const indexed_triangle_set &its, const char *file);
|
||||
|
||||
extern bool stl_write_dxf(stl_file *stl, const char *file, char *label);
|
||||
inline void stl_calculate_normal(stl_normal &normal, stl_facet *facet) {
|
||||
normal = (facet->vertex[1] - facet->vertex[0]).cross(facet->vertex[2] - facet->vertex[0]);
|
||||
}
|
||||
@ -258,24 +271,18 @@ inline void stl_normalize_vector(stl_normal &normal) {
|
||||
else
|
||||
normal *= float(1.0 / length);
|
||||
}
|
||||
inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
|
||||
return (a(0) != b(0)) ? (a(0) < b(0)) :
|
||||
((a(1) != b(1)) ? (a(1) < b(1)) : (a(2) < b(2)));
|
||||
}
|
||||
extern void stl_calculate_volume(stl_file *stl);
|
||||
|
||||
extern void stl_repair(stl_file *stl, int fixall_flag, int exact_flag, int tolerance_flag, float tolerance, int increment_flag, float increment, int nearby_flag, int iterations, int remove_unconnected_flag, int fill_holes_flag, int normal_directions_flag, int normal_values_flag, int reverse_all_flag, int verbose_flag);
|
||||
extern void stl_repair(stl_file *stl, bool fixall_flag, bool exact_flag, bool tolerance_flag, float tolerance, bool increment_flag, float increment, bool nearby_flag, int iterations, bool remove_unconnected_flag, bool fill_holes_flag, bool normal_directions_flag, bool normal_values_flag, bool reverse_all_flag, bool verbose_flag);
|
||||
|
||||
extern void stl_initialize(stl_file *stl);
|
||||
extern void stl_count_facets(stl_file *stl, const char *file);
|
||||
extern void stl_allocate(stl_file *stl);
|
||||
extern void stl_read(stl_file *stl, int first_facet, bool first);
|
||||
extern void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first);
|
||||
extern void stl_reallocate(stl_file *stl);
|
||||
extern void stl_add_facet(stl_file *stl, stl_facet *new_facet);
|
||||
extern void stl_add_facet(stl_file *stl, const stl_facet *new_facet);
|
||||
|
||||
extern void stl_clear_error(stl_file *stl);
|
||||
extern int stl_get_error(stl_file *stl);
|
||||
extern void stl_exit_on_error(stl_file *stl);
|
||||
// Validate the mesh, assert on error.
|
||||
extern bool stl_validate(const stl_file *stl);
|
||||
extern bool stl_validate(const stl_file *stl, const indexed_triangle_set &its);
|
||||
|
||||
#endif
|
||||
|
@ -22,159 +22,86 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/predef/other/endian.h>
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/detail/endian.hpp>
|
||||
|
||||
#if !defined(SEEK_SET)
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
#endif
|
||||
|
||||
void
|
||||
stl_stats_out(stl_file *stl, FILE *file, char *input_file) {
|
||||
if (stl->error) return;
|
||||
|
||||
/* this is here for Slic3r, without our config.h
|
||||
it won't use this part of the code anyway */
|
||||
void stl_stats_out(stl_file *stl, FILE *file, char *input_file)
|
||||
{
|
||||
// This is here for Slic3r, without our config.h it won't use this part of the code anyway.
|
||||
#ifndef VERSION
|
||||
#define VERSION "unknown"
|
||||
#endif
|
||||
fprintf(file, "\n\
|
||||
================= Results produced by ADMesh version " VERSION " ================\n");
|
||||
fprintf(file, "\
|
||||
Input file : %s\n", input_file);
|
||||
if(stl->stats.type == binary) {
|
||||
fprintf(file, "\
|
||||
File type : Binary STL file\n");
|
||||
} else {
|
||||
fprintf(file, "\
|
||||
File type : ASCII STL file\n");
|
||||
}
|
||||
fprintf(file, "\
|
||||
Header : %s\n", stl->stats.header);
|
||||
fprintf(file, "============== Size ==============\n");
|
||||
fprintf(file, "Min X = % f, Max X = % f\n",
|
||||
stl->stats.min(0), stl->stats.max(0));
|
||||
fprintf(file, "Min Y = % f, Max Y = % f\n",
|
||||
stl->stats.min(1), stl->stats.max(1));
|
||||
fprintf(file, "Min Z = % f, Max Z = % f\n",
|
||||
stl->stats.min(2), stl->stats.max(2));
|
||||
|
||||
fprintf(file, "\
|
||||
========= Facet Status ========== Original ============ Final ====\n");
|
||||
fprintf(file, "\
|
||||
Number of facets : %5d %5d\n",
|
||||
stl->stats.original_num_facets, stl->stats.number_of_facets);
|
||||
fprintf(file, "\
|
||||
Facets with 1 disconnected edge : %5d %5d\n",
|
||||
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge -
|
||||
stl->stats.connected_facets_3_edge);
|
||||
fprintf(file, "\
|
||||
Facets with 2 disconnected edges : %5d %5d\n",
|
||||
stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge -
|
||||
stl->stats.connected_facets_2_edge);
|
||||
fprintf(file, "\
|
||||
Facets with 3 disconnected edges : %5d %5d\n",
|
||||
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
|
||||
stl->stats.connected_facets_1_edge);
|
||||
fprintf(file, "\
|
||||
Total disconnected facets : %5d %5d\n",
|
||||
stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge +
|
||||
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets -
|
||||
stl->stats.connected_facets_3_edge);
|
||||
|
||||
fprintf(file,
|
||||
"=== Processing Statistics === ===== Other Statistics =====\n");
|
||||
fprintf(file, "\
|
||||
Number of parts : %5d Volume : % f\n",
|
||||
stl->stats.number_of_parts, stl->stats.volume);
|
||||
fprintf(file, "\
|
||||
Degenerate facets : %5d\n", stl->stats.degenerate_facets);
|
||||
fprintf(file, "\
|
||||
Edges fixed : %5d\n", stl->stats.edges_fixed);
|
||||
fprintf(file, "\
|
||||
Facets removed : %5d\n", stl->stats.facets_removed);
|
||||
fprintf(file, "\
|
||||
Facets added : %5d\n", stl->stats.facets_added);
|
||||
fprintf(file, "\
|
||||
Facets reversed : %5d\n", stl->stats.facets_reversed);
|
||||
fprintf(file, "\
|
||||
Backwards edges : %5d\n", stl->stats.backwards_edges);
|
||||
fprintf(file, "\
|
||||
Normals fixed : %5d\n", stl->stats.normals_fixed);
|
||||
fprintf(file, "\n================= Results produced by ADMesh version " VERSION " ================\n");
|
||||
fprintf(file, "Input file : %s\n", input_file);
|
||||
if (stl->stats.type == binary)
|
||||
fprintf(file, "File type : Binary STL file\n");
|
||||
else
|
||||
fprintf(file, "File type : ASCII STL file\n");
|
||||
fprintf(file, "Header : %s\n", stl->stats.header);
|
||||
fprintf(file, "============== Size ==============\n");
|
||||
fprintf(file, "Min X = % f, Max X = % f\n", stl->stats.min(0), stl->stats.max(0));
|
||||
fprintf(file, "Min Y = % f, Max Y = % f\n", stl->stats.min(1), stl->stats.max(1));
|
||||
fprintf(file, "Min Z = % f, Max Z = % f\n", stl->stats.min(2), stl->stats.max(2));
|
||||
fprintf(file, "========= Facet Status ========== Original ============ Final ====\n");
|
||||
fprintf(file, "Number of facets : %5d %5d\n", stl->stats.original_num_facets, stl->stats.number_of_facets);
|
||||
fprintf(file, "Facets with 1 disconnected edge : %5d %5d\n",
|
||||
stl->stats.facets_w_1_bad_edge, stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge);
|
||||
fprintf(file, "Facets with 2 disconnected edges : %5d %5d\n",
|
||||
stl->stats.facets_w_2_bad_edge, stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge);
|
||||
fprintf(file, "Facets with 3 disconnected edges : %5d %5d\n",
|
||||
stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_1_edge);
|
||||
fprintf(file, "Total disconnected facets : %5d %5d\n",
|
||||
stl->stats.facets_w_1_bad_edge + stl->stats.facets_w_2_bad_edge + stl->stats.facets_w_3_bad_edge, stl->stats.number_of_facets - stl->stats.connected_facets_3_edge);
|
||||
fprintf(file, "=== Processing Statistics === ===== Other Statistics =====\n");
|
||||
fprintf(file, "Number of parts : %5d Volume : %f\n", stl->stats.number_of_parts, stl->stats.volume);
|
||||
fprintf(file, "Degenerate facets : %5d\n", stl->stats.degenerate_facets);
|
||||
fprintf(file, "Edges fixed : %5d\n", stl->stats.edges_fixed);
|
||||
fprintf(file, "Facets removed : %5d\n", stl->stats.facets_removed);
|
||||
fprintf(file, "Facets added : %5d\n", stl->stats.facets_added);
|
||||
fprintf(file, "Facets reversed : %5d\n", stl->stats.facets_reversed);
|
||||
fprintf(file, "Backwards edges : %5d\n", stl->stats.backwards_edges);
|
||||
fprintf(file, "Normals fixed : %5d\n", stl->stats.normals_fixed);
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_ascii(stl_file *stl, const char *file, const char *label) {
|
||||
int i;
|
||||
char *error_msg;
|
||||
bool stl_write_ascii(stl_file *stl, const char *file, const char *label)
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_ascii: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
fprintf(fp, "solid %s\n", label);
|
||||
|
||||
/* Open the file */
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
fprintf(fp, " facet normal % .8E % .8E % .8E\n", stl->facet_start[i].normal(0), stl->facet_start[i].normal(1), stl->facet_start[i].normal(2));
|
||||
fprintf(fp, " outer loop\n");
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2));
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2));
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
|
||||
fprintf(fp, " endloop\n");
|
||||
fprintf(fp, " endfacet\n");
|
||||
}
|
||||
|
||||
fprintf(fp, "solid %s\n", label);
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
fprintf(fp, " facet normal % .8E % .8E % .8E\n",
|
||||
stl->facet_start[i].normal(0), stl->facet_start[i].normal(1),
|
||||
stl->facet_start[i].normal(2));
|
||||
fprintf(fp, " outer loop\n");
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n",
|
||||
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
|
||||
stl->facet_start[i].vertex[0](2));
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n",
|
||||
stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
|
||||
stl->facet_start[i].vertex[1](2));
|
||||
fprintf(fp, " vertex % .8E % .8E % .8E\n",
|
||||
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
|
||||
stl->facet_start[i].vertex[2](2));
|
||||
fprintf(fp, " endloop\n");
|
||||
fprintf(fp, " endfacet\n");
|
||||
}
|
||||
|
||||
fprintf(fp, "endsolid %s\n", label);
|
||||
|
||||
fclose(fp);
|
||||
fprintf(fp, "endsolid %s\n", label);
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
stl_print_neighbors(stl_file *stl, char *file) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
bool stl_print_neighbors(stl_file *stl, char *file)
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_print_neighbors: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_print_neighbors: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
fprintf(fp, "%d, %d,%d, %d,%d, %d,%d\n",
|
||||
i,
|
||||
stl->neighbors_start[i].neighbor[0],
|
||||
(int)stl->neighbors_start[i].which_vertex_not[0],
|
||||
@ -182,234 +109,142 @@ stl_print_neighbors(stl_file *stl, char *file) {
|
||||
(int)stl->neighbors_start[i].which_vertex_not[1],
|
||||
stl->neighbors_start[i].neighbor[2],
|
||||
(int)stl->neighbors_start[i].which_vertex_not[2]);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
#if BOOST_ENDIAN_BIG_BYTE
|
||||
// Swap a buffer of 32bit data from little endian to big endian and vice versa.
|
||||
void stl_internal_reverse_quads(char *buf, size_t cnt)
|
||||
{
|
||||
for (size_t i = 0; i < cnt; i += 4) {
|
||||
std::swap(buf[i], buf[i+3]);
|
||||
std::swap(buf[i+1], buf[i+2]);
|
||||
}
|
||||
for (size_t i = 0; i < cnt; i += 4) {
|
||||
std::swap(buf[i], buf[i+3]);
|
||||
std::swap(buf[i+1], buf[i+2]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
stl_write_binary(stl_file *stl, const char *file, const char *label) {
|
||||
FILE *fp;
|
||||
int i;
|
||||
char *error_msg;
|
||||
bool stl_write_binary(stl_file *stl, const char *file, const char *label)
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(file, "wb");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_binary: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
fprintf(fp, "%s", label);
|
||||
for (size_t i = strlen(label); i < LABEL_SIZE; ++ i)
|
||||
putc(0, fp);
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "wb");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_binary: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "%s", label);
|
||||
for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp);
|
||||
|
||||
fseek(fp, LABEL_SIZE, SEEK_SET);
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
fwrite(&stl->stats.number_of_facets, 4, 1, fp);
|
||||
for (i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp);
|
||||
#else /* BOOST_LITTLE_ENDIAN */
|
||||
char buffer[50];
|
||||
// Convert the number of facets to little endian.
|
||||
memcpy(buffer, &stl->stats.number_of_facets, 4);
|
||||
stl_internal_reverse_quads(buffer, 4);
|
||||
fwrite(buffer, 4, 1, fp);
|
||||
for (i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
memcpy(buffer, stl->facet_start + i, 50);
|
||||
// Convert to little endian.
|
||||
stl_internal_reverse_quads(buffer, 48);
|
||||
fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
|
||||
}
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
fclose(fp);
|
||||
#if !defined(SEEK_SET)
|
||||
#define SEEK_SET 0
|
||||
#endif
|
||||
fseek(fp, LABEL_SIZE, SEEK_SET);
|
||||
#if BOOST_ENDIAN_LITTLE_BYTE
|
||||
fwrite(&stl->stats.number_of_facets, 4, 1, fp);
|
||||
for (const stl_facet &facet : stl->facet_start)
|
||||
fwrite(&facet, SIZEOF_STL_FACET, 1, fp);
|
||||
#else /* BOOST_ENDIAN_LITTLE_BYTE */
|
||||
char buffer[50];
|
||||
// Convert the number of facets to little endian.
|
||||
memcpy(buffer, &stl->stats.number_of_facets, 4);
|
||||
stl_internal_reverse_quads(buffer, 4);
|
||||
fwrite(buffer, 4, 1, fp);
|
||||
for (i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
memcpy(buffer, stl->facet_start + i, 50);
|
||||
// Convert to little endian.
|
||||
stl_internal_reverse_quads(buffer, 48);
|
||||
fwrite(buffer, SIZEOF_STL_FACET, 1, fp);
|
||||
}
|
||||
#endif /* BOOST_ENDIAN_LITTLE_BYTE */
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_vertex(stl_file *stl, int facet, int vertex) {
|
||||
if (stl->error) return;
|
||||
printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
|
||||
void stl_write_vertex(stl_file *stl, int facet, int vertex)
|
||||
{
|
||||
printf(" vertex %d/%d % .8E % .8E % .8E\n", vertex, facet,
|
||||
stl->facet_start[facet].vertex[vertex](0),
|
||||
stl->facet_start[facet].vertex[vertex](1),
|
||||
stl->facet_start[facet].vertex[vertex](2));
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_facet(stl_file *stl, char *label, int facet) {
|
||||
if (stl->error) return;
|
||||
printf("facet (%d)/ %s\n", facet, label);
|
||||
stl_write_vertex(stl, facet, 0);
|
||||
stl_write_vertex(stl, facet, 1);
|
||||
stl_write_vertex(stl, facet, 2);
|
||||
void stl_write_facet(stl_file *stl, char *label, int facet)
|
||||
{
|
||||
printf("facet (%d)/ %s\n", facet, label);
|
||||
stl_write_vertex(stl, facet, 0);
|
||||
stl_write_vertex(stl, facet, 1);
|
||||
stl_write_vertex(stl, facet, 2);
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_edge(stl_file *stl, char *label, stl_hash_edge edge) {
|
||||
if (stl->error) return;
|
||||
printf("edge (%d)/(%d) %s\n", edge.facet_number, edge.which_edge, label);
|
||||
if(edge.which_edge < 3) {
|
||||
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
|
||||
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
|
||||
} else {
|
||||
stl_write_vertex(stl, edge.facet_number, (edge.which_edge + 1) % 3);
|
||||
stl_write_vertex(stl, edge.facet_number, edge.which_edge % 3);
|
||||
}
|
||||
void stl_write_neighbor(stl_file *stl, int facet)
|
||||
{
|
||||
printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
|
||||
stl->neighbors_start[facet].neighbor[0],
|
||||
stl->neighbors_start[facet].neighbor[1],
|
||||
stl->neighbors_start[facet].neighbor[2],
|
||||
stl->neighbors_start[facet].which_vertex_not[0],
|
||||
stl->neighbors_start[facet].which_vertex_not[1],
|
||||
stl->neighbors_start[facet].which_vertex_not[2]);
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_neighbor(stl_file *stl, int facet) {
|
||||
if (stl->error) return;
|
||||
printf("Neighbors %d: %d, %d, %d ; %d, %d, %d\n", facet,
|
||||
stl->neighbors_start[facet].neighbor[0],
|
||||
stl->neighbors_start[facet].neighbor[1],
|
||||
stl->neighbors_start[facet].neighbor[2],
|
||||
stl->neighbors_start[facet].which_vertex_not[0],
|
||||
stl->neighbors_start[facet].which_vertex_not[1],
|
||||
stl->neighbors_start[facet].which_vertex_not[2]);
|
||||
}
|
||||
bool stl_write_quad_object(stl_file *stl, char *file)
|
||||
{
|
||||
stl_vertex connect_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_1_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_2_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_3_color = stl_vertex::Zero();
|
||||
stl_vertex color;
|
||||
|
||||
void
|
||||
stl_write_quad_object(stl_file *stl, char *file) {
|
||||
FILE *fp;
|
||||
int i;
|
||||
int j;
|
||||
char *error_msg;
|
||||
stl_vertex connect_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_1_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_2_color = stl_vertex::Zero();
|
||||
stl_vertex uncon_3_color = stl_vertex::Zero();
|
||||
stl_vertex color;
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_quad_object: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "CQUAD\n");
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
j = ((stl->neighbors_start[i].neighbor[0] == -1) +
|
||||
(stl->neighbors_start[i].neighbor[1] == -1) +
|
||||
(stl->neighbors_start[i].neighbor[2] == -1));
|
||||
if(j == 0) {
|
||||
color = connect_color;
|
||||
} else if(j == 1) {
|
||||
color = uncon_1_color;
|
||||
} else if(j == 2) {
|
||||
color = uncon_2_color;
|
||||
} else {
|
||||
color = uncon_3_color;
|
||||
}
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
|
||||
stl->facet_start[i].vertex[0](0),
|
||||
stl->facet_start[i].vertex[0](1),
|
||||
stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
|
||||
stl->facet_start[i].vertex[1](0),
|
||||
stl->facet_start[i].vertex[1](1),
|
||||
stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
|
||||
stl->facet_start[i].vertex[2](0),
|
||||
stl->facet_start[i].vertex[2](1),
|
||||
stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n",
|
||||
stl->facet_start[i].vertex[2](0),
|
||||
stl->facet_start[i].vertex[2](1),
|
||||
stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "CQUAD\n");
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
switch (stl->neighbors_start[i].num_neighbors_missing()) {
|
||||
case 0: color = connect_color; break;
|
||||
case 1: color = uncon_1_color; break;
|
||||
case 2: color = uncon_2_color; break;
|
||||
default: color = uncon_3_color;
|
||||
}
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
|
||||
fprintf(fp, "%f %f %f %1.1f %1.1f %1.1f 1\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2), color(0), color(1), color(2));
|
||||
}
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
stl_write_dxf(stl_file *stl, const char *file, char *label) {
|
||||
int i;
|
||||
FILE *fp;
|
||||
char *error_msg;
|
||||
bool stl_write_dxf(stl_file *stl, const char *file, char *label)
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(file, "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_write_quad_object: Couldn't open " << file << " for writing";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stl->error) return;
|
||||
fprintf(fp, "999\n%s\n", label);
|
||||
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
|
||||
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\
|
||||
0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
|
||||
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
|
||||
|
||||
/* Open the file */
|
||||
fp = boost::nowide::fopen(file, "w");
|
||||
if(fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
|
||||
|
||||
fprintf(fp, "999\n%s\n", label);
|
||||
fprintf(fp, "0\nSECTION\n2\nHEADER\n0\nENDSEC\n");
|
||||
fprintf(fp, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nLAYER\n70\n1\n\
|
||||
0\nLAYER\n2\n0\n70\n0\n62\n7\n6\nCONTINUOUS\n0\nENDTAB\n0\nENDSEC\n");
|
||||
fprintf(fp, "0\nSECTION\n2\nBLOCKS\n0\nENDSEC\n");
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
fprintf(fp, "0\n3DFACE\n8\n0\n");
|
||||
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n", stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1), stl->facet_start[i].vertex[0](2));
|
||||
fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n", stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1), stl->facet_start[i].vertex[1](2));
|
||||
fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
|
||||
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n", stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1), stl->facet_start[i].vertex[2](2));
|
||||
}
|
||||
|
||||
fprintf(fp, "0\nSECTION\n2\nENTITIES\n");
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
fprintf(fp, "0\n3DFACE\n8\n0\n");
|
||||
fprintf(fp, "10\n%f\n20\n%f\n30\n%f\n",
|
||||
stl->facet_start[i].vertex[0](0), stl->facet_start[i].vertex[0](1),
|
||||
stl->facet_start[i].vertex[0](2));
|
||||
fprintf(fp, "11\n%f\n21\n%f\n31\n%f\n",
|
||||
stl->facet_start[i].vertex[1](0), stl->facet_start[i].vertex[1](1),
|
||||
stl->facet_start[i].vertex[1](2));
|
||||
fprintf(fp, "12\n%f\n22\n%f\n32\n%f\n",
|
||||
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
|
||||
stl->facet_start[i].vertex[2](2));
|
||||
fprintf(fp, "13\n%f\n23\n%f\n33\n%f\n",
|
||||
stl->facet_start[i].vertex[2](0), stl->facet_start[i].vertex[2](1),
|
||||
stl->facet_start[i].vertex[2](2));
|
||||
}
|
||||
|
||||
fprintf(fp, "0\nENDSEC\n0\nEOF\n");
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void
|
||||
stl_clear_error(stl_file *stl) {
|
||||
stl->error = 0;
|
||||
}
|
||||
|
||||
void
|
||||
stl_exit_on_error(stl_file *stl) {
|
||||
if (!stl->error) return;
|
||||
stl->error = 0;
|
||||
stl_close(stl);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
stl_get_error(stl_file *stl) {
|
||||
return stl->error;
|
||||
fprintf(fp, "0\nENDSEC\n0\nEOF\n");
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/detail/endian.hpp>
|
||||
|
||||
@ -35,351 +36,236 @@
|
||||
#error "SEEK_SET not defined"
|
||||
#endif
|
||||
|
||||
void
|
||||
stl_open(stl_file *stl, const char *file) {
|
||||
stl_initialize(stl);
|
||||
stl_count_facets(stl, file);
|
||||
stl_allocate(stl);
|
||||
stl_read(stl, 0, true);
|
||||
if (stl->fp != nullptr) {
|
||||
fclose(stl->fp);
|
||||
stl->fp = nullptr;
|
||||
}
|
||||
static FILE* stl_open_count_facets(stl_file *stl, const char *file)
|
||||
{
|
||||
// Open the file in binary mode first.
|
||||
FILE *fp = boost::nowide::fopen(file, "rb");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading";
|
||||
return nullptr;
|
||||
}
|
||||
// Find size of file.
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long file_size = ftell(fp);
|
||||
|
||||
// Check for binary or ASCII file.
|
||||
fseek(fp, HEADER_SIZE, SEEK_SET);
|
||||
unsigned char chtest[128];
|
||||
if (! fread(chtest, sizeof(chtest), 1, fp)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The input is an empty file: " << file;
|
||||
fclose(fp);
|
||||
return nullptr;
|
||||
}
|
||||
stl->stats.type = ascii;
|
||||
for (size_t s = 0; s < sizeof(chtest); s++) {
|
||||
if (chtest[s] > 127) {
|
||||
stl->stats.type = binary;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rewind(fp);
|
||||
|
||||
uint32_t num_facets = 0;
|
||||
|
||||
// Get the header and the number of facets in the .STL file.
|
||||
// If the .STL file is binary, then do the following:
|
||||
if (stl->stats.type == binary) {
|
||||
// Test if the STL file has the right size.
|
||||
if (((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0) || (file_size < STL_MIN_FILE_SIZE)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: The file " << file << " has the wrong size.";
|
||||
fclose(fp);
|
||||
return nullptr;
|
||||
}
|
||||
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
|
||||
|
||||
// Read the header.
|
||||
if (fread(stl->stats.header, LABEL_SIZE, 1, fp) > 79)
|
||||
stl->stats.header[80] = '\0';
|
||||
|
||||
// Read the int following the header. This should contain # of facets.
|
||||
uint32_t header_num_facets;
|
||||
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, fp) != 0;
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
// Convert from little endian to big endian.
|
||||
stl_internal_reverse_quads((char*)&header_num_facets, 4);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
if (! header_num_faces_read || num_facets != header_num_facets)
|
||||
BOOST_LOG_TRIVIAL(info) << "stl_open_count_facets: Warning: File size doesn't match number of facets in the header: " << file;
|
||||
}
|
||||
// Otherwise, if the .STL file is ASCII, then do the following:
|
||||
else
|
||||
{
|
||||
// Reopen the file in text mode (for getting correct newlines on Windows)
|
||||
// fix to silence a warning about unused return value.
|
||||
// obviously if it fails we have problems....
|
||||
fp = boost::nowide::freopen(file, "r", fp);
|
||||
|
||||
// do another null check to be safe
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "stl_open_count_facets: Couldn't open " << file << " for reading";
|
||||
fclose(fp);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Find the number of facets.
|
||||
char linebuf[100];
|
||||
int num_lines = 1;
|
||||
while (fgets(linebuf, 100, fp) != nullptr) {
|
||||
// Don't count short lines.
|
||||
if (strlen(linebuf) <= 4)
|
||||
continue;
|
||||
// Skip solid/endsolid lines as broken STL file generators may put several of them.
|
||||
if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0)
|
||||
continue;
|
||||
++ num_lines;
|
||||
}
|
||||
|
||||
rewind(fp);
|
||||
|
||||
// Get the header.
|
||||
int i = 0;
|
||||
for (; i < 80 && (stl->stats.header[i] = getc(fp)) != '\n'; ++ i) ;
|
||||
stl->stats.header[i] = '\0'; // Lose the '\n'
|
||||
stl->stats.header[80] = '\0';
|
||||
|
||||
num_facets = num_lines / ASCII_LINES_PER_FACET;
|
||||
}
|
||||
|
||||
stl->stats.number_of_facets += num_facets;
|
||||
stl->stats.original_num_facets = stl->stats.number_of_facets;
|
||||
return fp;
|
||||
}
|
||||
|
||||
void
|
||||
stl_initialize(stl_file *stl) {
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
stl->stats.volume = -1.0;
|
||||
/* Reads the contents of the file pointed to by fp into the stl structure,
|
||||
starting at facet first_facet. The second argument says if it's our first
|
||||
time running this for the stl and therefore we should reset our max and min stats. */
|
||||
static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first)
|
||||
{
|
||||
if (stl->stats.type == binary)
|
||||
fseek(fp, HEADER_SIZE, SEEK_SET);
|
||||
else
|
||||
rewind(fp);
|
||||
|
||||
char normal_buf[3][32];
|
||||
for (uint32_t i = first_facet; i < stl->stats.number_of_facets; ++ i) {
|
||||
stl_facet facet;
|
||||
|
||||
if (stl->stats.type == binary) {
|
||||
// Read a single facet from a binary .STL file. We assume little-endian architecture!
|
||||
if (fread(&facet, 1, SIZEOF_STL_FACET, fp) != SIZEOF_STL_FACET)
|
||||
return false;
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
// Convert the loaded little endian data to big endian.
|
||||
stl_internal_reverse_quads((char*)&facet, 48);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
} else {
|
||||
// Read a single facet from an ASCII .STL file
|
||||
// skip solid/endsolid
|
||||
// (in this order, otherwise it won't work when they are paired in the middle of a file)
|
||||
fscanf(fp, "endsolid%*[^\n]\n");
|
||||
fscanf(fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
|
||||
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
|
||||
int res_normal = fscanf(fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
||||
assert(res_normal == 3);
|
||||
int res_outer_loop = fscanf(fp, " outer loop");
|
||||
assert(res_outer_loop == 0);
|
||||
int res_vertex1 = fscanf(fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
|
||||
assert(res_vertex1 == 3);
|
||||
int res_vertex2 = fscanf(fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
|
||||
assert(res_vertex2 == 3);
|
||||
int res_vertex3 = fscanf(fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
|
||||
assert(res_vertex3 == 3);
|
||||
int res_endloop = fscanf(fp, " endloop");
|
||||
assert(res_endloop == 0);
|
||||
// There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines.
|
||||
int res_endfacet = fscanf(fp, " endfacet ");
|
||||
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Something is syntactically very wrong with this ASCII STL! ";
|
||||
return false;
|
||||
}
|
||||
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Report close to zero vertex coordinates. Due to the nature of the floating point numbers,
|
||||
// close to zero values may be represented with singificantly higher precision than the rest of the vertices.
|
||||
// It may be worth to round these numbers to zero during loading to reduce the number of errors reported
|
||||
// during the STL import.
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
|
||||
printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
|
||||
if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
|
||||
printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
|
||||
if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
|
||||
printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write the facet into memory.
|
||||
stl->facet_start[i] = facet;
|
||||
stl_facet_stats(stl, facet, first);
|
||||
}
|
||||
|
||||
stl->stats.size = stl->stats.max - stl->stats.min;
|
||||
stl->stats.bounding_diameter = stl->stats.size.norm();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stl_open(stl_file *stl, const char *file)
|
||||
{
|
||||
stl->clear();
|
||||
FILE *fp = stl_open_count_facets(stl, file);
|
||||
if (fp == nullptr)
|
||||
return false;
|
||||
stl_allocate(stl);
|
||||
bool result = stl_read(stl, fp, 0, true);
|
||||
fclose(fp);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
extern void stl_internal_reverse_quads(char *buf, size_t cnt);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
|
||||
void
|
||||
stl_count_facets(stl_file *stl, const char *file) {
|
||||
long file_size;
|
||||
uint32_t header_num_facets;
|
||||
uint32_t num_facets;
|
||||
int i;
|
||||
size_t s;
|
||||
unsigned char chtest[128];
|
||||
int num_lines = 1;
|
||||
char *error_msg;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Open the file in binary mode first */
|
||||
stl->fp = boost::nowide::fopen(file, "rb");
|
||||
if(stl->fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
/* Find size of file */
|
||||
fseek(stl->fp, 0, SEEK_END);
|
||||
file_size = ftell(stl->fp);
|
||||
|
||||
/* Check for binary or ASCII file */
|
||||
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
|
||||
if (!fread(chtest, sizeof(chtest), 1, stl->fp)) {
|
||||
perror("The input is an empty file");
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
stl->stats.type = ascii;
|
||||
for(s = 0; s < sizeof(chtest); s++) {
|
||||
if(chtest[s] > 127) {
|
||||
stl->stats.type = binary;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rewind(stl->fp);
|
||||
|
||||
/* Get the header and the number of facets in the .STL file */
|
||||
/* If the .STL file is binary, then do the following */
|
||||
if(stl->stats.type == binary) {
|
||||
/* Test if the STL file has the right size */
|
||||
if(((file_size - HEADER_SIZE) % SIZEOF_STL_FACET != 0)
|
||||
|| (file_size < STL_MIN_FILE_SIZE)) {
|
||||
fprintf(stderr, "The file %s has the wrong size.\n", file);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
num_facets = (file_size - HEADER_SIZE) / SIZEOF_STL_FACET;
|
||||
|
||||
/* Read the header */
|
||||
if (fread(stl->stats.header, LABEL_SIZE, 1, stl->fp) > 79) {
|
||||
stl->stats.header[80] = '\0';
|
||||
}
|
||||
|
||||
/* Read the int following the header. This should contain # of facets */
|
||||
bool header_num_faces_read = fread(&header_num_facets, sizeof(uint32_t), 1, stl->fp) != 0;
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
// Convert from little endian to big endian.
|
||||
stl_internal_reverse_quads((char*)&header_num_facets, 4);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
if (! header_num_faces_read || num_facets != header_num_facets) {
|
||||
fprintf(stderr,
|
||||
"Warning: File size doesn't match number of facets in the header\n");
|
||||
}
|
||||
}
|
||||
/* Otherwise, if the .STL file is ASCII, then do the following */
|
||||
else {
|
||||
/* Reopen the file in text mode (for getting correct newlines on Windows) */
|
||||
// fix to silence a warning about unused return value.
|
||||
// obviously if it fails we have problems....
|
||||
stl->fp = boost::nowide::freopen(file, "r", stl->fp);
|
||||
|
||||
// do another null check to be safe
|
||||
if(stl->fp == NULL) {
|
||||
error_msg = (char*)
|
||||
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
|
||||
file);
|
||||
perror(error_msg);
|
||||
free(error_msg);
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the number of facets */
|
||||
char linebuf[100];
|
||||
while (fgets(linebuf, 100, stl->fp) != NULL) {
|
||||
/* don't count short lines */
|
||||
if (strlen(linebuf) <= 4) continue;
|
||||
|
||||
/* skip solid/endsolid lines as broken STL file generators may put several of them */
|
||||
if (strncmp(linebuf, "solid", 5) == 0 || strncmp(linebuf, "endsolid", 8) == 0) continue;
|
||||
|
||||
++num_lines;
|
||||
}
|
||||
|
||||
rewind(stl->fp);
|
||||
|
||||
/* Get the header */
|
||||
for(i = 0;
|
||||
(i < 80) && (stl->stats.header[i] = getc(stl->fp)) != '\n'; i++);
|
||||
stl->stats.header[i] = '\0'; /* Lose the '\n' */
|
||||
stl->stats.header[80] = '\0';
|
||||
|
||||
num_facets = num_lines / ASCII_LINES_PER_FACET;
|
||||
}
|
||||
stl->stats.number_of_facets += num_facets;
|
||||
stl->stats.original_num_facets = stl->stats.number_of_facets;
|
||||
void stl_allocate(stl_file *stl)
|
||||
{
|
||||
// Allocate memory for the entire .STL file.
|
||||
stl->facet_start.assign(stl->stats.number_of_facets, stl_facet());
|
||||
// Allocate memory for the neighbors list.
|
||||
stl->neighbors_start.assign(stl->stats.number_of_facets, stl_neighbors());
|
||||
}
|
||||
|
||||
void
|
||||
stl_allocate(stl_file *stl) {
|
||||
if (stl->error) return;
|
||||
|
||||
/* Allocate memory for the entire .STL file */
|
||||
stl->facet_start = (stl_facet*)calloc(stl->stats.number_of_facets,
|
||||
sizeof(stl_facet));
|
||||
if(stl->facet_start == NULL) perror("stl_initialize");
|
||||
stl->stats.facets_malloced = stl->stats.number_of_facets;
|
||||
|
||||
/* Allocate memory for the neighbors list */
|
||||
stl->neighbors_start = (stl_neighbors*)
|
||||
calloc(stl->stats.number_of_facets, sizeof(stl_neighbors));
|
||||
if(stl->facet_start == NULL) perror("stl_initialize");
|
||||
}
|
||||
|
||||
void
|
||||
stl_open_merge(stl_file *stl, char *file_to_merge) {
|
||||
int num_facets_so_far;
|
||||
stl_type origStlType;
|
||||
FILE *origFp;
|
||||
stl_file stl_to_merge;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
/* Record how many facets we have so far from the first file. We will start putting
|
||||
facets in the next position. Since we're 0-indexed, it'l be the same position. */
|
||||
num_facets_so_far = stl->stats.number_of_facets;
|
||||
|
||||
/* Record the file type we started with: */
|
||||
origStlType=stl->stats.type;
|
||||
/* Record the file pointer too: */
|
||||
origFp=stl->fp;
|
||||
|
||||
/* Initialize the sturucture with zero stats, header info and sizes: */
|
||||
stl_initialize(&stl_to_merge);
|
||||
stl_count_facets(&stl_to_merge, file_to_merge);
|
||||
|
||||
/* Copy what we need to into stl so that we can read the file_to_merge directly into it
|
||||
using stl_read: Save the rest of the valuable info: */
|
||||
stl->stats.type=stl_to_merge.stats.type;
|
||||
stl->fp=stl_to_merge.fp;
|
||||
|
||||
/* Add the number of facets we already have in stl with what we we found in stl_to_merge but
|
||||
haven't read yet. */
|
||||
stl->stats.number_of_facets=num_facets_so_far+stl_to_merge.stats.number_of_facets;
|
||||
|
||||
/* Allocate enough room for stl->stats.number_of_facets facets and neighbors: */
|
||||
stl_reallocate(stl);
|
||||
|
||||
/* Read the file to merge directly into stl, adding it to what we have already.
|
||||
Start at num_facets_so_far, the index to the first unused facet. Also say
|
||||
that this isn't our first time so we should augment stats like min and max
|
||||
instead of erasing them. */
|
||||
stl_read(stl, num_facets_so_far, false);
|
||||
|
||||
/* Restore the stl information we overwrote (for stl_read) so that it still accurately
|
||||
reflects the subject part: */
|
||||
stl->stats.type=origStlType;
|
||||
stl->fp=origFp;
|
||||
}
|
||||
|
||||
extern void
|
||||
stl_reallocate(stl_file *stl) {
|
||||
if (stl->error) return;
|
||||
/* Reallocate more memory for the .STL file(s) */
|
||||
stl->facet_start = (stl_facet*)realloc(stl->facet_start, stl->stats.number_of_facets *
|
||||
sizeof(stl_facet));
|
||||
if(stl->facet_start == NULL) perror("stl_initialize");
|
||||
stl->stats.facets_malloced = stl->stats.number_of_facets;
|
||||
|
||||
/* Reallocate more memory for the neighbors list */
|
||||
stl->neighbors_start = (stl_neighbors*)
|
||||
realloc(stl->neighbors_start, stl->stats.number_of_facets *
|
||||
sizeof(stl_neighbors));
|
||||
if(stl->facet_start == NULL) perror("stl_initialize");
|
||||
}
|
||||
|
||||
|
||||
/* Reads the contents of the file pointed to by stl->fp into the stl structure,
|
||||
starting at facet first_facet. The second argument says if it's our first
|
||||
time running this for the stl and therefore we should reset our max and min stats. */
|
||||
void stl_read(stl_file *stl, int first_facet, bool first) {
|
||||
stl_facet facet;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
if(stl->stats.type == binary) {
|
||||
fseek(stl->fp, HEADER_SIZE, SEEK_SET);
|
||||
} else {
|
||||
rewind(stl->fp);
|
||||
}
|
||||
|
||||
char normal_buf[3][32];
|
||||
for(uint32_t i = first_facet; i < stl->stats.number_of_facets; i++) {
|
||||
if(stl->stats.type == binary)
|
||||
/* Read a single facet from a binary .STL file */
|
||||
{
|
||||
/* we assume little-endian architecture! */
|
||||
if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) {
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
// Convert the loaded little endian data to big endian.
|
||||
stl_internal_reverse_quads((char*)&facet, 48);
|
||||
#endif /* BOOST_LITTLE_ENDIAN */
|
||||
} else
|
||||
/* Read a single facet from an ASCII .STL file */
|
||||
{
|
||||
// skip solid/endsolid
|
||||
// (in this order, otherwise it won't work when they are paired in the middle of a file)
|
||||
fscanf(stl->fp, "endsolid%*[^\n]\n");
|
||||
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
|
||||
// Leading space in the fscanf format skips all leading white spaces including numerous new lines and tabs.
|
||||
int res_normal = fscanf(stl->fp, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
||||
assert(res_normal == 3);
|
||||
int res_outer_loop = fscanf(stl->fp, " outer loop");
|
||||
assert(res_outer_loop == 0);
|
||||
int res_vertex1 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
|
||||
assert(res_vertex1 == 3);
|
||||
int res_vertex2 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
|
||||
assert(res_vertex2 == 3);
|
||||
int res_vertex3 = fscanf(stl->fp, " vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
|
||||
assert(res_vertex3 == 3);
|
||||
int res_endloop = fscanf(stl->fp, " endloop");
|
||||
assert(res_endloop == 0);
|
||||
// There is a leading and trailing white space around endfacet to eat up all leading and trailing white spaces including numerous tabs and new lines.
|
||||
int res_endfacet = fscanf(stl->fp, " endfacet ");
|
||||
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
||||
perror("Something is syntactically very wrong with this ASCII STL!");
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Report close to zero vertex coordinates. Due to the nature of the floating point numbers,
|
||||
// close to zero values may be represented with singificantly higher precision than the rest of the vertices.
|
||||
// It may be worth to round these numbers to zero during loading to reduce the number of errors reported
|
||||
// during the STL import.
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
if (facet.vertex[j](0) > -1e-12f && facet.vertex[j](0) < 1e-12f)
|
||||
printf("stl_read: facet %d(0) = %e\r\n", j, facet.vertex[j](0));
|
||||
if (facet.vertex[j](1) > -1e-12f && facet.vertex[j](1) < 1e-12f)
|
||||
printf("stl_read: facet %d(1) = %e\r\n", j, facet.vertex[j](1));
|
||||
if (facet.vertex[j](2) > -1e-12f && facet.vertex[j](2) < 1e-12f)
|
||||
printf("stl_read: facet %d(2) = %e\r\n", j, facet.vertex[j](2));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write the facet into memory. */
|
||||
stl->facet_start[i] = facet;
|
||||
stl_facet_stats(stl, facet, first);
|
||||
}
|
||||
stl->stats.size = stl->stats.max - stl->stats.min;
|
||||
stl->stats.bounding_diameter = stl->stats.size.norm();
|
||||
void stl_reallocate(stl_file *stl)
|
||||
{
|
||||
stl->facet_start.resize(stl->stats.number_of_facets);
|
||||
stl->neighbors_start.resize(stl->stats.number_of_facets);
|
||||
}
|
||||
|
||||
void stl_facet_stats(stl_file *stl, stl_facet facet, bool &first)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
// While we are going through all of the facets, let's find the
|
||||
// maximum and minimum values for x, y, and z
|
||||
|
||||
// While we are going through all of the facets, let's find the
|
||||
// maximum and minimum values for x, y, and z
|
||||
if (first) {
|
||||
// Initialize the max and min values the first time through
|
||||
stl->stats.min = facet.vertex[0];
|
||||
stl->stats.max = facet.vertex[0];
|
||||
stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs();
|
||||
stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
// Initialize the max and min values the first time through
|
||||
stl->stats.min = facet.vertex[0];
|
||||
stl->stats.max = facet.vertex[0];
|
||||
stl_vertex diff = (facet.vertex[1] - facet.vertex[0]).cwiseAbs();
|
||||
stl->stats.shortest_edge = std::max(diff(0), std::max(diff(1), diff(2)));
|
||||
first = false;
|
||||
}
|
||||
|
||||
// Now find the max and min values.
|
||||
for (size_t i = 0; i < 3; ++ i) {
|
||||
stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
|
||||
stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void stl_close(stl_file *stl)
|
||||
{
|
||||
assert(stl->fp == nullptr);
|
||||
assert(stl->heads == nullptr);
|
||||
assert(stl->tail == nullptr);
|
||||
|
||||
if (stl->facet_start != NULL)
|
||||
free(stl->facet_start);
|
||||
if (stl->neighbors_start != NULL)
|
||||
free(stl->neighbors_start);
|
||||
if (stl->v_indices != NULL)
|
||||
free(stl->v_indices);
|
||||
if (stl->v_shared != NULL)
|
||||
free(stl->v_shared);
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
// Now find the max and min values.
|
||||
for (size_t i = 0; i < 3; ++ i) {
|
||||
stl->stats.min = stl->stats.min.cwiseMin(facet.vertex[i]);
|
||||
stl->stats.max = stl->stats.max.cwiseMax(facet.vertex[i]);
|
||||
}
|
||||
}
|
||||
|
@ -25,435 +25,375 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
static void stl_rotate(float *x, float *y, const double c, const double s);
|
||||
static float get_area(stl_facet *facet);
|
||||
static float get_volume(stl_file *stl);
|
||||
void stl_verify_neighbors(stl_file *stl)
|
||||
{
|
||||
stl->stats.backwards_edges = 0;
|
||||
|
||||
|
||||
void
|
||||
stl_verify_neighbors(stl_file *stl) {
|
||||
int i;
|
||||
int j;
|
||||
stl_edge edge_a;
|
||||
stl_edge edge_b;
|
||||
int neighbor;
|
||||
int vnot;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
stl->stats.backwards_edges = 0;
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
edge_a.p1 = stl->facet_start[i].vertex[j];
|
||||
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
|
||||
neighbor = stl->neighbors_start[i].neighbor[j];
|
||||
vnot = stl->neighbors_start[i].which_vertex_not[j];
|
||||
|
||||
if(neighbor == -1)
|
||||
continue; /* this edge has no neighbor... Continue. */
|
||||
if(vnot < 3) {
|
||||
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
|
||||
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
|
||||
} else {
|
||||
stl->stats.backwards_edges += 1;
|
||||
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
|
||||
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
|
||||
}
|
||||
if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
|
||||
/* These edges should match but they don't. Print results. */
|
||||
printf("edge %d of facet %d doesn't match edge %d of facet %d\n",
|
||||
j, i, vnot + 1, neighbor);
|
||||
stl_write_facet(stl, (char*)"first facet", i);
|
||||
stl_write_facet(stl, (char*)"second facet", neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
struct stl_edge {
|
||||
stl_vertex p1;
|
||||
stl_vertex p2;
|
||||
int facet_number;
|
||||
};
|
||||
stl_edge edge_a;
|
||||
edge_a.p1 = stl->facet_start[i].vertex[j];
|
||||
edge_a.p2 = stl->facet_start[i].vertex[(j + 1) % 3];
|
||||
int neighbor = stl->neighbors_start[i].neighbor[j];
|
||||
if (neighbor == -1)
|
||||
continue; // this edge has no neighbor... Continue.
|
||||
int vnot = stl->neighbors_start[i].which_vertex_not[j];
|
||||
stl_edge edge_b;
|
||||
if (vnot < 3) {
|
||||
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
|
||||
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
|
||||
} else {
|
||||
stl->stats.backwards_edges += 1;
|
||||
edge_b.p1 = stl->facet_start[neighbor].vertex[(vnot + 1) % 3];
|
||||
edge_b.p2 = stl->facet_start[neighbor].vertex[(vnot + 2) % 3];
|
||||
}
|
||||
if (edge_a.p1 != edge_b.p1 || edge_a.p2 != edge_b.p2) {
|
||||
// These edges should match but they don't. Print results.
|
||||
BOOST_LOG_TRIVIAL(info) << "edge " << j << " of facet " << i << " doesn't match edge " << (vnot + 1) << " of facet " << neighbor;
|
||||
stl_write_facet(stl, (char*)"first facet", i);
|
||||
stl_write_facet(stl, (char*)"second facet", neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stl_translate(stl_file *stl, float x, float y, float z)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
stl_vertex new_min(x, y, z);
|
||||
stl_vertex shift = new_min - stl->stats.min;
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j] += shift;
|
||||
stl->stats.min = new_min;
|
||||
stl->stats.max += shift;
|
||||
stl_invalidate_shared_vertices(stl);
|
||||
stl_vertex new_min(x, y, z);
|
||||
stl_vertex shift = new_min - stl->stats.min;
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j] += shift;
|
||||
stl->stats.min = new_min;
|
||||
stl->stats.max += shift;
|
||||
}
|
||||
|
||||
/* Translates the stl by x,y,z, relatively from wherever it is currently */
|
||||
void stl_translate_relative(stl_file *stl, float x, float y, float z)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
stl_vertex shift(x, y, z);
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j] += shift;
|
||||
stl->stats.min += shift;
|
||||
stl->stats.max += shift;
|
||||
stl_invalidate_shared_vertices(stl);
|
||||
stl_vertex shift(x, y, z);
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j] += shift;
|
||||
stl->stats.min += shift;
|
||||
stl->stats.max += shift;
|
||||
}
|
||||
|
||||
void stl_scale_versor(stl_file *stl, const stl_vertex &versor)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
// Scale extents.
|
||||
auto s = versor.array();
|
||||
stl->stats.min.array() *= s;
|
||||
stl->stats.max.array() *= s;
|
||||
// Scale size.
|
||||
stl->stats.size.array() *= s;
|
||||
// Scale volume.
|
||||
if (stl->stats.volume > 0.0)
|
||||
stl->stats.volume *= versor(0) * versor(1) * versor(2);
|
||||
// Scale the mesh.
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j].array() *= s;
|
||||
stl_invalidate_shared_vertices(stl);
|
||||
// Scale extents.
|
||||
auto s = versor.array();
|
||||
stl->stats.min.array() *= s;
|
||||
stl->stats.max.array() *= s;
|
||||
// Scale size.
|
||||
stl->stats.size.array() *= s;
|
||||
// Scale volume.
|
||||
if (stl->stats.volume > 0.0)
|
||||
stl->stats.volume *= versor(0) * versor(1) * versor(2);
|
||||
// Scale the mesh.
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j].array() *= s;
|
||||
}
|
||||
|
||||
static void calculate_normals(stl_file *stl)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
stl_normal normal;
|
||||
for(uint32_t i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
stl_calculate_normal(normal, &stl->facet_start[i]);
|
||||
stl_normalize_vector(normal);
|
||||
stl->facet_start[i].normal = normal;
|
||||
}
|
||||
stl_normal normal;
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
stl_calculate_normal(normal, &stl->facet_start[i]);
|
||||
stl_normalize_vector(normal);
|
||||
stl->facet_start[i].normal = normal;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
stl_rotate_x(stl_file *stl, float angle) {
|
||||
int i;
|
||||
int j;
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
stl_rotate(&stl->facet_start[i].vertex[j](1),
|
||||
&stl->facet_start[i].vertex[j](2), c, s);
|
||||
}
|
||||
}
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
static inline void rotate_point_2d(float &x, float &y, const double c, const double s)
|
||||
{
|
||||
double xold = x;
|
||||
double yold = y;
|
||||
x = float(c * xold - s * yold);
|
||||
y = float(s * xold + c * yold);
|
||||
}
|
||||
|
||||
void
|
||||
stl_rotate_y(stl_file *stl, float angle) {
|
||||
int i;
|
||||
int j;
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
stl_rotate(&stl->facet_start[i].vertex[j](2),
|
||||
&stl->facet_start[i].vertex[j](0), c, s);
|
||||
}
|
||||
}
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
void stl_rotate_x(stl_file *stl, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
rotate_point_2d(stl->facet_start[i].vertex[j](1), stl->facet_start[i].vertex[j](2), c, s);
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
}
|
||||
|
||||
void
|
||||
stl_rotate_z(stl_file *stl, float angle) {
|
||||
int i;
|
||||
int j;
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
for(i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for(j = 0; j < 3; j++) {
|
||||
stl_rotate(&stl->facet_start[i].vertex[j](0),
|
||||
&stl->facet_start[i].vertex[j](1), c, s);
|
||||
}
|
||||
}
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
void stl_rotate_y(stl_file *stl, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
rotate_point_2d(stl->facet_start[i].vertex[j](2), stl->facet_start[i].vertex[j](0), c, s);
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
}
|
||||
|
||||
void stl_rotate_z(stl_file *stl, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
rotate_point_2d(stl->facet_start[i].vertex[j](0), stl->facet_start[i].vertex[j](1), c, s);
|
||||
stl_get_size(stl);
|
||||
calculate_normals(stl);
|
||||
}
|
||||
|
||||
void its_rotate_x(indexed_triangle_set &its, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (stl_vertex &v : its.vertices)
|
||||
rotate_point_2d(v(1), v(2), c, s);
|
||||
}
|
||||
|
||||
static void
|
||||
stl_rotate(float *x, float *y, const double c, const double s) {
|
||||
double xold = *x;
|
||||
double yold = *y;
|
||||
*x = float(c * xold - s * yold);
|
||||
*y = float(s * xold + c * yold);
|
||||
void its_rotate_y(indexed_triangle_set& its, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (stl_vertex& v : its.vertices)
|
||||
rotate_point_2d(v(2), v(0), c, s);
|
||||
}
|
||||
|
||||
void its_rotate_z(indexed_triangle_set& its, float angle)
|
||||
{
|
||||
double radian_angle = (angle / 180.0) * M_PI;
|
||||
double c = cos(radian_angle);
|
||||
double s = sin(radian_angle);
|
||||
for (stl_vertex& v : its.vertices)
|
||||
rotate_point_2d(v(0), v(1), c, s);
|
||||
}
|
||||
|
||||
void stl_get_size(stl_file *stl)
|
||||
{
|
||||
if (stl->error || stl->stats.number_of_facets == 0)
|
||||
return;
|
||||
stl->stats.min = stl->facet_start[0].vertex[0];
|
||||
stl->stats.max = stl->stats.min;
|
||||
for (int i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
const stl_facet &face = stl->facet_start[i];
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]);
|
||||
stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]);
|
||||
}
|
||||
}
|
||||
stl->stats.size = stl->stats.max - stl->stats.min;
|
||||
stl->stats.bounding_diameter = stl->stats.size.norm();
|
||||
if (stl->stats.number_of_facets == 0)
|
||||
return;
|
||||
stl->stats.min = stl->facet_start[0].vertex[0];
|
||||
stl->stats.max = stl->stats.min;
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
const stl_facet &face = stl->facet_start[i];
|
||||
for (int j = 0; j < 3; ++ j) {
|
||||
stl->stats.min = stl->stats.min.cwiseMin(face.vertex[j]);
|
||||
stl->stats.max = stl->stats.max.cwiseMax(face.vertex[j]);
|
||||
}
|
||||
}
|
||||
stl->stats.size = stl->stats.max - stl->stats.min;
|
||||
stl->stats.bounding_diameter = stl->stats.size.norm();
|
||||
}
|
||||
|
||||
void stl_mirror_xy(stl_file *stl)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
for(int i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for(int j = 0; j < 3; j++) {
|
||||
stl->facet_start[i].vertex[j](2) *= -1.0;
|
||||
}
|
||||
}
|
||||
float temp_size = stl->stats.min(2);
|
||||
stl->stats.min(2) = stl->stats.max(2);
|
||||
stl->stats.max(2) = temp_size;
|
||||
stl->stats.min(2) *= -1.0;
|
||||
stl->stats.max(2) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j](2) *= -1.0;
|
||||
float temp_size = stl->stats.min(2);
|
||||
stl->stats.min(2) = stl->stats.max(2);
|
||||
stl->stats.max(2) = temp_size;
|
||||
stl->stats.min(2) *= -1.0;
|
||||
stl->stats.max(2) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
|
||||
}
|
||||
|
||||
void stl_mirror_yz(stl_file *stl)
|
||||
{
|
||||
if (stl->error) return;
|
||||
|
||||
for (int i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
stl->facet_start[i].vertex[j](0) *= -1.0;
|
||||
}
|
||||
}
|
||||
float temp_size = stl->stats.min(0);
|
||||
stl->stats.min(0) = stl->stats.max(0);
|
||||
stl->stats.max(0) = temp_size;
|
||||
stl->stats.min(0) *= -1.0;
|
||||
stl->stats.max(0) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; j++)
|
||||
stl->facet_start[i].vertex[j](0) *= -1.0;
|
||||
float temp_size = stl->stats.min(0);
|
||||
stl->stats.min(0) = stl->stats.max(0);
|
||||
stl->stats.max(0) = temp_size;
|
||||
stl->stats.min(0) *= -1.0;
|
||||
stl->stats.max(0) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
|
||||
}
|
||||
|
||||
void stl_mirror_xz(stl_file *stl)
|
||||
{
|
||||
if (stl->error)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < stl->stats.number_of_facets; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
stl->facet_start[i].vertex[j](1) *= -1.0;
|
||||
}
|
||||
}
|
||||
float temp_size = stl->stats.min(1);
|
||||
stl->stats.min(1) = stl->stats.max(1);
|
||||
stl->stats.max(1) = temp_size;
|
||||
stl->stats.min(1) *= -1.0;
|
||||
stl->stats.max(1) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; /* for not altering stats */
|
||||
}
|
||||
|
||||
static float get_volume(stl_file *stl)
|
||||
{
|
||||
if (stl->error)
|
||||
return 0;
|
||||
|
||||
// Choose a point, any point as the reference.
|
||||
stl_vertex p0 = stl->facet_start[0].vertex[0];
|
||||
float volume = 0.f;
|
||||
for(uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
// Do dot product to get distance from point to plane.
|
||||
float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
|
||||
float area = get_area(&stl->facet_start[i]);
|
||||
volume += (area * height) / 3.0f;
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
void stl_calculate_volume(stl_file *stl)
|
||||
{
|
||||
if (stl->error) return;
|
||||
stl->stats.volume = get_volume(stl);
|
||||
if(stl->stats.volume < 0.0) {
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.volume = -stl->stats.volume;
|
||||
}
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
stl->facet_start[i].vertex[j](1) *= -1.0;
|
||||
float temp_size = stl->stats.min(1);
|
||||
stl->stats.min(1) = stl->stats.max(1);
|
||||
stl->stats.max(1) = temp_size;
|
||||
stl->stats.min(1) *= -1.0;
|
||||
stl->stats.max(1) *= -1.0;
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.facets_reversed -= stl->stats.number_of_facets; // for not altering stats
|
||||
}
|
||||
|
||||
static float get_area(stl_facet *facet)
|
||||
{
|
||||
/* cast to double before calculating cross product because large coordinates
|
||||
can result in overflowing product
|
||||
(bad area is responsible for bad volume and bad facets reversal) */
|
||||
double cross[3][3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) -
|
||||
((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1)));
|
||||
cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) -
|
||||
((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2)));
|
||||
cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) -
|
||||
((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0)));
|
||||
}
|
||||
/* cast to double before calculating cross product because large coordinates
|
||||
can result in overflowing product
|
||||
(bad area is responsible for bad volume and bad facets reversal) */
|
||||
double cross[3][3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cross[i][0]=(((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](2)) -
|
||||
((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](1)));
|
||||
cross[i][1]=(((double)facet->vertex[i](2) * (double)facet->vertex[(i + 1) % 3](0)) -
|
||||
((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](2)));
|
||||
cross[i][2]=(((double)facet->vertex[i](0) * (double)facet->vertex[(i + 1) % 3](1)) -
|
||||
((double)facet->vertex[i](1) * (double)facet->vertex[(i + 1) % 3](0)));
|
||||
}
|
||||
|
||||
stl_normal sum;
|
||||
sum(0) = cross[0][0] + cross[1][0] + cross[2][0];
|
||||
sum(1) = cross[0][1] + cross[1][1] + cross[2][1];
|
||||
sum(2) = cross[0][2] + cross[1][2] + cross[2][2];
|
||||
stl_normal sum;
|
||||
sum(0) = cross[0][0] + cross[1][0] + cross[2][0];
|
||||
sum(1) = cross[0][1] + cross[1][1] + cross[2][1];
|
||||
sum(2) = cross[0][2] + cross[1][2] + cross[2][2];
|
||||
|
||||
// This should already be done. But just in case, let's do it again.
|
||||
//FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy.
|
||||
stl_normal n;
|
||||
stl_calculate_normal(n, facet);
|
||||
stl_normalize_vector(n);
|
||||
return 0.5f * n.dot(sum);
|
||||
// This should already be done. But just in case, let's do it again.
|
||||
//FIXME this is questionable. the "sum" normal should be accurate, while the normal "n" may be calculated with a low accuracy.
|
||||
stl_normal n;
|
||||
stl_calculate_normal(n, facet);
|
||||
stl_normalize_vector(n);
|
||||
return 0.5f * n.dot(sum);
|
||||
}
|
||||
|
||||
void stl_repair(stl_file *stl,
|
||||
int fixall_flag,
|
||||
int exact_flag,
|
||||
int tolerance_flag,
|
||||
float tolerance,
|
||||
int increment_flag,
|
||||
float increment,
|
||||
int nearby_flag,
|
||||
int iterations,
|
||||
int remove_unconnected_flag,
|
||||
int fill_holes_flag,
|
||||
int normal_directions_flag,
|
||||
int normal_values_flag,
|
||||
int reverse_all_flag,
|
||||
int verbose_flag) {
|
||||
|
||||
int i;
|
||||
int last_edges_fixed = 0;
|
||||
|
||||
if (stl->error) return;
|
||||
|
||||
if(exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag
|
||||
|| fill_holes_flag || normal_directions_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking exact...\n");
|
||||
exact_flag = 1;
|
||||
stl_check_facets_exact(stl);
|
||||
stl->stats.facets_w_1_bad_edge =
|
||||
(stl->stats.connected_facets_2_edge -
|
||||
stl->stats.connected_facets_3_edge);
|
||||
stl->stats.facets_w_2_bad_edge =
|
||||
(stl->stats.connected_facets_1_edge -
|
||||
stl->stats.connected_facets_2_edge);
|
||||
stl->stats.facets_w_3_bad_edge =
|
||||
(stl->stats.number_of_facets -
|
||||
stl->stats.connected_facets_1_edge);
|
||||
}
|
||||
|
||||
if(nearby_flag || fixall_flag) {
|
||||
if(!tolerance_flag) {
|
||||
tolerance = stl->stats.shortest_edge;
|
||||
}
|
||||
if(!increment_flag) {
|
||||
increment = stl->stats.bounding_diameter / 10000.0;
|
||||
}
|
||||
|
||||
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
for(i = 0; i < iterations; i++) {
|
||||
if(stl->stats.connected_facets_3_edge <
|
||||
stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("\
|
||||
Checking nearby. Tolerance= %f Iteration=%d of %d...",
|
||||
tolerance, i + 1, iterations);
|
||||
stl_check_facets_nearby(stl, tolerance);
|
||||
if (verbose_flag)
|
||||
printf(" Fixed %d edges.\n",
|
||||
stl->stats.edges_fixed - last_edges_fixed);
|
||||
last_edges_fixed = stl->stats.edges_fixed;
|
||||
tolerance += increment;
|
||||
} else {
|
||||
if (verbose_flag)
|
||||
printf("\
|
||||
All facets connected. No further nearby check necessary.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (verbose_flag)
|
||||
printf("All facets connected. No nearby check necessary.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(remove_unconnected_flag || fixall_flag || fill_holes_flag) {
|
||||
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("Removing unconnected facets...\n");
|
||||
stl_remove_unconnected_facets(stl);
|
||||
} else
|
||||
if (verbose_flag)
|
||||
printf("No unconnected need to be removed.\n");
|
||||
}
|
||||
|
||||
if(fill_holes_flag || fixall_flag) {
|
||||
if(stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("Filling holes...\n");
|
||||
stl_fill_holes(stl);
|
||||
} else
|
||||
if (verbose_flag)
|
||||
printf("No holes need to be filled.\n");
|
||||
}
|
||||
|
||||
if(reverse_all_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Reversing all facets...\n");
|
||||
stl_reverse_all_facets(stl);
|
||||
}
|
||||
|
||||
if(normal_directions_flag || fixall_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking normal directions...\n");
|
||||
stl_fix_normal_directions(stl);
|
||||
}
|
||||
|
||||
if(normal_values_flag || fixall_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking normal values...\n");
|
||||
stl_fix_normal_values(stl);
|
||||
}
|
||||
|
||||
/* Always calculate the volume. It shouldn't take too long */
|
||||
if (verbose_flag)
|
||||
printf("Calculating volume...\n");
|
||||
stl_calculate_volume(stl);
|
||||
|
||||
if(exact_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Verifying neighbors...\n");
|
||||
stl_verify_neighbors(stl);
|
||||
}
|
||||
static float get_volume(stl_file *stl)
|
||||
{
|
||||
// Choose a point, any point as the reference.
|
||||
stl_vertex p0 = stl->facet_start[0].vertex[0];
|
||||
float volume = 0.f;
|
||||
for (uint32_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||
// Do dot product to get distance from point to plane.
|
||||
float height = stl->facet_start[i].normal.dot(stl->facet_start[i].vertex[0] - p0);
|
||||
float area = get_area(&stl->facet_start[i]);
|
||||
volume += (area * height) / 3.0f;
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
void stl_calculate_volume(stl_file *stl)
|
||||
{
|
||||
stl->stats.volume = get_volume(stl);
|
||||
if (stl->stats.volume < 0.0) {
|
||||
stl_reverse_all_facets(stl);
|
||||
stl->stats.volume = -stl->stats.volume;
|
||||
}
|
||||
}
|
||||
|
||||
void stl_repair(
|
||||
stl_file *stl,
|
||||
bool fixall_flag,
|
||||
bool exact_flag,
|
||||
bool tolerance_flag,
|
||||
float tolerance,
|
||||
bool increment_flag,
|
||||
float increment,
|
||||
bool nearby_flag,
|
||||
int iterations,
|
||||
bool remove_unconnected_flag,
|
||||
bool fill_holes_flag,
|
||||
bool normal_directions_flag,
|
||||
bool normal_values_flag,
|
||||
bool reverse_all_flag,
|
||||
bool verbose_flag)
|
||||
{
|
||||
if (exact_flag || fixall_flag || nearby_flag || remove_unconnected_flag || fill_holes_flag || normal_directions_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking exact...\n");
|
||||
exact_flag = true;
|
||||
stl_check_facets_exact(stl);
|
||||
stl->stats.facets_w_1_bad_edge = (stl->stats.connected_facets_2_edge - stl->stats.connected_facets_3_edge);
|
||||
stl->stats.facets_w_2_bad_edge = (stl->stats.connected_facets_1_edge - stl->stats.connected_facets_2_edge);
|
||||
stl->stats.facets_w_3_bad_edge = (stl->stats.number_of_facets - stl->stats.connected_facets_1_edge);
|
||||
}
|
||||
|
||||
if (nearby_flag || fixall_flag) {
|
||||
if (! tolerance_flag)
|
||||
tolerance = stl->stats.shortest_edge;
|
||||
if (! increment_flag)
|
||||
increment = stl->stats.bounding_diameter / 10000.0;
|
||||
}
|
||||
|
||||
if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
int last_edges_fixed = 0;
|
||||
for (int i = 0; i < iterations; ++ i) {
|
||||
if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
|
||||
stl_check_facets_nearby(stl, tolerance);
|
||||
if (verbose_flag)
|
||||
printf(" Fixed %d edges.\n", stl->stats.edges_fixed - last_edges_fixed);
|
||||
last_edges_fixed = stl->stats.edges_fixed;
|
||||
tolerance += increment;
|
||||
} else {
|
||||
if (verbose_flag)
|
||||
printf("All facets connected. No further nearby check necessary.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (verbose_flag)
|
||||
printf("All facets connected. No nearby check necessary.\n");
|
||||
|
||||
if (remove_unconnected_flag || fixall_flag || fill_holes_flag) {
|
||||
if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("Removing unconnected facets...\n");
|
||||
stl_remove_unconnected_facets(stl);
|
||||
} else if (verbose_flag)
|
||||
printf("No unconnected need to be removed.\n");
|
||||
}
|
||||
|
||||
if (fill_holes_flag || fixall_flag) {
|
||||
if (stl->stats.connected_facets_3_edge < stl->stats.number_of_facets) {
|
||||
if (verbose_flag)
|
||||
printf("Filling holes...\n");
|
||||
stl_fill_holes(stl);
|
||||
} else if (verbose_flag)
|
||||
printf("No holes need to be filled.\n");
|
||||
}
|
||||
|
||||
if (reverse_all_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Reversing all facets...\n");
|
||||
stl_reverse_all_facets(stl);
|
||||
}
|
||||
|
||||
if (normal_directions_flag || fixall_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking normal directions...\n");
|
||||
stl_fix_normal_directions(stl);
|
||||
}
|
||||
|
||||
if (normal_values_flag || fixall_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Checking normal values...\n");
|
||||
stl_fix_normal_values(stl);
|
||||
}
|
||||
|
||||
// Always calculate the volume. It shouldn't take too long.
|
||||
if (verbose_flag)
|
||||
printf("Calculating volume...\n");
|
||||
stl_calculate_volume(stl);
|
||||
|
||||
if (exact_flag) {
|
||||
if (verbose_flag)
|
||||
printf("Verifying neighbors...\n");
|
||||
stl_verify_neighbors(stl);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,6 @@ add_library(nowide STATIC
|
||||
nowide/windows.hpp
|
||||
)
|
||||
|
||||
target_include_directories(nowide SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(nowide PUBLIC boost_headeronly)
|
||||
|
||||
|
||||
|
@ -56,12 +56,12 @@ endif()
|
||||
|
||||
# Clipper backend is not enough on its own, it still needs some functions
|
||||
# from Boost geometry
|
||||
if(NOT Boost_INCLUDE_DIRS_FOUND)
|
||||
if(NOT Boost_FOUND)
|
||||
find_package(Boost 1.58 REQUIRED)
|
||||
# TODO automatic download of boost geometry headers
|
||||
endif()
|
||||
|
||||
target_include_directories(clipperBackend SYSTEM INTERFACE ${Boost_INCLUDE_DIRS} )
|
||||
target_link_libraries(clipperBackend INTERFACE Boost::boost )
|
||||
#target_sources(ClipperBackend INTERFACE
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp
|
||||
|
@ -183,13 +183,12 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
endif ()
|
||||
|
||||
target_compile_definitions(libslic3r PUBLIC -DUSE_TBB)
|
||||
target_include_directories(libslic3r SYSTEM PUBLIC ${Boost_INCLUDE_DIRS})
|
||||
target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(libslic3r
|
||||
libnest2d
|
||||
admesh
|
||||
miniz
|
||||
${Boost_LIBRARIES}
|
||||
boost_libs
|
||||
clipper
|
||||
nowide
|
||||
${EXPAT_LIBRARIES}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include "FillRectilinear3.hpp"
|
||||
|
||||
#define SLIC3R_DEBUG
|
||||
// #define SLIC3R_DEBUG
|
||||
|
||||
// Make assert active if SLIC3R_DEBUG
|
||||
#ifdef SLIC3R_DEBUG
|
||||
|
@ -1489,10 +1489,10 @@ namespace Slic3r {
|
||||
}
|
||||
|
||||
// splits volume out of imported geometry
|
||||
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
|
||||
ModelVolume* volume = object.add_volume(TriangleMesh());
|
||||
stl_file& stl = volume->mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
TriangleMesh triangle_mesh;
|
||||
stl_file &stl = triangle_mesh.stl;
|
||||
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = (uint32_t)triangles_count;
|
||||
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
@ -1509,9 +1509,11 @@ namespace Slic3r {
|
||||
}
|
||||
}
|
||||
|
||||
stl_get_size(&stl);
|
||||
volume->mesh.repair();
|
||||
volume->center_geometry();
|
||||
stl_get_size(&stl);
|
||||
triangle_mesh.repair();
|
||||
|
||||
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
|
||||
volume->center_geometry_after_creation();
|
||||
volume->calculate_convex_hull();
|
||||
|
||||
// apply volume's name and config data
|
||||
@ -1879,29 +1881,28 @@ namespace Slic3r {
|
||||
if (volume == nullptr)
|
||||
continue;
|
||||
|
||||
if (!volume->mesh().repaired)
|
||||
throw std::runtime_error("store_3mf() requires repair()");
|
||||
if (!volume->mesh().has_shared_vertices())
|
||||
throw std::runtime_error("store_3mf() requires shared vertices");
|
||||
|
||||
volumes_offsets.insert(VolumeToOffsetsMap::value_type(volume, Offsets(vertices_count))).first;
|
||||
|
||||
if (!volume->mesh.repaired)
|
||||
volume->mesh.repair();
|
||||
|
||||
stl_file& stl = volume->mesh.stl;
|
||||
if (stl.v_shared == nullptr)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
|
||||
if (stl.stats.shared_vertices == 0)
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
if (its.vertices.empty())
|
||||
{
|
||||
add_error("Found invalid mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
vertices_count += stl.stats.shared_vertices;
|
||||
vertices_count += its.vertices.size();
|
||||
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i)
|
||||
{
|
||||
stream << " <" << VERTEX_TAG << " ";
|
||||
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
|
||||
Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
|
||||
stream << "x=\"" << v(0) << "\" ";
|
||||
stream << "y=\"" << v(1) << "\" ";
|
||||
stream << "z=\"" << v(2) << "\" />\n";
|
||||
@ -1920,19 +1921,19 @@ namespace Slic3r {
|
||||
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
|
||||
assert(volume_it != volumes_offsets.end());
|
||||
|
||||
stl_file& stl = volume->mesh.stl;
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
|
||||
// updates triangle offsets
|
||||
volume_it->second.first_triangle_id = triangles_count;
|
||||
triangles_count += stl.stats.number_of_facets;
|
||||
triangles_count += its.indices.size();
|
||||
volume_it->second.last_triangle_id = triangles_count - 1;
|
||||
|
||||
for (uint32_t i = 0; i < stl.stats.number_of_facets; ++i)
|
||||
for (size_t i = 0; i < its.indices.size(); ++ i)
|
||||
{
|
||||
stream << " <" << TRIANGLE_TAG << " ";
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
stream << "v" << j + 1 << "=\"" << stl.v_indices[i].vertex[j] + volume_it->second.first_vertex_id << "\" ";
|
||||
stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
|
||||
}
|
||||
stream << "/>\n";
|
||||
}
|
||||
|
@ -522,7 +522,8 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||
case NODE_TYPE_VOLUME:
|
||||
{
|
||||
assert(m_object && m_volume);
|
||||
stl_file &stl = m_volume->mesh.stl;
|
||||
TriangleMesh mesh;
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
@ -533,8 +534,9 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||
memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
|
||||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume->center_geometry();
|
||||
mesh.repair();
|
||||
m_volume->set_mesh(std::move(mesh));
|
||||
m_volume->center_geometry_after_creation();
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
@ -923,23 +925,23 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||
int num_vertices = 0;
|
||||
for (ModelVolume *volume : object->volumes) {
|
||||
vertices_offsets.push_back(num_vertices);
|
||||
if (! volume->mesh.repaired)
|
||||
if (! volume->mesh().repaired)
|
||||
throw std::runtime_error("store_amf() requires repair()");
|
||||
auto &stl = volume->mesh.stl;
|
||||
if (stl.v_shared == nullptr)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
if (! volume->mesh().has_shared_vertices())
|
||||
throw std::runtime_error("store_amf() requires shared vertices");
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
|
||||
for (size_t i = 0; i < its.vertices.size(); ++i) {
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
|
||||
Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
|
||||
stream << " <x>" << v(0) << "</x>\n";
|
||||
stream << " <y>" << v(1) << "</y>\n";
|
||||
stream << " <z>" << v(2) << "</z>\n";
|
||||
stream << " </coordinates>\n";
|
||||
stream << " </vertex>\n";
|
||||
}
|
||||
num_vertices += stl.stats.shared_vertices;
|
||||
num_vertices += its.vertices.size();
|
||||
}
|
||||
stream << " </vertices>\n";
|
||||
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
|
||||
@ -956,10 +958,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||
if (volume->is_modifier())
|
||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
|
||||
for (int i = 0; i < (int)volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||
const indexed_triangle_set &its = volume->mesh().its;
|
||||
for (size_t i = 0; i < (int)its.indices.size(); ++i) {
|
||||
stream << " <triangle>\n";
|
||||
for (int j = 0; j < 3; ++j)
|
||||
stream << " <v" << j + 1 << ">" << volume->mesh.stl.v_indices[i].vertex[j] + vertices_offset << "</v" << j + 1 << ">\n";
|
||||
stream << " <v" << j + 1 << ">" << its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n";
|
||||
stream << " </triangle>\n";
|
||||
}
|
||||
stream << " </volume>\n";
|
||||
|
@ -161,16 +161,15 @@ static void extract_model_from_archive(
|
||||
else {
|
||||
// Header has been extracted. Now read the faces.
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.error = 0;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = header.nTriangles;
|
||||
stl.stats.original_num_facets = header.nTriangles;
|
||||
stl_allocate(&stl);
|
||||
if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
|
||||
memcpy((char*)stl.facet_start, data.data() + sizeof(StlHeader), 50 * header.nTriangles);
|
||||
memcpy((char*)stl.facet_start.data(), data.data() + sizeof(StlHeader), 50 * header.nTriangles);
|
||||
if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
|
||||
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
|
||||
unsigned char *data = (unsigned char*)stl.facet_start;
|
||||
unsigned char *data = (unsigned char*)stl.facet_start.data();
|
||||
for (size_t i = header.nTriangles - 1; i > 0; -- i)
|
||||
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
|
||||
}
|
||||
@ -257,7 +256,7 @@ static void extract_model_from_archive(
|
||||
stl.stats.number_of_facets = (uint32_t)facets.size();
|
||||
stl.stats.original_num_facets = (int)facets.size();
|
||||
stl_allocate(&stl);
|
||||
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||
memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
// Add a mesh to a model.
|
||||
|
@ -17,8 +17,7 @@ namespace Slic3r {
|
||||
bool load_stl(const char *path, Model *model, const char *object_name_in)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
mesh.ReadSTLFile(path);
|
||||
if (mesh.stl.error) {
|
||||
if (! mesh.ReadSTLFile(path)) {
|
||||
// die "Failed to open $file\n" if !-e $path;
|
||||
return false;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <mutex> // for std::lock_guard
|
||||
#include <functional> // for std::function
|
||||
#include <utility> // for std::forward
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -182,6 +183,14 @@ public:
|
||||
inline bool empty() const { return size() == 0; }
|
||||
};
|
||||
|
||||
template<class C> bool all_of(const C &container) {
|
||||
return std::all_of(container.begin(),
|
||||
container.end(),
|
||||
[](const typename C::value_type &v) {
|
||||
return static_cast<bool>(v);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // MTUTILS_HPP
|
||||
|
@ -160,12 +160,6 @@ Model Model::read_from_archive(const std::string &input_file, DynamicPrintConfig
|
||||
return model;
|
||||
}
|
||||
|
||||
void Model::repair()
|
||||
{
|
||||
for (ModelObject *o : this->objects)
|
||||
o->repair();
|
||||
}
|
||||
|
||||
ModelObject* Model::add_object()
|
||||
{
|
||||
this->objects.emplace_back(new ModelObject(this));
|
||||
@ -472,7 +466,7 @@ bool Model::looks_like_multipart_object() const
|
||||
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
|
||||
return false;
|
||||
for (const ModelVolume *vol : obj->volumes) {
|
||||
double zmin_this = vol->mesh.bounding_box().min(2);
|
||||
double zmin_this = vol->mesh().bounding_box().min(2);
|
||||
if (zmin == std::numeric_limits<double>::max())
|
||||
zmin = zmin_this;
|
||||
else if (std::abs(zmin - zmin_this) > EPSILON)
|
||||
@ -679,7 +673,7 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
|
||||
{
|
||||
ModelVolume* v = new ModelVolume(this, mesh);
|
||||
this->volumes.push_back(v);
|
||||
v->center_geometry();
|
||||
v->center_geometry_after_creation();
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
@ -688,7 +682,7 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
|
||||
{
|
||||
ModelVolume* v = new ModelVolume(this, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
v->center_geometry();
|
||||
v->center_geometry_after_creation();
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
@ -697,8 +691,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
|
||||
{
|
||||
ModelVolume* v = new ModelVolume(this, other);
|
||||
this->volumes.push_back(v);
|
||||
v->center_geometry();
|
||||
this->invalidate_bounding_box();
|
||||
// The volume should already be centered at this point of time when copying shared pointers of the triangle mesh and convex hull.
|
||||
// v->center_geometry_after_creation();
|
||||
// this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
||||
@ -706,7 +701,7 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
|
||||
{
|
||||
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
v->center_geometry();
|
||||
v->center_geometry_after_creation();
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
@ -827,7 +822,7 @@ TriangleMesh ModelObject::raw_mesh() const
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
{
|
||||
TriangleMesh vol_mesh(v->mesh);
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix());
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
@ -840,7 +835,7 @@ TriangleMesh ModelObject::full_raw_mesh() const
|
||||
TriangleMesh mesh;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
TriangleMesh vol_mesh(v->mesh);
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix());
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
@ -854,7 +849,7 @@ const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
|
||||
m_raw_mesh_bounding_box.reset();
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
|
||||
m_raw_mesh_bounding_box.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
|
||||
}
|
||||
return m_raw_mesh_bounding_box;
|
||||
}
|
||||
@ -863,7 +858,7 @@ BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
bb.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
|
||||
bb.merge(v->mesh().transformed_bounding_box(v->get_matrix()));
|
||||
return bb;
|
||||
}
|
||||
|
||||
@ -881,7 +876,7 @@ const BoundingBoxf3& ModelObject::raw_bounding_box() const
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
if (v->is_model_part())
|
||||
m_raw_bounding_box.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
m_raw_bounding_box.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
}
|
||||
}
|
||||
return m_raw_bounding_box;
|
||||
@ -895,7 +890,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
||||
for (ModelVolume *v : this->volumes)
|
||||
{
|
||||
if (v->is_model_part())
|
||||
bb.merge(v->mesh.transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
bb.merge(v->mesh().transformed_bounding_box(inst_matrix * v->get_matrix()));
|
||||
}
|
||||
return bb;
|
||||
}
|
||||
@ -908,21 +903,20 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) const
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
const stl_file &stl = v->mesh.stl;
|
||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
if (stl.v_shared == nullptr) {
|
||||
const indexed_triangle_set &its = v->mesh().its;
|
||||
if (its.vertices.empty()) {
|
||||
// Using the STL faces.
|
||||
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) {
|
||||
const stl_facet &facet = stl.facet_start[i];
|
||||
const stl_file& stl = v->mesh().stl;
|
||||
for (const stl_facet &facet : stl.facet_start)
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
Vec3d p = trafo * facet.vertex[j].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
Vec3d p = trafo * stl.v_shared[i].cast<double>();
|
||||
for (size_t i = 0; i < its.vertices.size(); ++ i) {
|
||||
Vec3d p = trafo * its.vertices[i].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
@ -1039,6 +1033,7 @@ void ModelObject::mirror(Axis axis)
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void ModelObject::scale_mesh(const Vec3d &versor)
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
@ -1062,14 +1057,14 @@ size_t ModelObject::facets_count() const
|
||||
size_t num = 0;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
num += v->mesh.stl.stats.number_of_facets;
|
||||
num += v->mesh().stl.stats.number_of_facets;
|
||||
return num;
|
||||
}
|
||||
|
||||
bool ModelObject::needed_repair() const
|
||||
{
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part() && v->mesh.needed_repair())
|
||||
if (v->is_model_part() && v->mesh().needed_repair())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@ -1135,11 +1130,12 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
||||
|
||||
// Transform the mesh by the combined transformation matrix.
|
||||
// Flip the triangles in case the composite transformation is left handed.
|
||||
volume->mesh.transform(instance_matrix * volume_matrix, true);
|
||||
TriangleMesh mesh(volume->mesh());
|
||||
mesh.transform(instance_matrix * volume_matrix, true);
|
||||
volume->reset_mesh();
|
||||
|
||||
// Perform cut
|
||||
volume->mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
TriangleMeshSlicer tms(&volume->mesh);
|
||||
TriangleMeshSlicer tms(&mesh);
|
||||
tms.cut(float(z), &upper_mesh, &lower_mesh);
|
||||
|
||||
// Reset volume transformation except for offset
|
||||
@ -1158,14 +1154,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
||||
|
||||
if (keep_upper && upper_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
}
|
||||
if (keep_lower && lower_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
// Compute the lower part instances' bounding boxes to figure out where to place
|
||||
@ -1233,7 +1229,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||
}
|
||||
|
||||
ModelVolume* volume = this->volumes.front();
|
||||
TriangleMeshPtrs meshptrs = volume->mesh.split();
|
||||
TriangleMeshPtrs meshptrs = volume->mesh().split();
|
||||
for (TriangleMesh *mesh : meshptrs) {
|
||||
mesh->repair();
|
||||
|
||||
@ -1260,12 +1256,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||
return;
|
||||
}
|
||||
|
||||
void ModelObject::repair()
|
||||
{
|
||||
for (ModelVolume *v : this->volumes)
|
||||
v->mesh.repair();
|
||||
}
|
||||
|
||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
||||
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
||||
@ -1295,8 +1285,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
|
||||
|
||||
// Adjust the meshes.
|
||||
// Transformation to be applied to the meshes.
|
||||
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
|
||||
Eigen::Matrix3d mesh_trafo_3x3 = reference_trafo.get_matrix(true, false, uniform_scaling, ! has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||
Transform3d volume_offset_correction = this->instances[instance_idx]->get_transformation().get_matrix().inverse() * reference_trafo.get_matrix();
|
||||
for (ModelVolume *model_volume : this->volumes) {
|
||||
const Geometry::Transformation volume_trafo = model_volume->get_transformation();
|
||||
bool volume_left_handed = volume_trafo.is_left_handed();
|
||||
@ -1306,7 +1296,8 @@ void ModelObject::bake_xy_rotation_into_meshes(size_t instance_idx)
|
||||
double volume_new_scaling_factor = volume_uniform_scaling ? volume_trafo.get_scaling_factor().x() : 1.;
|
||||
// Transform the mesh.
|
||||
Matrix3d volume_trafo_3x3 = volume_trafo.get_matrix(true, false, volume_uniform_scaling, !volume_has_mirrorring).matrix().block<3, 3>(0, 0);
|
||||
model_volume->transform_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
|
||||
// Following method creates a new shared_ptr<TriangleMesh>
|
||||
model_volume->transform_this_mesh(mesh_trafo_3x3 * volume_trafo_3x3, left_handed != volume_left_handed);
|
||||
// Reset the rotation, scaling and mirroring.
|
||||
model_volume->set_rotation(Vec3d(0., 0., 0.));
|
||||
model_volume->set_scaling_factor(Vec3d(volume_new_scaling_factor, volume_new_scaling_factor, volume_new_scaling_factor));
|
||||
@ -1347,13 +1338,9 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
|
||||
|
||||
Transform3d mv = mi * v->get_matrix();
|
||||
const TriangleMesh& hull = v->get_convex_hull();
|
||||
for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f)
|
||||
{
|
||||
const stl_facet* facet = hull.stl.facet_start + f;
|
||||
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast<double>()));
|
||||
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast<double>()));
|
||||
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>()));
|
||||
}
|
||||
for (const stl_facet &facet : hull.stl.facet_start)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
min_z = std::min(min_z, (mv * facet.vertex[i].cast<double>()).z());
|
||||
}
|
||||
|
||||
return min_z + inst->get_offset(Z);
|
||||
@ -1452,7 +1439,7 @@ std::string ModelObject::get_export_filename() const
|
||||
stl_stats ModelObject::get_object_stl_stats() const
|
||||
{
|
||||
if (this->volumes.size() == 1)
|
||||
return this->volumes[0]->mesh.stl.stats;
|
||||
return this->volumes[0]->mesh().stl.stats;
|
||||
|
||||
stl_stats full_stats;
|
||||
memset(&full_stats, 0, sizeof(stl_stats));
|
||||
@ -1463,7 +1450,7 @@ stl_stats ModelObject::get_object_stl_stats() const
|
||||
if (volume->id() == this->volumes[0]->id())
|
||||
continue;
|
||||
|
||||
const stl_stats& stats = volume->mesh.stl.stats;
|
||||
const stl_stats& stats = volume->mesh().stl.stats;
|
||||
|
||||
// initialize full_stats (for repaired errors)
|
||||
full_stats.degenerate_facets += stats.degenerate_facets;
|
||||
@ -1531,30 +1518,30 @@ bool ModelVolume::is_splittable() const
|
||||
{
|
||||
// the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
|
||||
if (m_is_splittable == -1)
|
||||
m_is_splittable = (int)mesh.is_splittable();
|
||||
m_is_splittable = (int)this->mesh().is_splittable();
|
||||
|
||||
return m_is_splittable == 1;
|
||||
}
|
||||
|
||||
void ModelVolume::center_geometry()
|
||||
void ModelVolume::center_geometry_after_creation()
|
||||
{
|
||||
Vec3d shift = mesh.bounding_box().center();
|
||||
Vec3d shift = this->mesh().bounding_box().center();
|
||||
if (!shift.isApprox(Vec3d::Zero()))
|
||||
{
|
||||
mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
translate(shift);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelVolume::calculate_convex_hull()
|
||||
{
|
||||
m_convex_hull = mesh.convex_hull_3d();
|
||||
m_convex_hull = std::make_shared<TriangleMesh>(this->mesh().convex_hull_3d());
|
||||
}
|
||||
|
||||
int ModelVolume::get_mesh_errors_count() const
|
||||
{
|
||||
const stl_stats& stats = this->mesh.stl.stats;
|
||||
const stl_stats& stats = this->mesh().stl.stats;
|
||||
|
||||
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
||||
@ -1562,7 +1549,7 @@ int ModelVolume::get_mesh_errors_count() const
|
||||
|
||||
const TriangleMesh& ModelVolume::get_convex_hull() const
|
||||
{
|
||||
return m_convex_hull;
|
||||
return *m_convex_hull.get();
|
||||
}
|
||||
|
||||
ModelVolumeType ModelVolume::type_from_string(const std::string &s)
|
||||
@ -1602,7 +1589,7 @@ std::string ModelVolume::type_to_string(const ModelVolumeType t)
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
size_t ModelVolume::split(unsigned int max_extruders)
|
||||
{
|
||||
TriangleMeshPtrs meshptrs = this->mesh.split();
|
||||
TriangleMeshPtrs meshptrs = this->mesh().split();
|
||||
if (meshptrs.size() <= 1) {
|
||||
delete meshptrs.front();
|
||||
return 1;
|
||||
@ -1619,7 +1606,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
||||
mesh->repair();
|
||||
if (idx == 0)
|
||||
{
|
||||
this->mesh = std::move(*mesh);
|
||||
this->set_mesh(std::move(*mesh));
|
||||
this->calculate_convex_hull();
|
||||
// Assign a new unique ID, so that a new GLVolume will be generated.
|
||||
this->set_new_unique_id();
|
||||
@ -1628,7 +1615,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
||||
this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh)));
|
||||
|
||||
this->object->volumes[ivolume]->set_offset(Vec3d::Zero());
|
||||
this->object->volumes[ivolume]->center_geometry();
|
||||
this->object->volumes[ivolume]->center_geometry_after_creation();
|
||||
this->object->volumes[ivolume]->translate(offset);
|
||||
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
|
||||
this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders));
|
||||
@ -1694,24 +1681,33 @@ void ModelVolume::mirror(Axis axis)
|
||||
set_mirror(mirror);
|
||||
}
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void ModelVolume::scale_geometry(const Vec3d& versor)
|
||||
{
|
||||
mesh.scale(versor);
|
||||
m_convex_hull.scale(versor);
|
||||
m_mesh->scale(versor);
|
||||
m_convex_hull->scale(versor);
|
||||
}
|
||||
|
||||
void ModelVolume::transform_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
|
||||
void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
|
||||
{
|
||||
this->mesh.transform(mesh_trafo, fix_left_handed);
|
||||
this->m_convex_hull.transform(mesh_trafo, fix_left_handed);
|
||||
TriangleMesh mesh = this->mesh();
|
||||
mesh.transform(mesh_trafo, fix_left_handed);
|
||||
this->set_mesh(std::move(mesh));
|
||||
TriangleMesh convex_hull = this->get_convex_hull();
|
||||
convex_hull.transform(mesh_trafo, fix_left_handed);
|
||||
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||
this->set_new_unique_id();
|
||||
}
|
||||
|
||||
void ModelVolume::transform_mesh(const Matrix3d &matrix, bool fix_left_handed)
|
||||
void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_handed)
|
||||
{
|
||||
this->mesh.transform(matrix, fix_left_handed);
|
||||
this->m_convex_hull.transform(matrix, fix_left_handed);
|
||||
TriangleMesh mesh = this->mesh();
|
||||
mesh.transform(matrix, fix_left_handed);
|
||||
this->set_mesh(std::move(mesh));
|
||||
TriangleMesh convex_hull = this->get_convex_hull();
|
||||
convex_hull.transform(matrix, fix_left_handed);
|
||||
this->m_convex_hull = std::make_shared<TriangleMesh>(std::move(convex_hull));
|
||||
// Let the rest of the application know that the geometry changed, so the meshes have to be reloaded.
|
||||
this->set_new_unique_id();
|
||||
}
|
||||
|
@ -7,7 +7,9 @@
|
||||
#include "Point.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "Slicing.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -261,6 +263,7 @@ public:
|
||||
void rotate(double angle, const Vec3d& axis);
|
||||
void mirror(Axis axis);
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void scale_mesh(const Vec3d& versor);
|
||||
|
||||
size_t materials_count() const;
|
||||
@ -268,7 +271,6 @@ public:
|
||||
bool needed_repair() const;
|
||||
ModelObjectPtrs cut(size_t instance, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); // Note: z is in world coordinates
|
||||
void split(ModelObjectPtrs* new_objects);
|
||||
void repair();
|
||||
// Support for non-uniform scaling of instances. If an instance is rotated by angles, which are not multiples of ninety degrees,
|
||||
// then the scaling in world coordinate system is not representable by the Geometry::Transformation structure.
|
||||
// This situation is solved by baking in the instance transformation into the mesh vertices.
|
||||
@ -340,7 +342,12 @@ class ModelVolume : public ModelBase
|
||||
public:
|
||||
std::string name;
|
||||
// The triangular model.
|
||||
TriangleMesh mesh;
|
||||
const TriangleMesh& mesh() const { return *m_mesh.get(); }
|
||||
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
|
||||
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
|
||||
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
|
||||
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
|
||||
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
|
||||
// Configuration parameters specific to an object model geometry or a modifier volume,
|
||||
// overriding the global Slic3r settings and the ModelObject settings.
|
||||
DynamicPrintConfig config;
|
||||
@ -377,13 +384,16 @@ public:
|
||||
void rotate(double angle, const Vec3d& axis);
|
||||
void mirror(Axis axis);
|
||||
|
||||
// This method could only be called before the meshes of this ModelVolumes are not shared!
|
||||
void scale_geometry(const Vec3d& versor);
|
||||
|
||||
// translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box
|
||||
void center_geometry();
|
||||
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
|
||||
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
|
||||
void center_geometry_after_creation();
|
||||
|
||||
void calculate_convex_hull();
|
||||
const TriangleMesh& get_convex_hull() const;
|
||||
std::shared_ptr<const TriangleMesh> get_convex_hull_shared_ptr() const { return m_convex_hull; }
|
||||
// Get count of errors in the mesh
|
||||
int get_mesh_errors_count() const;
|
||||
|
||||
@ -430,18 +440,20 @@ protected:
|
||||
|
||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||
void set_model_object(ModelObject *model_object) { object = model_object; }
|
||||
void transform_mesh(const Transform3d& t, bool fix_left_handed);
|
||||
void transform_mesh(const Matrix3d& m, bool fix_left_handed);
|
||||
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
|
||||
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
|
||||
|
||||
private:
|
||||
// Parent object owning this ModelVolume.
|
||||
ModelObject* object;
|
||||
ModelObject* object;
|
||||
// The triangular model.
|
||||
std::shared_ptr<TriangleMesh> m_mesh;
|
||||
// Is it an object to be printed, or a modifier volume?
|
||||
ModelVolumeType m_type;
|
||||
t_model_material_id m_material_id;
|
||||
ModelVolumeType m_type;
|
||||
t_model_material_id m_material_id;
|
||||
// The convex hull of this model's mesh.
|
||||
TriangleMesh m_convex_hull;
|
||||
Geometry::Transformation m_transformation;
|
||||
std::shared_ptr<TriangleMesh> m_convex_hull;
|
||||
Geometry::Transformation m_transformation;
|
||||
|
||||
// flag to optimize the checking if the volume is splittable
|
||||
// -1 -> is unknown value (before first cheking)
|
||||
@ -449,24 +461,24 @@ private:
|
||||
// 1 -> is splittable
|
||||
mutable int m_is_splittable{ -1 };
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object)
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
|
||||
{
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
|
||||
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {}
|
||||
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
|
||||
|
||||
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||
ModelBase(other), // copy the ID
|
||||
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||
{
|
||||
this->set_material_id(other.material_id());
|
||||
}
|
||||
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
|
||||
{
|
||||
this->set_material_id(other.material_id());
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
@ -597,10 +609,6 @@ public:
|
||||
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
|
||||
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
|
||||
|
||||
/// Repair the ModelObjects of the current Model.
|
||||
/// This function calls repair function on each TriangleMesh of each model object volume
|
||||
void repair();
|
||||
|
||||
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
|
||||
ModelObject* add_object();
|
||||
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
|
||||
|
@ -1797,7 +1797,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||
if (! volumes.empty()) {
|
||||
// Compose mesh.
|
||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
||||
TriangleMesh mesh(volumes.front()->mesh);
|
||||
TriangleMesh mesh(volumes.front()->mesh());
|
||||
mesh.transform(volumes.front()->get_matrix(), true);
|
||||
assert(mesh.repaired);
|
||||
if (volumes.size() == 1 && mesh.repaired) {
|
||||
@ -1806,7 +1806,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||
}
|
||||
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
|
||||
const ModelVolume &model_volume = *volumes[idx_volume];
|
||||
TriangleMesh vol_mesh(model_volume.mesh);
|
||||
TriangleMesh vol_mesh(model_volume.mesh());
|
||||
vol_mesh.transform(model_volume.get_matrix(), true);
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
@ -1815,10 +1815,11 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||
// apply XY shift
|
||||
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
|
||||
// perform actual slicing
|
||||
TriangleMeshSlicer mslicer;
|
||||
const Print *print = this->print();
|
||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
// TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
|
||||
mesh.require_shared_vertices();
|
||||
TriangleMeshSlicer mslicer;
|
||||
mslicer.init(&mesh, callback);
|
||||
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
|
||||
m_print->throw_if_canceled();
|
||||
@ -1832,7 +1833,7 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
|
||||
std::vector<ExPolygons> layers;
|
||||
// Compose mesh.
|
||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
||||
TriangleMesh mesh(volume.mesh);
|
||||
TriangleMesh mesh(volume.mesh());
|
||||
mesh.transform(volume.get_matrix(), true);
|
||||
if (mesh.repaired) {
|
||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||
@ -1846,7 +1847,8 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
|
||||
TriangleMeshSlicer mslicer;
|
||||
const Print *print = this->print();
|
||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||
mesh.require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
// TriangleMeshSlicer needs the shared vertices.
|
||||
mesh.require_shared_vertices();
|
||||
mslicer.init(&mesh, callback);
|
||||
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
|
||||
m_print->throw_if_canceled();
|
||||
|
@ -53,7 +53,7 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
|
||||
// Shorthand for the vertex arrays
|
||||
auto& upoints = upper.points, &lpoints = lower.points;
|
||||
auto& rpts = ret.points; auto& rfaces = ret.indices;
|
||||
auto& rpts = ret.points; auto& ind = ret.indices;
|
||||
|
||||
// If the Z levels are flipped, or the offset difference is negative, we
|
||||
// will interpret that as the triangles normals should be inverted.
|
||||
@ -61,10 +61,11 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
|
||||
// Copy the points into the mesh, convert them from 2D to 3D
|
||||
rpts.reserve(upoints.size() + lpoints.size());
|
||||
rfaces.reserve(2*upoints.size() + 2*lpoints.size());
|
||||
const double sf = SCALING_FACTOR;
|
||||
for(auto& p : upoints) rpts.emplace_back(p.x()*sf, p.y()*sf, upper_z_mm);
|
||||
for(auto& p : lpoints) rpts.emplace_back(p.x()*sf, p.y()*sf, lower_z_mm);
|
||||
ind.reserve(2 * upoints.size() + 2 * lpoints.size());
|
||||
for (auto &p : upoints)
|
||||
rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm);
|
||||
for (auto &p : lpoints)
|
||||
rpts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm);
|
||||
|
||||
// Create pointing indices into vertex arrays. u-upper, l-lower
|
||||
size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1;
|
||||
@ -121,9 +122,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
case Proceed::UPPER:
|
||||
if(!ustarted || uidx != uendidx) { // there are vertices remaining
|
||||
// Get the 3D vertices in order
|
||||
const Vec3d& p_up1 = rpts[size_t(uidx)];
|
||||
const Vec3d& p_low = rpts[size_t(lidx)];
|
||||
const Vec3d& p_up2 = rpts[size_t(unextidx)];
|
||||
const Vec3d& p_up1 = rpts[uidx];
|
||||
const Vec3d& p_low = rpts[lidx];
|
||||
const Vec3d& p_up2 = rpts[unextidx];
|
||||
|
||||
// Calculate fitness: the average of the two connecting edges
|
||||
double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2);
|
||||
@ -133,8 +134,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
if(current_fit > prev_fit) { // fit is worse than previously
|
||||
proceed = Proceed::LOWER;
|
||||
} else { // good to go, create the triangle
|
||||
inverted? rfaces.emplace_back(unextidx, lidx, uidx) :
|
||||
rfaces.emplace_back(uidx, lidx, unextidx) ;
|
||||
inverted
|
||||
? ind.emplace_back(int(unextidx), int(lidx), int(uidx))
|
||||
: ind.emplace_back(int(uidx), int(lidx), int(unextidx));
|
||||
|
||||
// Increment the iterators, rotate if necessary
|
||||
++uidx; ++unextidx;
|
||||
@ -150,9 +152,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
case Proceed::LOWER:
|
||||
// Mode with lower segment, upper vertex. Same structure:
|
||||
if(!lstarted || lidx != lendidx) {
|
||||
const Vec3d& p_low1 = rpts[size_t(lidx)];
|
||||
const Vec3d& p_low2 = rpts[size_t(lnextidx)];
|
||||
const Vec3d& p_up = rpts[size_t(uidx)];
|
||||
const Vec3d& p_low1 = rpts[lidx];
|
||||
const Vec3d& p_low2 = rpts[lnextidx];
|
||||
const Vec3d& p_up = rpts[uidx];
|
||||
|
||||
double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2);
|
||||
double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2);
|
||||
@ -161,8 +163,9 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
if(current_fit > prev_fit) {
|
||||
proceed = Proceed::UPPER;
|
||||
} else {
|
||||
inverted? rfaces.emplace_back(uidx, lnextidx, lidx) :
|
||||
rfaces.emplace_back(lidx, lnextidx, uidx);
|
||||
inverted
|
||||
? ind.emplace_back(int(uidx), int(lnextidx), int(lidx))
|
||||
: ind.emplace_back(int(lidx), int(lnextidx), int(uidx));
|
||||
|
||||
++lidx; ++lnextidx;
|
||||
if(lnextidx == rpts.size()) lnextidx = offs;
|
||||
@ -200,7 +203,7 @@ void offset(ExPolygon& sh, coord_t distance) {
|
||||
}
|
||||
|
||||
ClipperOffset offs;
|
||||
offs.ArcTolerance = 0.01*mm(1);
|
||||
offs.ArcTolerance = 0.01*scaled(1.0);
|
||||
Paths result;
|
||||
offs.AddPath(ctour, jtRound, etClosedPolygon);
|
||||
offs.AddPaths(holes, jtRound, etClosedPolygon);
|
||||
@ -303,16 +306,6 @@ ExPolygons unify(const ExPolygons& shapes) {
|
||||
return retv;
|
||||
}
|
||||
|
||||
/// Only a debug function to generate top and bottom plates from a 2D shape.
|
||||
/// It is not used in the algorithm directly.
|
||||
inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
|
||||
auto lower = triangulate_expolygon_3d(poly);
|
||||
auto upper = triangulate_expolygon_3d(poly, z_distance*SCALING_FACTOR, true);
|
||||
Contour3D ret;
|
||||
ret.merge(lower); ret.merge(upper);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// This method will create a rounded edge around a flat polygon in 3d space.
|
||||
/// 'base_plate' parameter is the target plate.
|
||||
/// 'radius' is the radius of the edges.
|
||||
@ -358,7 +351,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
double x2 = xx*xx;
|
||||
double stepy = std::sqrt(r2 - x2);
|
||||
|
||||
offset(ob, s*mm(xx));
|
||||
offset(ob, s*scaled(xx));
|
||||
wh = ceilheight_mm - radius_mm + stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
@ -382,7 +375,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
double xx = radius_mm - i*stepx;
|
||||
double x2 = xx*xx;
|
||||
double stepy = std::sqrt(r2 - x2);
|
||||
offset(ob, s*mm(xx));
|
||||
offset(ob, s*scaled(xx));
|
||||
wh = ceilheight_mm - radius_mm - stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
@ -402,41 +395,6 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
return curvedwalls;
|
||||
}
|
||||
|
||||
/// Generating the concave part of the 3D pool with the bottom plate and the
|
||||
/// side walls.
|
||||
Contour3D inner_bed(const ExPolygon& poly,
|
||||
double depth_mm,
|
||||
double begin_h_mm = 0)
|
||||
{
|
||||
Contour3D bottom;
|
||||
Pointf3s triangles = triangulate_expolygon_3d(poly, -depth_mm + begin_h_mm);
|
||||
bottom.merge(triangles);
|
||||
|
||||
coord_t depth = mm(depth_mm);
|
||||
coord_t begin_h = mm(begin_h_mm);
|
||||
|
||||
auto lines = poly.lines();
|
||||
|
||||
// Generate outer walls
|
||||
auto fp = [](const Point& p, Point::coord_type z) {
|
||||
return unscale(x(p), y(p), z);
|
||||
};
|
||||
|
||||
for(auto& l : lines) {
|
||||
auto s = coord_t(bottom.points.size());
|
||||
|
||||
bottom.points.emplace_back(fp(l.a, -depth + begin_h));
|
||||
bottom.points.emplace_back(fp(l.b, -depth + begin_h));
|
||||
bottom.points.emplace_back(fp(l.a, begin_h));
|
||||
bottom.points.emplace_back(fp(l.b, begin_h));
|
||||
|
||||
bottom.indices.emplace_back(s + 3, s + 1, s);
|
||||
bottom.indices.emplace_back(s + 2, s + 3, s);
|
||||
}
|
||||
|
||||
return bottom;
|
||||
}
|
||||
|
||||
inline Point centroid(Points& pp) {
|
||||
Point c;
|
||||
switch(pp.size()) {
|
||||
@ -518,7 +476,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
|
||||
double dx = x(c) - x(cc), dy = y(c) - y(cc);
|
||||
double l = std::sqrt(dx * dx + dy * dy);
|
||||
double nx = dx / l, ny = dy / l;
|
||||
double max_dist = mm(max_dist_mm);
|
||||
double max_dist = scaled(max_dist_mm);
|
||||
|
||||
ExPolygon& expo = punion[idx++];
|
||||
BoundingBox querybb(expo);
|
||||
@ -534,10 +492,10 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
|
||||
ctour.reserve(3);
|
||||
ctour.emplace_back(cc);
|
||||
|
||||
Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny));
|
||||
Point d(coord_t(scaled(1.)*nx), coord_t(scaled(1.)*ny));
|
||||
ctour.emplace_back(c + Point( -y(d), x(d) ));
|
||||
ctour.emplace_back(c + Point( y(d), -x(d) ));
|
||||
offset(r, mm(1));
|
||||
offset(r, scaled(1.));
|
||||
|
||||
return r;
|
||||
});
|
||||
@ -569,15 +527,16 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
||||
// Now we have to unify all slice layers which can be an expensive operation
|
||||
// so we will try to simplify the polygons
|
||||
ExPolygons tmp; tmp.reserve(count);
|
||||
for(ExPolygons& o : out) for(ExPolygon& e : o) {
|
||||
auto&& exss = e.simplify(0.1/SCALING_FACTOR);
|
||||
for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
|
||||
}
|
||||
for(ExPolygons& o : out)
|
||||
for(ExPolygon& e : o) {
|
||||
auto&& exss = e.simplify(scaled(0.1));
|
||||
for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
|
||||
}
|
||||
|
||||
ExPolygons utmp = unify(tmp);
|
||||
|
||||
for(auto& o : utmp) {
|
||||
auto&& smp = o.simplify(0.1/SCALING_FACTOR);
|
||||
auto&& smp = o.simplify(scaled(0.1));
|
||||
output.insert(output.end(), smp.begin(), smp.end());
|
||||
}
|
||||
}
|
||||
@ -607,11 +566,11 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
|
||||
const double bottom_offs = (thickness + wingheight) / std::tan(slope);
|
||||
|
||||
// scaled values
|
||||
const coord_t s_thickness = mm(thickness);
|
||||
const coord_t s_eradius = mm(cfg.edge_radius_mm);
|
||||
const coord_t s_thickness = scaled(thickness);
|
||||
const coord_t s_eradius = scaled(cfg.edge_radius_mm);
|
||||
const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness);
|
||||
const coord_t s_wingdist = mm(wingdist);
|
||||
const coord_t s_bottom_offs = mm(bottom_offs);
|
||||
const coord_t s_wingdist = scaled(wingdist);
|
||||
const coord_t s_bottom_offs = scaled(bottom_offs);
|
||||
|
||||
auto& thrcl = cfg.throw_on_cancel;
|
||||
|
||||
|
@ -11,11 +11,6 @@
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
||||
using coord_t = Point::coord_type;
|
||||
|
||||
/// get the scaled clipper units for a millimeter value
|
||||
inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); }
|
||||
|
||||
/// Get x and y coordinates (because we are eigenizing...)
|
||||
inline coord_t x(const Point& p) { return p(0); }
|
||||
inline coord_t y(const Point& p) { return p(1); }
|
||||
@ -36,12 +31,10 @@ inline coord_t x(const Vec3crd& p) { return p(0); }
|
||||
inline coord_t y(const Vec3crd& p) { return p(1); }
|
||||
inline coord_t z(const Vec3crd& p) { return p(2); }
|
||||
|
||||
using Indices = std::vector<Vec3crd>;
|
||||
|
||||
/// Intermediate struct for a 3D mesh
|
||||
struct Contour3D {
|
||||
Pointf3s points;
|
||||
Indices indices;
|
||||
std::vector<Vec3i> indices;
|
||||
|
||||
void merge(const Contour3D& ctr) {
|
||||
auto s3 = coord_t(points.size());
|
||||
|
@ -44,7 +44,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj,
|
||||
// call the status callback in each iteration but the actual value may be
|
||||
// the same for subsequent iterations (status goes from 0 to 100 but
|
||||
// iterations can be many more)
|
||||
auto objfunc = [&emesh, &status, &statuscb, max_tries]
|
||||
auto objfunc = [&emesh, &status, &statuscb, &stopcond, max_tries]
|
||||
(double rx, double ry, double rz)
|
||||
{
|
||||
EigenMesh3D& m = emesh;
|
||||
@ -91,7 +91,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj,
|
||||
}
|
||||
|
||||
// report status
|
||||
statuscb( unsigned(++status * 100.0/max_tries) );
|
||||
if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) );
|
||||
|
||||
return score;
|
||||
};
|
||||
|
@ -236,13 +236,13 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
|
||||
// According to the slicing algorithms, we need to aid them with generating
|
||||
// a watertight body. So we create a triangle fan for the upper and lower
|
||||
// ending of the cylinder to close the geometry.
|
||||
points.emplace_back(jp); size_t ci = points.size() - 1;
|
||||
points.emplace_back(jp); int ci = int(points.size() - 1);
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(i + offs + 1, i + offs, ci);
|
||||
|
||||
indices.emplace_back(offs, steps + offs - 1, ci);
|
||||
|
||||
points.emplace_back(endp); ci = points.size() - 1;
|
||||
points.emplace_back(endp); ci = int(points.size() - 1);
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(ci, i, i + 1);
|
||||
|
||||
|
@ -121,19 +121,10 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
||||
V.resize(3*stl.stats.number_of_facets, 3);
|
||||
F.resize(stl.stats.number_of_facets, 3);
|
||||
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
|
||||
const stl_facet* facet = stl.facet_start+i;
|
||||
V(3*i+0, 0) = double(facet->vertex[0](0));
|
||||
V(3*i+0, 1) = double(facet->vertex[0](1));
|
||||
V(3*i+0, 2) = double(facet->vertex[0](2));
|
||||
|
||||
V(3*i+1, 0) = double(facet->vertex[1](0));
|
||||
V(3*i+1, 1) = double(facet->vertex[1](1));
|
||||
V(3*i+1, 2) = double(facet->vertex[1](2));
|
||||
|
||||
V(3*i+2, 0) = double(facet->vertex[2](0));
|
||||
V(3*i+2, 1) = double(facet->vertex[2](1));
|
||||
V(3*i+2, 2) = double(facet->vertex[2](2));
|
||||
|
||||
const stl_facet &facet = stl.facet_start[i];
|
||||
V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>();
|
||||
V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>();
|
||||
V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>();
|
||||
F(i, 0) = int(3*i+0);
|
||||
F(i, 1) = int(3*i+1);
|
||||
F(i, 2) = int(3*i+2);
|
||||
|
@ -28,14 +28,16 @@ namespace Slic3r {
|
||||
|
||||
using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
|
||||
|
||||
class SLAPrintObject::SupportData {
|
||||
class SLAPrintObject::SupportData
|
||||
{
|
||||
public:
|
||||
sla::EigenMesh3D emesh; // index-triangle representation
|
||||
std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
|
||||
SupportTreePtr support_tree_ptr; // the supports
|
||||
SlicedSupports support_slices; // sliced supports
|
||||
sla::EigenMesh3D emesh; // index-triangle representation
|
||||
std::vector<sla::SupportPoint>
|
||||
support_points; // all the support points (manual/auto)
|
||||
SupportTreePtr support_tree_ptr; // the supports
|
||||
SlicedSupports support_slices; // sliced supports
|
||||
|
||||
inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {}
|
||||
inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -666,11 +668,11 @@ void SLAPrint::process()
|
||||
double ilhd = m_material_config.initial_layer_height.getFloat();
|
||||
auto ilh = float(ilhd);
|
||||
|
||||
auto ilhs = coord_t(ilhd / SCALING_FACTOR);
|
||||
auto ilhs = scaled(ilhd);
|
||||
const size_t objcount = m_objects.size();
|
||||
|
||||
const unsigned min_objstatus = 0; // where the per object operations start
|
||||
const unsigned max_objstatus = 50; // where the per object operations end
|
||||
static const unsigned min_objstatus = 0; // where the per object operations start
|
||||
static const unsigned max_objstatus = 50; // where the per object operations end
|
||||
|
||||
// the coefficient that multiplies the per object status values which
|
||||
// are set up for <0, 100>. They need to be scaled into the whole process
|
||||
@ -687,31 +689,32 @@ void SLAPrint::process()
|
||||
|
||||
// Slicing the model object. This method is oversimplified and needs to
|
||||
// be compared with the fff slicing algorithm for verification
|
||||
auto slice_model = [this, ilhs, ilh, ilhd](SLAPrintObject& po) {
|
||||
auto slice_model = [this, ilhs, ilh](SLAPrintObject& po) {
|
||||
const TriangleMesh& mesh = po.transformed_mesh();
|
||||
|
||||
// We need to prepare the slice index...
|
||||
|
||||
double lhd = m_objects.front()->m_config.layer_height.getFloat();
|
||||
float lh = float(lhd);
|
||||
auto lhs = coord_t(lhd / SCALING_FACTOR);
|
||||
auto lhs = scaled(lhd);
|
||||
|
||||
auto&& bb3d = mesh.bounding_box();
|
||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||
double maxZ = bb3d.max(Z);
|
||||
auto &&bb3d = mesh.bounding_box();
|
||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||
double maxZ = bb3d.max(Z);
|
||||
auto minZf = float(minZ);
|
||||
|
||||
auto minZs = coord_t(minZ / SCALING_FACTOR);
|
||||
auto maxZs = coord_t(maxZ / SCALING_FACTOR);
|
||||
auto minZs = scaled(minZ);
|
||||
auto maxZs = scaled(maxZ);
|
||||
|
||||
po.m_slice_index.clear();
|
||||
|
||||
size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs);
|
||||
po.m_slice_index.reserve(cap);
|
||||
|
||||
po.m_slice_index.emplace_back(minZs + ilhs, minZ + ilhd / 2.0, ilh);
|
||||
po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh);
|
||||
|
||||
for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs)
|
||||
po.m_slice_index.emplace_back(h, h*SCALING_FACTOR - lhd / 2.0, lh);
|
||||
for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs)
|
||||
po.m_slice_index.emplace_back(h, unscaled<float>(h) - lh / 2.f, lh);
|
||||
|
||||
// Just get the first record that is form the model:
|
||||
auto slindex_it =
|
||||
@ -737,7 +740,7 @@ void SLAPrint::process()
|
||||
|
||||
auto mit = slindex_it;
|
||||
double doffs = m_printer_config.absolute_correction.getFloat();
|
||||
coord_t clpr_offs = coord_t(doffs / SCALING_FACTOR);
|
||||
coord_t clpr_offs = scaled(doffs);
|
||||
for(size_t id = 0;
|
||||
id < po.m_model_slices.size() && mit != po.m_slice_index.end();
|
||||
id++)
|
||||
@ -949,15 +952,15 @@ void SLAPrint::process()
|
||||
}
|
||||
|
||||
double doffs = m_printer_config.absolute_correction.getFloat();
|
||||
coord_t clpr_offs = coord_t(doffs / SCALING_FACTOR);
|
||||
coord_t clpr_offs = scaled(doffs);
|
||||
for(size_t i = 0;
|
||||
i < sd->support_slices.size() && i < po.m_slice_index.size();
|
||||
++i)
|
||||
{
|
||||
// We apply the printer correction offset here.
|
||||
if(clpr_offs != 0)
|
||||
sd->support_slices[i] =
|
||||
offset_ex(sd->support_slices[i], clpr_offs);
|
||||
sd->support_slices[i] =
|
||||
offset_ex(sd->support_slices[i], float(clpr_offs));
|
||||
|
||||
po.m_slice_index[i].set_support_slice_idx(po, i);
|
||||
}
|
||||
@ -1063,8 +1066,8 @@ void SLAPrint::process()
|
||||
|
||||
const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR;
|
||||
const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR;
|
||||
const double width = scaled(m_printer_config.display_width.getFloat());
|
||||
const double height = scaled(m_printer_config.display_height.getFloat());
|
||||
const double display_area = width*height;
|
||||
|
||||
// get polygons for all instances in the object
|
||||
@ -1170,13 +1173,20 @@ void SLAPrint::process()
|
||||
ClipperPolygons model_polygons;
|
||||
ClipperPolygons supports_polygons;
|
||||
|
||||
size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
size_t c = std::accumulate(layer.slices().begin(),
|
||||
layer.slices().end(),
|
||||
size_t(0),
|
||||
[](size_t a, const SliceRecord &sr) {
|
||||
return a + sr.get_slice(soModel)
|
||||
.size();
|
||||
});
|
||||
|
||||
model_polygons.reserve(c);
|
||||
|
||||
c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
|
||||
c = std::accumulate(layer.slices().begin(),
|
||||
layer.slices().end(),
|
||||
size_t(0),
|
||||
[](size_t a, const SliceRecord &sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
});
|
||||
|
||||
@ -1264,8 +1274,9 @@ void SLAPrint::process()
|
||||
// for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i);
|
||||
tbb::parallel_for<size_t, decltype(printlayerfn)>(0, m_printer_input.size(), printlayerfn);
|
||||
|
||||
m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR;
|
||||
m_print_statistics.support_used_material = supports_volume * SCALING2;
|
||||
m_print_statistics.objects_used_material = models_volume * SCALING2;
|
||||
|
||||
// Estimated printing time
|
||||
// A layers count o the highest object
|
||||
@ -1281,7 +1292,7 @@ void SLAPrint::process()
|
||||
};
|
||||
|
||||
// Rasterizing the model objects, and their supports
|
||||
auto rasterize = [this, max_objstatus]() {
|
||||
auto rasterize = [this]() {
|
||||
if(canceled()) return;
|
||||
|
||||
{ // create a raster printer for the current print parameters
|
||||
@ -1352,11 +1363,12 @@ void SLAPrint::process()
|
||||
tbb::parallel_for<unsigned, decltype(lvlfn)>(0, lvlcnt, lvlfn);
|
||||
|
||||
// Set statistics values to the printer
|
||||
m_printer->set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000,
|
||||
double(m_default_object_config.faded_layers.getInt()),
|
||||
double(m_print_statistics.slow_layers_count),
|
||||
double(m_print_statistics.fast_layers_count)
|
||||
});
|
||||
m_printer->set_statistics(
|
||||
{(m_print_statistics.objects_used_material
|
||||
+ m_print_statistics.support_used_material) / 1000,
|
||||
double(m_default_object_config.faded_layers.getInt()),
|
||||
double(m_print_statistics.slow_layers_count),
|
||||
double(m_print_statistics.fast_layers_count)});
|
||||
};
|
||||
|
||||
using slaposFn = std::function<void(SLAPrintObject&)>;
|
||||
@ -1384,25 +1396,36 @@ void SLAPrint::process()
|
||||
|
||||
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
||||
// power available
|
||||
// Calculate the support structures first before slicing the supports, so that the preview will get displayed ASAP for all objects.
|
||||
std::vector<SLAPrintObjectStep> step_ranges = { slaposObjectSlice, slaposSliceSupports, slaposCount };
|
||||
for (size_t idx_range = 0; idx_range + 1 < step_ranges.size(); ++ idx_range) {
|
||||
for(SLAPrintObject * po : m_objects) {
|
||||
// Calculate the support structures first before slicing the supports,
|
||||
// so that the preview will get displayed ASAP for all objects.
|
||||
std::vector<SLAPrintObjectStep> step_ranges = {slaposObjectSlice,
|
||||
slaposSliceSupports,
|
||||
slaposCount};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name;
|
||||
for (size_t idx_range = 0; idx_range + 1 < step_ranges.size(); ++idx_range) {
|
||||
for (SLAPrintObject *po : m_objects) {
|
||||
|
||||
for (int s = int(step_ranges[idx_range]); s < int(step_ranges[idx_range + 1]); ++s) {
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Slicing object " << po->model_object()->name;
|
||||
|
||||
for (int s = int(step_ranges[idx_range]);
|
||||
s < int(step_ranges[idx_range + 1]);
|
||||
++s) {
|
||||
auto currentstep = static_cast<SLAPrintObjectStep>(s);
|
||||
|
||||
// Cancellation checking. Each step will check for cancellation
|
||||
// on its own and return earlier gracefully. Just after it returns
|
||||
// execution gets to this point and throws the canceled signal.
|
||||
// Cancellation checking. Each step will check for
|
||||
// cancellation on its own and return earlier gracefully.
|
||||
// Just after it returns execution gets to this point and
|
||||
// throws the canceled signal.
|
||||
throw_if_canceled();
|
||||
|
||||
st += incr * ostepd;
|
||||
|
||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||
m_report_status(*this, st, OBJ_STEP_LABELS(currentstep));
|
||||
if (po->m_stepmask[currentstep]
|
||||
&& po->set_started(currentstep)) {
|
||||
m_report_status(*this,
|
||||
st,
|
||||
OBJ_STEP_LABELS(currentstep));
|
||||
pobj_program[currentstep](*po);
|
||||
throw_if_canceled();
|
||||
po->set_done(currentstep);
|
||||
@ -1764,8 +1787,8 @@ std::vector<sla::SupportPoint> SLAPrintObject::transformed_support_points() cons
|
||||
ret.reserve(spts.size());
|
||||
|
||||
for(sla::SupportPoint& sp : spts) {
|
||||
Vec3d transformed_pos = trafo() * Vec3d(sp.pos(0), sp.pos(1), sp.pos(2));
|
||||
ret.emplace_back(transformed_pos(0), transformed_pos(1), transformed_pos(2), sp.head_front_radius, sp.is_new_island);
|
||||
Vec3f transformed_pos = trafo().cast<float>() * sp.pos;
|
||||
ret.emplace_back(transformed_pos, sp.head_front_radius, sp.is_new_island);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -54,15 +54,15 @@ public:
|
||||
bool is_left_handed() const { return m_left_handed; }
|
||||
|
||||
struct Instance {
|
||||
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
|
||||
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
|
||||
// ID of the corresponding ModelInstance.
|
||||
ModelID instance_id;
|
||||
// Slic3r::Point objects in scaled G-code coordinates
|
||||
Point shift;
|
||||
// Rotation along the Z axis, in radians.
|
||||
float rotation;
|
||||
};
|
||||
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
|
||||
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
|
||||
// ID of the corresponding ModelInstance.
|
||||
ModelID instance_id;
|
||||
// Slic3r::Point objects in scaled G-code coordinates
|
||||
Point shift;
|
||||
// Rotation along the Z axis, in radians.
|
||||
float rotation;
|
||||
};
|
||||
const std::vector<Instance>& instances() const { return m_instances; }
|
||||
|
||||
bool has_mesh(SLAPrintObjectStep step) const;
|
||||
@ -142,15 +142,19 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
template <class T> inline static T level(const SliceRecord& sr) {
|
||||
template<class T> inline static T level(const SliceRecord &sr)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
|
||||
return std::is_integral<T>::value ? T(sr.print_level()) : T(sr.slice_level());
|
||||
return std::is_integral<T>::value ? T(sr.print_level())
|
||||
: T(sr.slice_level());
|
||||
}
|
||||
|
||||
template <class T> inline static SliceRecord create_slice_record(T val) {
|
||||
template<class T> inline static SliceRecord create_slice_record(T val)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
|
||||
return std::is_integral<T>::value ? SliceRecord{ coord_t(val), 0.f, 0.f } : SliceRecord{ 0, float(val), 0.f };
|
||||
return std::is_integral<T>::value
|
||||
? SliceRecord{coord_t(val), 0.f, 0.f}
|
||||
: SliceRecord{0, float(val), 0.f};
|
||||
}
|
||||
|
||||
// This is a template method for searching the slice index either by
|
||||
@ -241,11 +245,11 @@ protected:
|
||||
~SLAPrintObject();
|
||||
|
||||
void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); }
|
||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
||||
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||
void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false)
|
||||
{ this->m_config.apply_only(other, keys, ignore_nonexistent); }
|
||||
|
||||
void set_trafo(const Transform3d& trafo, bool left_handed) {
|
||||
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
|
||||
m_transformed_rmesh.invalidate([this, &trafo, left_handed](){ m_trafo = trafo; m_left_handed = left_handed; });
|
||||
}
|
||||
|
||||
template<class InstVec> inline void set_instances(InstVec&& instances) { m_instances = std::forward<InstVec>(instances); }
|
||||
@ -349,7 +353,7 @@ public:
|
||||
void set_task(const TaskParams ¶ms) override;
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool is_step_done(SLAPrintObjectStep step) const;
|
||||
// Returns true if the last step was finished with success.
|
||||
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
||||
|
@ -227,7 +227,7 @@ std::vector<coordf_t> layer_height_profile_adaptive(
|
||||
as.set_slicing_parameters(slicing_params);
|
||||
for (const ModelVolume *volume : volumes)
|
||||
if (volume->is_model_part())
|
||||
as.add_mesh(&volume->mesh);
|
||||
as.add_mesh(&volume->mesh());
|
||||
as.prepare();
|
||||
|
||||
// 2) Generate layers using the algorithm of @platsch
|
||||
|
@ -27,8 +27,8 @@ void SlicingAdaptive::prepare()
|
||||
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
|
||||
m_faces.reserve(nfaces_total);
|
||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
|
||||
m_faces.push_back((*it_mesh)->stl.facet_start + i);
|
||||
for (const stl_facet &face : (*it_mesh)->stl.facet_start)
|
||||
m_faces.emplace_back(&face);
|
||||
|
||||
// 2) Sort faces lexicographically by their Z span.
|
||||
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
|
||||
|
@ -42,20 +42,17 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets)
|
||||
: repaired(false)
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets) : repaired(false)
|
||||
{
|
||||
stl_initialize(&this->stl);
|
||||
stl_file &stl = this->stl;
|
||||
stl.error = 0;
|
||||
stl.stats.type = inmemory;
|
||||
|
||||
// count facets and allocate memory
|
||||
stl.stats.number_of_facets = facets.size();
|
||||
stl.stats.number_of_facets = (uint32_t)facets.size();
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
|
||||
for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) {
|
||||
for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) {
|
||||
stl_facet facet;
|
||||
facet.vertex[0] = points[facets[i](0)].cast<float>();
|
||||
facet.vertex[1] = points[facets[i](1)].cast<float>();
|
||||
@ -73,57 +70,37 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& f
|
||||
stl_get_size(&stl);
|
||||
}
|
||||
|
||||
TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
|
||||
{
|
||||
stl_close(&this->stl);
|
||||
this->stl = other.stl;
|
||||
this->repaired = other.repaired;
|
||||
this->stl.heads = nullptr;
|
||||
this->stl.tail = nullptr;
|
||||
this->stl.error = other.stl.error;
|
||||
if (other.stl.facet_start != nullptr) {
|
||||
this->stl.facet_start = (stl_facet*)calloc(other.stl.stats.number_of_facets, sizeof(stl_facet));
|
||||
std::copy(other.stl.facet_start, other.stl.facet_start + other.stl.stats.number_of_facets, this->stl.facet_start);
|
||||
}
|
||||
if (other.stl.neighbors_start != nullptr) {
|
||||
this->stl.neighbors_start = (stl_neighbors*)calloc(other.stl.stats.number_of_facets, sizeof(stl_neighbors));
|
||||
std::copy(other.stl.neighbors_start, other.stl.neighbors_start + other.stl.stats.number_of_facets, this->stl.neighbors_start);
|
||||
}
|
||||
if (other.stl.v_indices != nullptr) {
|
||||
this->stl.v_indices = (v_indices_struct*)calloc(other.stl.stats.number_of_facets, sizeof(v_indices_struct));
|
||||
std::copy(other.stl.v_indices, other.stl.v_indices + other.stl.stats.number_of_facets, this->stl.v_indices);
|
||||
}
|
||||
if (other.stl.v_shared != nullptr) {
|
||||
this->stl.v_shared = (stl_vertex*)calloc(other.stl.stats.shared_vertices, sizeof(stl_vertex));
|
||||
std::copy(other.stl.v_shared, other.stl.v_shared + other.stl.stats.shared_vertices, this->stl.v_shared);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// #define SLIC3R_TRACE_REPAIR
|
||||
|
||||
void TriangleMesh::repair()
|
||||
void TriangleMesh::repair(bool update_shared_vertices)
|
||||
{
|
||||
if (this->repaired) return;
|
||||
|
||||
if (this->repaired) {
|
||||
if (update_shared_vertices)
|
||||
this->require_shared_vertices();
|
||||
return;
|
||||
}
|
||||
|
||||
// admesh fails when repairing empty meshes
|
||||
if (this->stl.stats.number_of_facets == 0) return;
|
||||
if (this->stl.stats.number_of_facets == 0)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
|
||||
|
||||
|
||||
// checking exact
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
assert(stl_validate(&this->stl));
|
||||
stl_check_facets_exact(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
|
||||
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
|
||||
stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge);
|
||||
|
||||
// checking nearby
|
||||
//int last_edges_fixed = 0;
|
||||
float tolerance = stl.stats.shortest_edge;
|
||||
float increment = stl.stats.bounding_diameter / 10000.0;
|
||||
float tolerance = (float)stl.stats.shortest_edge;
|
||||
float increment = (float)stl.stats.bounding_diameter / 10000.0f;
|
||||
int iterations = 2;
|
||||
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
@ -141,6 +118,7 @@ void TriangleMesh::repair()
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(stl_validate(&this->stl));
|
||||
|
||||
// remove_unconnected
|
||||
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
|
||||
@ -148,6 +126,7 @@ void TriangleMesh::repair()
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_remove_unconnected_facets(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
}
|
||||
|
||||
// fill_holes
|
||||
@ -168,28 +147,38 @@ void TriangleMesh::repair()
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_directions(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
|
||||
// normal_values
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_values(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
|
||||
// always calculate the volume and reverse all normals if volume is negative
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_calculate_volume(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
|
||||
// neighbors
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_verify_neighbors(&stl);
|
||||
assert(stl_validate(&this->stl));
|
||||
|
||||
this->repaired = true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
|
||||
|
||||
// This call should be quite cheap, a lot of code requires the indexed_triangle_set data structure,
|
||||
// and it is risky to generate such a structure once the meshes are shared. Do it now.
|
||||
this->its.clear();
|
||||
if (update_shared_vertices)
|
||||
this->require_shared_vertices();
|
||||
}
|
||||
|
||||
float TriangleMesh::volume()
|
||||
@ -249,20 +238,24 @@ bool TriangleMesh::needed_repair() const
|
||||
|
||||
void TriangleMesh::WriteOBJFile(const char* output_file)
|
||||
{
|
||||
stl_generate_shared_vertices(&stl);
|
||||
stl_write_obj(&stl, output_file);
|
||||
its_write_obj(this->its, output_file);
|
||||
}
|
||||
|
||||
void TriangleMesh::scale(float factor)
|
||||
{
|
||||
stl_scale(&(this->stl), factor);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
for (stl_vertex& v : this->its.vertices)
|
||||
v *= factor;
|
||||
}
|
||||
|
||||
void TriangleMesh::scale(const Vec3d &versor)
|
||||
{
|
||||
stl_scale_versor(&this->stl, versor.cast<float>());
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
for (stl_vertex& v : this->its.vertices) {
|
||||
v.x() *= versor.x();
|
||||
v.y() *= versor.y();
|
||||
v.z() *= versor.z();
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleMesh::translate(float x, float y, float z)
|
||||
@ -270,7 +263,9 @@ void TriangleMesh::translate(float x, float y, float z)
|
||||
if (x == 0.f && y == 0.f && z == 0.f)
|
||||
return;
|
||||
stl_translate_relative(&(this->stl), x, y, z);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
stl_vertex shift(x, y, z);
|
||||
for (stl_vertex& v : this->its.vertices)
|
||||
v += shift;
|
||||
}
|
||||
|
||||
void TriangleMesh::translate(const Vec3f &displacement)
|
||||
@ -287,13 +282,15 @@ void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||
angle = Slic3r::Geometry::rad2deg(angle);
|
||||
|
||||
if (axis == X) {
|
||||
stl_rotate_x(&(this->stl), angle);
|
||||
stl_rotate_x(&this->stl, angle);
|
||||
its_rotate_x(this->its, angle);
|
||||
} else if (axis == Y) {
|
||||
stl_rotate_y(&(this->stl), angle);
|
||||
stl_rotate_y(&this->stl, angle);
|
||||
its_rotate_y(this->its, angle);
|
||||
} else if (axis == Z) {
|
||||
stl_rotate_z(&(this->stl), angle);
|
||||
stl_rotate_z(&this->stl, angle);
|
||||
its_rotate_z(this->its, angle);
|
||||
}
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, const Vec3d& axis)
|
||||
@ -305,39 +302,49 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
|
||||
Transform3d m = Transform3d::Identity();
|
||||
m.rotate(Eigen::AngleAxisd(angle, axis_norm));
|
||||
stl_transform(&stl, m);
|
||||
its_transform(its, m);
|
||||
}
|
||||
|
||||
void TriangleMesh::mirror(const Axis &axis)
|
||||
{
|
||||
if (axis == X) {
|
||||
stl_mirror_yz(&this->stl);
|
||||
for (stl_vertex &v : this->its.vertices)
|
||||
v(0) *= -1.0;
|
||||
} else if (axis == Y) {
|
||||
stl_mirror_xz(&this->stl);
|
||||
for (stl_vertex &v : this->its.vertices)
|
||||
v(1) *= -1.0;
|
||||
} else if (axis == Z) {
|
||||
stl_mirror_xy(&this->stl);
|
||||
for (stl_vertex &v : this->its.vertices)
|
||||
v(2) *= -1.0;
|
||||
}
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
||||
{
|
||||
stl_transform(&stl, t);
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
its_transform(its, t);
|
||||
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
|
||||
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
|
||||
this->repair();
|
||||
this->repair(false);
|
||||
stl_reverse_all_facets(&stl);
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
||||
{
|
||||
stl_transform(&stl, m);
|
||||
stl_invalidate_shared_vertices(&stl);
|
||||
its_transform(its, m);
|
||||
if (fix_left_handed && m.determinant() < 0.) {
|
||||
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
|
||||
this->repair();
|
||||
this->repair(false);
|
||||
stl_reverse_all_facets(&stl);
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +362,8 @@ void TriangleMesh::rotate(double angle, Point* center)
|
||||
return;
|
||||
Vec2f c = center->cast<float>();
|
||||
this->translate(-c(0), -c(1), 0);
|
||||
stl_rotate_z(&(this->stl), (float)angle);
|
||||
stl_rotate_z(&this->stl, (float)angle);
|
||||
its_rotate_z(this->its, (float)angle);
|
||||
this->translate(c(0), c(1), 0);
|
||||
}
|
||||
|
||||
@ -435,9 +443,8 @@ TriangleMeshPtrs TriangleMesh::split() const
|
||||
TriangleMesh* mesh = new TriangleMesh;
|
||||
meshes.emplace_back(mesh);
|
||||
mesh->stl.stats.type = inmemory;
|
||||
mesh->stl.stats.number_of_facets = facets.size();
|
||||
mesh->stl.stats.number_of_facets = (uint32_t)facets.size();
|
||||
mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
|
||||
stl_clear_error(&mesh->stl);
|
||||
stl_allocate(&mesh->stl);
|
||||
|
||||
// Assign the facets to the new mesh.
|
||||
@ -455,7 +462,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
|
||||
{
|
||||
// reset stats and metadata
|
||||
int number_of_facets = this->stl.stats.number_of_facets;
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
this->its.clear();
|
||||
this->repaired = false;
|
||||
|
||||
// update facet count and allocate more memory
|
||||
@ -477,13 +484,12 @@ ExPolygons TriangleMesh::horizontal_projection() const
|
||||
{
|
||||
Polygons pp;
|
||||
pp.reserve(this->stl.stats.number_of_facets);
|
||||
for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) {
|
||||
stl_facet* facet = &this->stl.facet_start[i];
|
||||
for (const stl_facet &facet : this->stl.facet_start) {
|
||||
Polygon p;
|
||||
p.points.resize(3);
|
||||
p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1));
|
||||
p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1));
|
||||
p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1));
|
||||
p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
|
||||
p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
|
||||
p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
|
||||
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
|
||||
pp.emplace_back(p);
|
||||
}
|
||||
@ -495,11 +501,10 @@ ExPolygons TriangleMesh::horizontal_projection() const
|
||||
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
|
||||
Polygon TriangleMesh::convex_hull()
|
||||
{
|
||||
this->require_shared_vertices();
|
||||
Points pp;
|
||||
pp.reserve(this->stl.stats.shared_vertices);
|
||||
for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) {
|
||||
const stl_vertex &v = this->stl.v_shared[i];
|
||||
pp.reserve(this->its.vertices.size());
|
||||
for (size_t i = 0; i < this->its.vertices.size(); ++ i) {
|
||||
const stl_vertex &v = this->its.vertices[i];
|
||||
pp.emplace_back(Point::new_scale(v(0), v(1)));
|
||||
}
|
||||
return Slic3r::Geometry::convex_hull(pp);
|
||||
@ -517,49 +522,47 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
|
||||
{
|
||||
BoundingBoxf3 bbox;
|
||||
if (stl.v_shared == nullptr) {
|
||||
if (this->its.vertices.empty()) {
|
||||
// Using the STL faces.
|
||||
for (size_t i = 0; i < this->facets_count(); ++ i) {
|
||||
const stl_facet &facet = this->stl.facet_start[i];
|
||||
for (const stl_facet &facet : this->stl.facet_start)
|
||||
for (size_t j = 0; j < 3; ++ j)
|
||||
bbox.merge(trafo * facet.vertex[j].cast<double>());
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++ i)
|
||||
bbox.merge(trafo * this->stl.v_shared[i].cast<double>());
|
||||
for (const stl_vertex &v : this->its.vertices)
|
||||
bbox.merge(trafo * v.cast<double>());
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
// Helper struct for qhull:
|
||||
struct PointForQHull{
|
||||
PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
|
||||
realT x, y, z;
|
||||
};
|
||||
std::vector<PointForQHull> src_vertices;
|
||||
|
||||
// We will now fill the vector with input points for computation:
|
||||
stl_facet* facet_ptr = stl.facet_start;
|
||||
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
const stl_vertex& v = facet_ptr->vertex[i];
|
||||
src_vertices.emplace_back(v(0), v(1), v(2));
|
||||
}
|
||||
|
||||
facet_ptr += 1;
|
||||
}
|
||||
|
||||
// The qhull call:
|
||||
orgQhull::Qhull qhull;
|
||||
qhull.disableOutputStream(); // we want qhull to be quiet
|
||||
try
|
||||
std::vector<realT> src_vertices;
|
||||
try
|
||||
{
|
||||
qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
|
||||
if (this->has_shared_vertices()) {
|
||||
#if REALfloat
|
||||
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
|
||||
#else
|
||||
src_vertices.reserve(this->its.vertices() * 3);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_vertex &v : ths->its.vertices.size())
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
src_vertices.emplace_back(v(i));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
#endif
|
||||
} else {
|
||||
src_vertices.reserve(this->stl.facet_start.size() * 9);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_facet &f : this->stl.facet_start)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
for (int j = 0; j < 3; ++ j)
|
||||
src_vertices.emplace_back(f.vertex[i](j));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -578,7 +581,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{ // iterate through facet's vertices
|
||||
|
||||
orgQhull::QhullPoint p = vertices[i].point();
|
||||
const float* coords = p.coordinates();
|
||||
const auto* coords = p.coordinates();
|
||||
dst_vertices.emplace_back(coords[0], coords[1], coords[2]);
|
||||
}
|
||||
unsigned int size = (unsigned int)dst_vertices.size();
|
||||
@ -587,34 +590,20 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
|
||||
TriangleMesh output_mesh(dst_vertices, facets);
|
||||
output_mesh.repair();
|
||||
output_mesh.require_shared_vertices();
|
||||
return output_mesh;
|
||||
}
|
||||
|
||||
void TriangleMesh::require_shared_vertices()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
|
||||
if (!this->repaired)
|
||||
assert(stl_validate(&this->stl));
|
||||
if (! this->repaired)
|
||||
this->repair();
|
||||
if (this->stl.v_shared == NULL) {
|
||||
if (this->its.vertices.empty()) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
|
||||
stl_generate_shared_vertices(&(this->stl));
|
||||
stl_generate_shared_vertices(&this->stl, this->its);
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
// Verify validity of neighborship data.
|
||||
for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
|
||||
const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
|
||||
const int *vertices = stl.v_indices[facet_idx].vertex;
|
||||
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
|
||||
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
|
||||
if (nbr_face != -1) {
|
||||
assert(
|
||||
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]) ||
|
||||
(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[(nbr_idx + 1) % 3] && stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[nbr_idx]));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
assert(stl_validate(&this->stl, this->its));
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
|
||||
}
|
||||
|
||||
@ -626,10 +615,9 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac
|
||||
|
||||
throw_on_cancel();
|
||||
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
|
||||
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);
|
||||
// Scale the copied vertices.
|
||||
for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i)
|
||||
this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR);
|
||||
v_scaled_shared.assign(_mesh->its.vertices.size(), stl_vertex());
|
||||
for (size_t i = 0; i < v_scaled_shared.size(); ++ i)
|
||||
this->v_scaled_shared[i] = _mesh->its.vertices[i] / float(SCALING_FACTOR);
|
||||
|
||||
// Create a mapping from triangle edge into face.
|
||||
struct EdgeToFace {
|
||||
@ -649,8 +637,8 @@ void TriangleMeshSlicer::init(const TriangleMesh *_mesh, throw_on_cancel_callbac
|
||||
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
|
||||
for (int i = 0; i < 3; ++ i) {
|
||||
EdgeToFace &e2f = edges_map[facet_idx*3+i];
|
||||
e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i];
|
||||
e2f.vertex_high = this->mesh->stl.v_indices[facet_idx].vertex[(i + 1) % 3];
|
||||
e2f.vertex_low = this->mesh->its.indices[facet_idx][i];
|
||||
e2f.vertex_high = this->mesh->its.indices[facet_idx][(i + 1) % 3];
|
||||
e2f.face = facet_idx;
|
||||
// 1 based indexing, to be always strictly positive.
|
||||
e2f.face_edge = i + 1;
|
||||
@ -818,7 +806,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
|
||||
void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
|
||||
const std::vector<float> &z) const
|
||||
{
|
||||
const stl_facet &facet = m_use_quaternion ? this->mesh->stl.facet_start[facet_idx].rotated(m_quaternion) : this->mesh->stl.facet_start[facet_idx];
|
||||
const stl_facet &facet = m_use_quaternion ? (this->mesh->stl.facet_start.data() + facet_idx)->rotated(m_quaternion) : *(this->mesh->stl.facet_start.data() + facet_idx);
|
||||
|
||||
// find facet extents
|
||||
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
|
||||
@ -887,7 +875,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
|
||||
// Reorder vertices so that the first one is the one with lowest Z.
|
||||
// This is needed to get all intersection lines in a consistent order
|
||||
// (external on the right of the line)
|
||||
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
||||
const stl_triangle_vertex_indices &vertices = this->mesh->its.indices[facet_idx];
|
||||
int i = (facet.vertex[1].z() == min_z) ? 1 : ((facet.vertex[2].z() == min_z) ? 2 : 0);
|
||||
|
||||
// These are used only if the cut plane is tilted:
|
||||
@ -1714,7 +1702,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object";
|
||||
float scaled_z = scale_(z);
|
||||
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
|
||||
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
|
||||
const stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
|
||||
|
||||
// find facet extents
|
||||
float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2)));
|
||||
@ -1736,10 +1724,12 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
|
||||
if (min_z > z || (min_z == z && max_z > z)) {
|
||||
// facet is above the cut plane and does not belong to it
|
||||
if (upper != NULL) stl_add_facet(&upper->stl, facet);
|
||||
if (upper != nullptr)
|
||||
stl_add_facet(&upper->stl, facet);
|
||||
} else if (max_z < z || (max_z == z && min_z < z)) {
|
||||
// facet is below the cut plane and does not belong to it
|
||||
if (lower != NULL) stl_add_facet(&lower->stl, facet);
|
||||
if (lower != nullptr)
|
||||
stl_add_facet(&lower->stl, facet);
|
||||
} else if (min_z < z && max_z > z) {
|
||||
// Facet is cut by the slicing plane.
|
||||
|
||||
@ -1786,22 +1776,24 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
quadrilateral[1].vertex[2] = v0v1;
|
||||
|
||||
if (v0(2) > z) {
|
||||
if (upper != NULL) stl_add_facet(&upper->stl, &triangle);
|
||||
if (lower != NULL) {
|
||||
if (upper != nullptr)
|
||||
stl_add_facet(&upper->stl, &triangle);
|
||||
if (lower != nullptr) {
|
||||
stl_add_facet(&lower->stl, &quadrilateral[0]);
|
||||
stl_add_facet(&lower->stl, &quadrilateral[1]);
|
||||
}
|
||||
} else {
|
||||
if (upper != NULL) {
|
||||
if (upper != nullptr) {
|
||||
stl_add_facet(&upper->stl, &quadrilateral[0]);
|
||||
stl_add_facet(&upper->stl, &quadrilateral[1]);
|
||||
}
|
||||
if (lower != NULL) stl_add_facet(&lower->stl, &triangle);
|
||||
if (lower != nullptr)
|
||||
stl_add_facet(&lower->stl, &triangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (upper != NULL) {
|
||||
if (upper != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part";
|
||||
ExPolygons section;
|
||||
this->make_expolygons_simple(upper_lines, §ion);
|
||||
@ -1815,7 +1807,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
}
|
||||
}
|
||||
|
||||
if (lower != NULL) {
|
||||
if (lower != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part";
|
||||
ExPolygons section;
|
||||
this->make_expolygons_simple(lower_lines, §ion);
|
||||
@ -1905,10 +1897,10 @@ TriangleMesh make_cylinder(double r, double h, double fa)
|
||||
//FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
|
||||
TriangleMesh make_sphere(double radius, double fa)
|
||||
{
|
||||
int sectorCount = ceil(2. * M_PI / fa);
|
||||
int stackCount = ceil(M_PI / fa);
|
||||
float sectorStep = 2. * M_PI / sectorCount;
|
||||
float stackStep = M_PI / stackCount;
|
||||
int sectorCount = int(ceil(2. * M_PI / fa));
|
||||
int stackCount = int(ceil(M_PI / fa));
|
||||
float sectorStep = float(2. * M_PI / sectorCount);
|
||||
float stackStep = float(M_PI / stackCount);
|
||||
|
||||
Pointf3s vertices;
|
||||
vertices.reserve((stackCount - 1) * sectorCount + 2);
|
||||
|
@ -21,19 +21,13 @@ typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
|
||||
class TriangleMesh
|
||||
{
|
||||
public:
|
||||
TriangleMesh() : repaired(false) { stl_initialize(&this->stl); }
|
||||
TriangleMesh() : repaired(false) {}
|
||||
TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets);
|
||||
TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; }
|
||||
TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); }
|
||||
~TriangleMesh() { clear(); }
|
||||
TriangleMesh& operator=(const TriangleMesh &other);
|
||||
TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; }
|
||||
void clear() { stl_close(&this->stl); this->repaired = false; }
|
||||
void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
|
||||
void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
|
||||
void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
|
||||
void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); }
|
||||
void repair();
|
||||
void clear() { this->stl.clear(); this->its.clear(); this->repaired = false; }
|
||||
bool ReadSTLFile(const char* input_file) { return stl_open(&stl, input_file); }
|
||||
bool write_ascii(const char* output_file) { return stl_write_ascii(&this->stl, output_file, ""); }
|
||||
bool write_binary(const char* output_file) { return stl_write_binary(&this->stl, output_file, ""); }
|
||||
void repair(bool update_shared_vertices = true);
|
||||
float volume();
|
||||
void check_topology();
|
||||
bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; }
|
||||
@ -58,7 +52,7 @@ public:
|
||||
TriangleMeshPtrs split() const;
|
||||
void merge(const TriangleMesh &mesh);
|
||||
ExPolygons horizontal_projection() const;
|
||||
const float* first_vertex() const { return this->stl.facet_start ? &this->stl.facet_start->vertex[0](0) : nullptr; }
|
||||
const float* first_vertex() const { return this->stl.facet_start.empty() ? nullptr : &this->stl.facet_start.front().vertex[0](0); }
|
||||
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
|
||||
Polygon convex_hull();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
@ -69,12 +63,13 @@ public:
|
||||
void reset_repair_stats();
|
||||
bool needed_repair() const;
|
||||
void require_shared_vertices();
|
||||
bool has_shared_vertices() const { return stl.v_shared != NULL; }
|
||||
bool has_shared_vertices() const { return ! this->its.vertices.empty(); }
|
||||
size_t facets_count() const { return this->stl.stats.number_of_facets; }
|
||||
bool empty() const { return this->facets_count() == 0; }
|
||||
bool is_splittable() const;
|
||||
|
||||
stl_file stl;
|
||||
indexed_triangle_set its;
|
||||
bool repaired;
|
||||
|
||||
private:
|
||||
|
@ -48,10 +48,33 @@ typedef double coordf_t;
|
||||
//FIXME Better to use an inline function with an explicit return type.
|
||||
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
|
||||
#define scale_(val) ((val) / SCALING_FACTOR)
|
||||
|
||||
#define SCALED_EPSILON scale_(EPSILON)
|
||||
|
||||
#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/"
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
# define SLIC3R_CONSTEXPR
|
||||
# define SLIC3R_NOEXCEPT
|
||||
#else
|
||||
#define SLIC3R_CONSTEXPR constexpr
|
||||
#define SLIC3R_NOEXCEPT noexcept
|
||||
#endif
|
||||
|
||||
template<class Tf> inline SLIC3R_CONSTEXPR coord_t scaled(Tf val)
|
||||
{
|
||||
static_assert (std::is_floating_point<Tf>::value, "Floating point only");
|
||||
return coord_t(val / Tf(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
template<class Tf = double> inline SLIC3R_CONSTEXPR Tf unscaled(coord_t val)
|
||||
{
|
||||
static_assert (std::is_floating_point<Tf>::value, "Floating point only");
|
||||
return Tf(val * Tf(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
inline SLIC3R_CONSTEXPR float unscaledf(coord_t val) { return unscaled<float>(val); }
|
||||
|
||||
inline std::string debug_out_path(const char *name, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
|
@ -8,6 +8,22 @@
|
||||
# Created by modification of the original qhull CMakeLists.
|
||||
# Lukas Matena (25.7.2018), lukasmatena@seznam.cz
|
||||
|
||||
# see bug report: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925540
|
||||
|
||||
find_package(Qhull 7.2 QUIET)
|
||||
|
||||
add_library(qhull INTERFACE)
|
||||
|
||||
if(Qhull_FOUND)
|
||||
|
||||
message(STATUS "Using qhull from system.")
|
||||
if(SLIC3R_STATIC)
|
||||
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhullstatic_r)
|
||||
else()
|
||||
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhull_r)
|
||||
endif()
|
||||
|
||||
else(Qhull_FOUND)
|
||||
|
||||
project(qhull)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
@ -112,7 +128,7 @@ set(libqhull_SOURCES
|
||||
|
||||
##################################################
|
||||
# combined library (reentrant qhull and qhullcpp) for Slic3r:
|
||||
set(qhull_STATIC qhull)
|
||||
set(qhull_STATIC qhullstatic)
|
||||
add_library(${qhull_STATIC} STATIC ${libqhull_SOURCES})
|
||||
set_target_properties(${qhull_STATIC} PROPERTIES
|
||||
VERSION ${qhull_VERSION})
|
||||
@ -123,4 +139,7 @@ endif(UNIX)
|
||||
##################################################
|
||||
|
||||
# LIBDIR is defined in the main xs CMake file:
|
||||
target_include_directories(${qhull_STATIC} PRIVATE ${LIBDIR}/qhull/src)
|
||||
target_include_directories(${qhull_STATIC} BEFORE PUBLIC ${LIBDIR}/qhull/src)
|
||||
target_link_libraries(qhull INTERFACE ${qhull_STATIC})
|
||||
|
||||
endif()
|
||||
|
@ -198,6 +198,11 @@ size_t Index::load(const boost::filesystem::path &path)
|
||||
size_t idx_line = 0;
|
||||
Version ver;
|
||||
while (std::getline(ifs, line)) {
|
||||
#ifndef _MSVCVER
|
||||
// On a Unix system, getline does not remove the trailing carriage returns, if the index is shared over a Windows filesystem. Remove them manually.
|
||||
while (! line.empty() && line.back() == '\r')
|
||||
line.pop_back();
|
||||
#endif
|
||||
++ idx_line;
|
||||
// Skip the initial white spaces.
|
||||
char *key = left_trim(const_cast<char*>(line.data()));
|
||||
|
@ -19,8 +19,6 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
|
||||
m_user_drawn_background = false;
|
||||
#endif /*__APPLE__*/
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
|
||||
Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent &event) { mouse_event(event); }));
|
||||
Bind(wxEVT_MOTION, ([this](wxMouseEvent &event) { mouse_event(event); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent & /* e */) { Refresh(); }));
|
||||
}
|
||||
void Bed_2D::repaint()
|
||||
@ -43,22 +41,14 @@ void Bed_2D::repaint()
|
||||
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
|
||||
}
|
||||
|
||||
// turn cw and ch from sizes to max coordinates
|
||||
cw--;
|
||||
ch--;
|
||||
if (m_bed_shape.empty())
|
||||
return;
|
||||
|
||||
// reduce size to have some space around the drawn shape
|
||||
cw -= (2 * Border);
|
||||
ch -= (2 * Border);
|
||||
|
||||
auto cbb = BoundingBoxf(Vec2d(0, 0),Vec2d(cw, ch));
|
||||
// leave space for origin point
|
||||
cbb.min(0) += 4;
|
||||
cbb.max -= Vec2d(4., 4.);
|
||||
|
||||
// leave space for origin label
|
||||
cbb.max(1) -= 13;
|
||||
|
||||
// read new size
|
||||
cw = cbb.size()(0);
|
||||
ch = cbb.size()(1);
|
||||
|
||||
auto ccenter = cbb.center();
|
||||
|
||||
// get bounding box of bed shape in G - code coordinates
|
||||
@ -76,17 +66,17 @@ void Bed_2D::repaint()
|
||||
ccenter(0) - bcenter(0) * sfactor,
|
||||
ccenter(1) - bcenter(1) * sfactor
|
||||
);
|
||||
|
||||
m_scale_factor = sfactor;
|
||||
m_shift = Vec2d(shift(0) + cbb.min(0),
|
||||
shift(1) - (cbb.max(1) - GetSize().GetHeight()));
|
||||
m_shift = Vec2d(shift(0) + cbb.min(0), shift(1) - (cbb.max(1) - ch));
|
||||
|
||||
// draw bed fill
|
||||
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
|
||||
wxPointList pt_list;
|
||||
for (auto pt: m_bed_shape)
|
||||
{
|
||||
Point pt_pix = to_pixels(pt);
|
||||
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
|
||||
Point pt_pix = to_pixels(pt, ch);
|
||||
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
|
||||
}
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
@ -105,9 +95,9 @@ void Bed_2D::repaint()
|
||||
for (auto pl : polylines)
|
||||
{
|
||||
for (size_t i = 0; i < pl.points.size()-1; i++) {
|
||||
Point pt1 = to_pixels(unscale(pl.points[i]));
|
||||
Point pt2 = to_pixels(unscale(pl.points[i+1]));
|
||||
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
|
||||
Point pt1 = to_pixels(unscale(pl.points[i]), ch);
|
||||
Point pt2 = to_pixels(unscale(pl.points[i + 1]), ch);
|
||||
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +106,7 @@ void Bed_2D::repaint()
|
||||
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
|
||||
dc.DrawPolygon(&pt_list, 0, 0);
|
||||
|
||||
auto origin_px = to_pixels(Vec2d(0, 0));
|
||||
auto origin_px = to_pixels(Vec2d(0, 0), ch);
|
||||
|
||||
// draw axes
|
||||
auto axes_len = 50;
|
||||
@ -153,7 +143,7 @@ void Bed_2D::repaint()
|
||||
|
||||
// draw current position
|
||||
if (m_pos!= Vec2d(0, 0)) {
|
||||
auto pos_px = to_pixels(m_pos);
|
||||
auto pos_px = to_pixels(m_pos, ch);
|
||||
dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxPENSTYLE_SOLID));
|
||||
dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
|
||||
dc.DrawCircle(pos_px(0), pos_px(1), 5);
|
||||
@ -161,35 +151,14 @@ void Bed_2D::repaint()
|
||||
dc.DrawLine(pos_px(0) - 15, pos_px(1), pos_px(0) + 15, pos_px(1));
|
||||
dc.DrawLine(pos_px(0), pos_px(1) - 15, pos_px(0), pos_px(1) + 15);
|
||||
}
|
||||
|
||||
m_painted = true;
|
||||
}
|
||||
|
||||
|
||||
// convert G - code coordinates into pixels
|
||||
Point Bed_2D::to_pixels(Vec2d point)
|
||||
Point Bed_2D::to_pixels(Vec2d point, int height)
|
||||
{
|
||||
auto p = point * m_scale_factor + m_shift;
|
||||
return Point(p(0), GetSize().GetHeight() - p(1));
|
||||
}
|
||||
|
||||
void Bed_2D::mouse_event(wxMouseEvent event)
|
||||
{
|
||||
if (!m_interactive) return;
|
||||
if (!m_painted) return;
|
||||
|
||||
auto pos = event.GetPosition();
|
||||
auto point = to_units(Point(pos.x, pos.y));
|
||||
if (event.LeftDown() || event.Dragging()) {
|
||||
if (m_on_move)
|
||||
m_on_move(point) ;
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// convert pixels into G - code coordinates
|
||||
Vec2d Bed_2D::to_units(Point point)
|
||||
{
|
||||
return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor);
|
||||
return Point(p(0) + Border, height - p(1) + Border);
|
||||
}
|
||||
|
||||
void Bed_2D::set_pos(Vec2d pos)
|
||||
|
@ -9,20 +9,17 @@ namespace GUI {
|
||||
|
||||
class Bed_2D : public wxPanel
|
||||
{
|
||||
static const int Border = 10;
|
||||
|
||||
bool m_user_drawn_background = true;
|
||||
|
||||
bool m_painted = false;
|
||||
bool m_interactive = false;
|
||||
double m_scale_factor;
|
||||
double m_scale_factor;
|
||||
Vec2d m_shift = Vec2d::Zero();
|
||||
Vec2d m_pos = Vec2d::Zero();
|
||||
std::function<void(Vec2d)> m_on_move = nullptr;
|
||||
|
||||
Point to_pixels(Vec2d point);
|
||||
Vec2d to_units(Point point);
|
||||
void repaint();
|
||||
void mouse_event(wxMouseEvent event);
|
||||
void set_pos(Vec2d pos);
|
||||
Point to_pixels(Vec2d point, int height);
|
||||
void repaint();
|
||||
void set_pos(Vec2d pos);
|
||||
|
||||
public:
|
||||
Bed_2D(wxWindow* parent);
|
||||
|
@ -241,8 +241,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
: m_transformed_bounding_box_dirty(true)
|
||||
, m_sla_shift_z(0.0)
|
||||
, m_transformed_convex_hull_bounding_box_dirty(true)
|
||||
, m_convex_hull(nullptr)
|
||||
, m_convex_hull_owned(false)
|
||||
// geometry_id == 0 -> invalid
|
||||
, geometry_id(std::pair<size_t, size_t>(0, 0))
|
||||
, extruder_id(0)
|
||||
@ -268,12 +266,6 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
set_render_color(r, g, b, a);
|
||||
}
|
||||
|
||||
GLVolume::~GLVolume()
|
||||
{
|
||||
if (m_convex_hull_owned)
|
||||
delete m_convex_hull;
|
||||
}
|
||||
|
||||
void GLVolume::set_render_color(float r, float g, float b, float a)
|
||||
{
|
||||
render_color[0] = r;
|
||||
@ -335,12 +327,6 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume)
|
||||
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||
}
|
||||
|
||||
void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned)
|
||||
{
|
||||
m_convex_hull = convex_hull;
|
||||
m_convex_hull_owned = owned;
|
||||
}
|
||||
|
||||
Transform3d GLVolume::world_matrix() const
|
||||
{
|
||||
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
|
||||
@ -377,7 +363,7 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const
|
||||
|
||||
BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &trafo) const
|
||||
{
|
||||
return (m_convex_hull != nullptr && m_convex_hull->stl.stats.number_of_facets > 0) ?
|
||||
return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
|
||||
m_convex_hull->transformed_bounding_box(trafo) :
|
||||
bounding_box.transformed(trafo);
|
||||
}
|
||||
@ -587,7 +573,7 @@ int GLVolumeCollection::load_object_volume(
|
||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
const int extruder_id = model_volume->extruder_id();
|
||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
||||
const TriangleMesh& mesh = model_volume->mesh;
|
||||
const TriangleMesh& mesh = model_volume->mesh();
|
||||
float color[4];
|
||||
memcpy(color, GLVolume::MODEL_COLOR[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
|
||||
/* if (model_volume->is_support_blocker()) {
|
||||
@ -613,7 +599,7 @@ int GLVolumeCollection::load_object_volume(
|
||||
if (model_volume->is_model_part())
|
||||
{
|
||||
// GLVolume will reference a convex hull from model_volume!
|
||||
v.set_convex_hull(&model_volume->get_convex_hull(), false);
|
||||
v.set_convex_hull(model_volume->get_convex_hull_shared_ptr());
|
||||
if (extruder_id != -1)
|
||||
v.extruder_id = extruder_id;
|
||||
}
|
||||
@ -656,7 +642,10 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, - int(milestone), (int)instance_idx.first);
|
||||
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
||||
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||
if (&instance_idx == &instances.back())
|
||||
v.set_convex_hull(std::move(convex_hull));
|
||||
else
|
||||
v.set_convex_hull(convex_hull);
|
||||
v.is_modifier = false;
|
||||
v.shader_outside_printer_detection_enabled = (milestone == slaposSupportTree);
|
||||
v.set_instance_transformation(model_instance.get_transformation());
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "slic3r/GUI/GLCanvas3DManager.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define HAS_GLSAFE
|
||||
@ -243,7 +244,6 @@ public:
|
||||
|
||||
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
|
||||
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
|
||||
~GLVolume();
|
||||
|
||||
private:
|
||||
Geometry::Transformation m_instance_transformation;
|
||||
@ -255,10 +255,8 @@ private:
|
||||
mutable BoundingBoxf3 m_transformed_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed bounding box.
|
||||
mutable bool m_transformed_bounding_box_dirty;
|
||||
// Pointer to convex hull of the original mesh, if any.
|
||||
// This object may or may not own the convex hull instance based on m_convex_hull_owned
|
||||
const TriangleMesh* m_convex_hull;
|
||||
bool m_convex_hull_owned;
|
||||
// Convex hull of the volume, if any.
|
||||
std::shared_ptr<const TriangleMesh> m_convex_hull;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed convex hull bounding box.
|
||||
@ -395,7 +393,9 @@ public:
|
||||
double get_sla_shift_z() const { return m_sla_shift_z; }
|
||||
void set_sla_shift_z(double z) { m_sla_shift_z = z; }
|
||||
|
||||
void set_convex_hull(const TriangleMesh *convex_hull, bool owned);
|
||||
void set_convex_hull(std::shared_ptr<const TriangleMesh> convex_hull) { m_convex_hull = std::move(convex_hull); }
|
||||
void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(convex_hull); }
|
||||
void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(std::move(convex_hull)); }
|
||||
|
||||
int object_idx() const { return this->composite_id.object_id; }
|
||||
int volume_idx() const { return this->composite_id.volume_id; }
|
||||
|
@ -89,7 +89,7 @@ void BackgroundSlicingProcess::process_fff()
|
||||
// Perform the final post-processing of the export path by applying the print statistics over the file name.
|
||||
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
|
||||
if (copy_file(m_temp_output_path, export_path) != 0)
|
||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
|
||||
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
|
||||
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
|
||||
run_post_process_scripts(export_path, m_fff_print->config());
|
||||
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());
|
||||
|
@ -30,11 +30,9 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
|
||||
SetMinSize(GetSize());
|
||||
main_sizer->SetSizeHints(this);
|
||||
|
||||
// needed to actually free memory
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e) {
|
||||
EndModal(wxID_OK);
|
||||
Destroy();
|
||||
}));
|
||||
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) {
|
||||
EndModal(wxID_CANCEL);
|
||||
}));
|
||||
}
|
||||
|
||||
void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
@ -135,7 +133,7 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||
|
||||
// Called from the constructor.
|
||||
// Create a panel for a rectangular / circular / custom bed shape.
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
|
||||
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
|
||||
{
|
||||
|
||||
auto panel = new wxPanel(m_shape_options_book);
|
||||
@ -305,8 +303,9 @@ void BedShapePanel::update_shape()
|
||||
}
|
||||
m_canvas->m_bed_shape = points;
|
||||
}
|
||||
else if (page_idx == SHAPE_CUSTOM)
|
||||
m_canvas->m_bed_shape = m_loaded_bed_shape;
|
||||
|
||||
// $self->{on_change}->();
|
||||
update_preview();
|
||||
}
|
||||
|
||||
@ -351,8 +350,9 @@ void BedShapePanel::load_stl()
|
||||
std::vector<Vec2d> points;
|
||||
for (auto pt : polygon.points)
|
||||
points.push_back(unscale(pt));
|
||||
m_canvas->m_bed_shape = points;
|
||||
update_preview();
|
||||
|
||||
m_loaded_bed_shape = points;
|
||||
update_shape();
|
||||
}
|
||||
|
||||
} // GUI
|
||||
|
@ -16,7 +16,8 @@ namespace GUI {
|
||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||
class BedShapePanel : public wxPanel
|
||||
{
|
||||
Bed_2D* m_canvas;
|
||||
Bed_2D* m_canvas;
|
||||
std::vector<Vec2d> m_loaded_bed_shape;
|
||||
|
||||
public:
|
||||
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
|
||||
@ -24,8 +25,8 @@ public:
|
||||
|
||||
void build_panel(ConfigOptionPoints* default_pt);
|
||||
|
||||
ConfigOptionsGroupShp init_shape_options_page(wxString title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
|
||||
void set_shape(ConfigOptionPoints* points);
|
||||
void update_preview();
|
||||
void update_shape();
|
||||
void load_stl();
|
||||
|
@ -56,9 +56,9 @@ public:
|
||||
|
||||
Vec3d get_dir_right() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(0); }
|
||||
Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); }
|
||||
Vec3d get_dir_forward() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
|
||||
Vec3d get_dir_forward() const { return -m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
|
||||
|
||||
Vec3d get_position() const { return m_view_matrix.matrix().block(0, 3, 3, 1); }
|
||||
Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); }
|
||||
|
||||
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
|
||||
void apply_view_matrix() const;
|
||||
|
@ -63,11 +63,6 @@ static const float GROUND_Z = -0.02f;
|
||||
static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f;
|
||||
static const float GIZMO_RESET_BUTTON_WIDTH = 70.f;
|
||||
|
||||
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f };
|
||||
static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f };
|
||||
static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f };
|
||||
@ -452,8 +447,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
||||
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)m_layers_texture.height);
|
||||
m_shader.set_uniform("z_cursor", m_object_max_z * this->get_cursor_z_relative(canvas));
|
||||
m_shader.set_uniform("z_cursor_band_width", band_width);
|
||||
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
|
||||
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
|
||||
m_shader.set_uniform("object_max_z", m_object_max_z);
|
||||
|
||||
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_z_texture_id));
|
||||
@ -466,10 +460,10 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
||||
|
||||
::glBegin(GL_QUADS);
|
||||
::glNormal3f(0.0f, 0.0f, 1.0f);
|
||||
::glVertex3f(l, b, 0.0f);
|
||||
::glVertex3f(r, b, 0.0f);
|
||||
::glVertex3f(r, t, m_object_max_z);
|
||||
::glVertex3f(l, t, m_object_max_z);
|
||||
::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(l, b);
|
||||
::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(r, b);
|
||||
::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(r, t);
|
||||
::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(l, t);
|
||||
glsafe(::glEnd());
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
@ -522,6 +516,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
||||
GLint z_cursor_id = ::glGetUniformLocation(shader_id, "z_cursor");
|
||||
GLint z_cursor_band_width_id = ::glGetUniformLocation(shader_id, "z_cursor_band_width");
|
||||
GLint world_matrix_id = ::glGetUniformLocation(shader_id, "volume_world_matrix");
|
||||
GLint object_max_z_id = ::glGetUniformLocation(shader_id, "object_max_z");
|
||||
glcheck();
|
||||
|
||||
if (z_to_texture_row_id != -1 && z_texture_row_to_normalized_id != -1 && z_cursor_id != -1 && z_cursor_band_width_id != -1 && world_matrix_id != -1)
|
||||
@ -548,7 +543,10 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
||||
// Render the object using the layer editing shader and texture.
|
||||
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
||||
continue;
|
||||
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
||||
if (world_matrix_id != -1)
|
||||
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
||||
if (object_max_z_id != -1)
|
||||
glsafe(::glUniform1f(object_max_z_id, GLfloat(0)));
|
||||
glvolume->render();
|
||||
}
|
||||
// Revert back to the previous shader.
|
||||
@ -1210,6 +1208,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
|
||||
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
|
||||
: m_canvas(canvas)
|
||||
@ -1579,7 +1578,13 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
|
||||
|
||||
void GLCanvas3D::render()
|
||||
{
|
||||
wxCHECK_RET(!m_in_render, "GLCanvas3D::render() called recursively");
|
||||
if (m_in_render)
|
||||
{
|
||||
// if called recursively, return
|
||||
m_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_in_render = true;
|
||||
Slic3r::ScopeGuard in_render_guard([this]() { m_in_render = false; });
|
||||
(void)in_render_guard;
|
||||
@ -2463,6 +2468,20 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||
}
|
||||
else if (keyCode == WXK_CONTROL)
|
||||
m_dirty = true;
|
||||
// DoubleSlider navigation in Preview
|
||||
else if (keyCode == WXK_LEFT ||
|
||||
keyCode == WXK_RIGHT ||
|
||||
keyCode == WXK_UP ||
|
||||
keyCode == WXK_DOWN ||
|
||||
keyCode == '+' ||
|
||||
keyCode == WXK_NUMPAD_ADD ||
|
||||
keyCode == '-' ||
|
||||
keyCode == 390 ||
|
||||
keyCode == WXK_DELETE ||
|
||||
keyCode == WXK_BACK )
|
||||
{
|
||||
post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5498,7 +5517,7 @@ void GLCanvas3D::_load_sla_shells()
|
||||
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
|
||||
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
||||
v.set_instance_mirror(X, object.is_left_handed() ? -1. : 1.);
|
||||
v.set_convex_hull(new TriangleMesh(std::move(mesh.convex_hull_3d())), true);
|
||||
v.set_convex_hull(mesh.convex_hull_3d());
|
||||
};
|
||||
|
||||
// adds objects' volumes
|
||||
|
@ -124,6 +124,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
|
@ -261,7 +261,7 @@ wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /
|
||||
|
||||
const stl_stats& stats = vol_idx == -1 ?
|
||||
(*m_objects)[obj_idx]->get_object_stl_stats() :
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats;
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stl.stats;
|
||||
|
||||
std::map<std::string, int> error_msg = {
|
||||
{ L("degenerate facets"), stats.degenerate_facets },
|
||||
@ -1415,13 +1415,18 @@ void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
|
||||
|
||||
void ObjectList::load_subobject(ModelVolumeType type)
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0))
|
||||
wxDataViewItem item = GetSelection();
|
||||
// we can add volumes for Object or Instance
|
||||
if (!item || !(m_objects_model->GetItemType(item)&(itObject|itInstance)))
|
||||
return;
|
||||
int obj_idx = m_objects_model->GetIdByItem(item);
|
||||
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||
|
||||
if (obj_idx < 0) return;
|
||||
|
||||
// Get object item, if Instance is selected
|
||||
if (m_objects_model->GetItemType(item)&itInstance)
|
||||
item = m_objects_model->GetItemById(obj_idx);
|
||||
|
||||
std::vector<std::pair<wxString, bool>> volumes_info;
|
||||
load_part((*m_objects)[obj_idx], volumes_info, type);
|
||||
|
||||
@ -1592,7 +1597,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
|
||||
// First (any) GLVolume of the selected instance. They all share the same instance matrix.
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
// Transform the new modifier to be aligned with the print bed.
|
||||
const BoundingBoxf3 mesh_bb = new_volume->mesh.bounding_box();
|
||||
const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box();
|
||||
new_volume->set_transformation(volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb));
|
||||
// Set the modifier position.
|
||||
auto offset = (type_name == "Slab") ?
|
||||
@ -2150,9 +2155,11 @@ void ObjectList::update_selections()
|
||||
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
||||
{
|
||||
const auto item = GetSelection();
|
||||
if (selection.is_single_full_object() &&
|
||||
m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
||||
return;
|
||||
if (selection.is_single_full_object()) {
|
||||
if ( m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
||||
return;
|
||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
||||
}
|
||||
if (selection.is_single_volume() || selection.is_any_modifier()) {
|
||||
const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx())
|
||||
|
@ -92,6 +92,7 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo)
|
||||
combo->SetValue(selection);
|
||||
}
|
||||
|
||||
|
||||
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
OG_Settings(parent, true)
|
||||
#ifndef __APPLE__
|
||||
@ -162,16 +163,71 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
|
||||
const int field_width = 5;
|
||||
|
||||
// Mirror button size:
|
||||
const int mirror_btn_width = 3;
|
||||
|
||||
// Legend for object modification
|
||||
line = Line{ "", "" };
|
||||
def.label = "";
|
||||
def.type = coString;
|
||||
def.width = field_width/*50*/;
|
||||
def.width = field_width - mirror_btn_width;//field_width/*50*/;
|
||||
|
||||
// Load bitmaps to be used for the mirroring buttons:
|
||||
m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on.png");
|
||||
m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off.png");
|
||||
m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png");
|
||||
|
||||
for (const std::string axis : { "x", "y", "z" }) {
|
||||
const std::string label = boost::algorithm::to_upper_copy(axis);
|
||||
def.set_default_value(new ConfigOptionString{ " " + label });
|
||||
Option option = Option(def, axis + "_axis_legend");
|
||||
|
||||
unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
|
||||
|
||||
// We will add a button to toggle mirroring to each axis:
|
||||
auto mirror_button = [=](wxWindow* parent) {
|
||||
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
|
||||
btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label));
|
||||
|
||||
m_mirror_buttons[axis_idx].first = btn;
|
||||
m_mirror_buttons[axis_idx].second = mbShown;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn);
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
Axis axis = (Axis)(axis_idx + X);
|
||||
if (m_mirror_buttons[axis_idx].second == mbHidden)
|
||||
return;
|
||||
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
|
||||
volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis));
|
||||
}
|
||||
else if (selection.is_single_full_instance()) {
|
||||
for (unsigned int idx : selection.get_volume_idxs()){
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
|
||||
volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis));
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Update mirroring at the GLVolumes.
|
||||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_mirror();
|
||||
canvas->set_as_dirty();
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
|
||||
option.side_widget = mirror_button;
|
||||
line.append_option(option);
|
||||
}
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
@ -190,8 +246,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
def.set_default_value(new ConfigOptionFloat(0.0));
|
||||
def.width = field_width/*50*/;
|
||||
|
||||
// Add "uniform scaling" button in front of "Scale" option
|
||||
if (option_name == "Scale") {
|
||||
// Add "uniform scaling" button in front of "Scale" option
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
auto btn = new LockButton(parent, wxID_ANY);
|
||||
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){
|
||||
@ -201,8 +257,59 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
m_lock_bnt = btn;
|
||||
return btn;
|
||||
};
|
||||
// Add reset scale button
|
||||
auto reset_scale_button = [=](wxWindow* parent) {
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
btn->SetToolTip(_(L("Reset scale")));
|
||||
m_reset_scale_button = btn;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn, wxBU_EXACTFIT);
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
change_scale_value(0, 100.);
|
||||
change_scale_value(1, 100.);
|
||||
change_scale_value(2, 100.);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
line.append_widget(reset_scale_button);
|
||||
}
|
||||
else if (option_name == "Rotation") {
|
||||
// Add reset rotation button
|
||||
auto reset_rotation_button = [=](wxWindow* parent) {
|
||||
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
|
||||
btn->SetToolTip(_(L("Reset rotation")));
|
||||
m_reset_rotation_button = btn;
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(btn, wxBU_EXACTFIT);
|
||||
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
|
||||
volume->set_volume_rotation(Vec3d::Zero());
|
||||
}
|
||||
else if (selection.is_single_full_instance()) {
|
||||
for (unsigned int idx : selection.get_volume_idxs()){
|
||||
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
|
||||
volume->set_instance_rotation(Vec3d::Zero());
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Update rotation at the GLVolumes.
|
||||
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
|
||||
selection.synchronize_unselected_volumes();
|
||||
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
|
||||
canvas->do_rotate();
|
||||
|
||||
UpdateAndShow(true);
|
||||
});
|
||||
return sizer;
|
||||
};
|
||||
line.append_widget(reset_rotation_button);
|
||||
}
|
||||
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
|
||||
else if (option_name == "Size") {
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
@ -224,8 +331,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
return line;
|
||||
};
|
||||
|
||||
|
||||
// Settings table
|
||||
m_og->sidetext_width = 3;
|
||||
m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label);
|
||||
m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label);
|
||||
@ -239,6 +346,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
||||
ctrl->msw_rescale();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::Show(const bool show)
|
||||
{
|
||||
@ -408,9 +517,95 @@ void ObjectManipulation::update_if_dirty()
|
||||
else
|
||||
m_og->disable();
|
||||
|
||||
update_reset_buttons_visibility();
|
||||
update_mirror_buttons_visibility();
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::update_reset_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
if (!canvas)
|
||||
return;
|
||||
const Selection& selection = canvas->get_selection();
|
||||
|
||||
bool show_rotation = false;
|
||||
bool show_scale = false;
|
||||
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Vec3d rotation;
|
||||
Vec3d scale;
|
||||
|
||||
if (selection.is_single_full_instance()) {
|
||||
rotation = volume->get_instance_rotation();
|
||||
scale = volume->get_instance_scaling_factor();
|
||||
}
|
||||
else {
|
||||
rotation = volume->get_volume_rotation();
|
||||
scale = volume->get_volume_scaling_factor();
|
||||
}
|
||||
show_rotation = !rotation.isApprox(Vec3d::Zero());
|
||||
show_scale = !scale.isApprox(Vec3d::Ones());
|
||||
}
|
||||
|
||||
wxGetApp().CallAfter([this, show_rotation, show_scale]{
|
||||
m_reset_rotation_button->Show(show_rotation);
|
||||
m_reset_scale_button->Show(show_scale);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ObjectManipulation::update_mirror_buttons_visibility()
|
||||
{
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
Selection& selection = canvas->get_selection();
|
||||
std::array<MirrorButtonState, 3> new_states = {mbHidden, mbHidden, mbHidden};
|
||||
|
||||
if (!m_world_coordinates) {
|
||||
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
Vec3d mirror;
|
||||
|
||||
if (selection.is_single_full_instance())
|
||||
mirror = volume->get_instance_mirror();
|
||||
else
|
||||
mirror = volume->get_volume_mirror();
|
||||
|
||||
for (unsigned char i=0; i<3; ++i)
|
||||
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the mirroring buttons should be hidden in world coordinates,
|
||||
// unless we make it actually mirror in world coords.
|
||||
}
|
||||
|
||||
// Hiding the buttons through Hide() always messed up the sizers. As a workaround, the button
|
||||
// is assigned a transparent bitmap. We must of course remember the actual state.
|
||||
wxGetApp().CallAfter([this, new_states]{
|
||||
for (int i=0; i<3; ++i) {
|
||||
if (new_states[i] != m_mirror_buttons[i].second) {
|
||||
const wxBitmap* bmp;
|
||||
switch (new_states[i]) {
|
||||
case mbHidden : bmp = &m_mirror_bitmap_hidden.bmp(); m_mirror_buttons[i].first->Enable(false); break;
|
||||
case mbShown : bmp = &m_mirror_bitmap_off.bmp(); m_mirror_buttons[i].first->Enable(true); break;
|
||||
case mbActive : bmp = &m_mirror_bitmap_on.bmp(); m_mirror_buttons[i].first->Enable(true); break;
|
||||
}
|
||||
m_mirror_buttons[i].first->SetBitmap(*bmp);
|
||||
m_mirror_buttons[i].second = new_states[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __APPLE__
|
||||
void ObjectManipulation::emulate_kill_focus()
|
||||
{
|
||||
@ -493,7 +688,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
|
||||
|
||||
m_cache.rotation = rotation;
|
||||
m_cache.rotation_rounded(axis) = DBL_MAX;
|
||||
this->UpdateAndShow(true);
|
||||
this->UpdateAndShow(true);
|
||||
}
|
||||
|
||||
void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
@ -511,6 +706,7 @@ void ObjectManipulation::change_scale_value(int axis, double value)
|
||||
this->UpdateAndShow(true);
|
||||
}
|
||||
|
||||
|
||||
void ObjectManipulation::change_size_value(int axis, double value)
|
||||
{
|
||||
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
|
||||
@ -666,6 +862,12 @@ void ObjectManipulation::msw_rescale()
|
||||
m_manifold_warning_bmp.msw_rescale();
|
||||
m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
|
||||
|
||||
m_mirror_bitmap_on.msw_rescale();
|
||||
m_mirror_bitmap_off.msw_rescale();
|
||||
m_mirror_bitmap_hidden.msw_rescale();
|
||||
m_reset_scale_button->msw_rescale();
|
||||
m_reset_rotation_button->msw_rescale();
|
||||
|
||||
get_og()->msw_rescale();
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,23 @@ class ObjectManipulation : public OG_Settings
|
||||
wxStaticText* m_scale_Label = nullptr;
|
||||
wxStaticText* m_rotate_Label = nullptr;
|
||||
|
||||
// Non-owning pointers to the reset buttons, so we can hide and show them.
|
||||
ScalableButton* m_reset_scale_button = nullptr;
|
||||
ScalableButton* m_reset_rotation_button = nullptr;
|
||||
|
||||
// Mirroring buttons and their current state
|
||||
enum MirrorButtonState {
|
||||
mbHidden,
|
||||
mbShown,
|
||||
mbActive
|
||||
};
|
||||
std::array<std::pair<ScalableButton*, MirrorButtonState>, 3> m_mirror_buttons;
|
||||
|
||||
// Bitmaps for the mirroring buttons.
|
||||
ScalableBitmap m_mirror_bitmap_on;
|
||||
ScalableBitmap m_mirror_bitmap_off;
|
||||
ScalableBitmap m_mirror_bitmap_hidden;
|
||||
|
||||
// Needs to be updated from OnIdle?
|
||||
bool m_dirty = false;
|
||||
// Cached labels for the delayed update, not localized!
|
||||
@ -111,10 +128,10 @@ private:
|
||||
void reset_settings_value();
|
||||
void update_settings_value(const Selection& selection);
|
||||
|
||||
// update size values after scale unit changing or "gizmos"
|
||||
void update_size_value(const Vec3d& size);
|
||||
// update rotation value after "gizmos"
|
||||
void update_rotation_value(const Vec3d& rotation);
|
||||
// Show or hide scale/rotation reset buttons if needed
|
||||
void update_reset_buttons_visibility();
|
||||
//Show or hide mirror buttons
|
||||
void update_mirror_buttons_visibility();
|
||||
|
||||
// change values
|
||||
void change_position_value(int axis, double value);
|
||||
|
@ -414,6 +414,12 @@ void Preview::msw_rescale()
|
||||
refresh_print();
|
||||
}
|
||||
|
||||
void Preview::move_double_slider(wxKeyEvent& evt)
|
||||
{
|
||||
if (m_slider)
|
||||
m_slider->OnKeyDown(evt);
|
||||
}
|
||||
|
||||
void Preview::bind_event_handlers()
|
||||
{
|
||||
this->Bind(wxEVT_SIZE, &Preview::on_size, this);
|
||||
|
@ -122,6 +122,7 @@ public:
|
||||
void refresh_print();
|
||||
|
||||
void msw_rescale();
|
||||
void move_double_slider(wxKeyEvent& evt);
|
||||
|
||||
private:
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
|
||||
|
@ -27,6 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_i
|
||||
: GLGizmoBase(parent, sprite_id)
|
||||
#endif // ENABLE_SVG_ICONS
|
||||
, m_quadric(nullptr)
|
||||
, m_its(nullptr)
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
@ -379,36 +380,23 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0);
|
||||
&& ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::update_mesh()
|
||||
{
|
||||
wxBusyCursor wait;
|
||||
Eigen::MatrixXf& V = m_V;
|
||||
Eigen::MatrixXi& F = m_F;
|
||||
// We rely on SLA model object having a single volume,
|
||||
// this way we can use that mesh directly.
|
||||
// This mesh does not account for the possible Z up SLA offset.
|
||||
m_mesh = &m_model_object->volumes.front()->mesh;
|
||||
const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
const stl_file& stl = m_mesh->stl;
|
||||
V.resize(3 * stl.stats.number_of_facets, 3);
|
||||
F.resize(stl.stats.number_of_facets, 3);
|
||||
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
|
||||
const stl_facet* facet = stl.facet_start+i;
|
||||
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
|
||||
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
|
||||
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
|
||||
F(i, 0) = 3*i+0;
|
||||
F(i, 1) = 3*i+1;
|
||||
F(i, 2) = 3*i+2;
|
||||
}
|
||||
m_mesh = &m_model_object->volumes.front()->mesh();
|
||||
m_its = &m_mesh->its;
|
||||
m_current_mesh_model_id = m_model_object->id();
|
||||
m_editing_mode = false;
|
||||
|
||||
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
|
||||
m_AABB.init(m_V, m_F);
|
||||
m_AABB.deinit();
|
||||
m_AABB.init(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
|
||||
}
|
||||
|
||||
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
|
||||
@ -416,7 +404,7 @@ void GLGizmoSlaSupports::update_mesh()
|
||||
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
if (m_V.size() == 0)
|
||||
if (m_its == nullptr)
|
||||
update_mesh();
|
||||
|
||||
const Camera& camera = m_parent.get_camera();
|
||||
@ -442,7 +430,10 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
|
||||
point1 = inv * point1;
|
||||
point2 = inv * point2;
|
||||
|
||||
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hits))
|
||||
if (!m_AABB.intersect_ray(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
point1.cast<float>(), (point2-point1).cast<float>(), hits))
|
||||
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
|
||||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
@ -457,9 +448,9 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
|
||||
igl::Hit& hit = hits[i];
|
||||
int fid = hit.id; // facet id
|
||||
bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
|
||||
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>()))
|
||||
break;
|
||||
}
|
||||
@ -550,7 +541,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
|
||||
Vec3f direction_to_camera = camera.get_dir_forward().cast<float>();
|
||||
Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
|
||||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = volume->get_instance_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
@ -564,15 +555,18 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
|
||||
if (m_AABB.intersect_ray(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
int fid = hits.front().id; // facet id
|
||||
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
|
||||
if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
|
||||
is_obscured = true;
|
||||
|
||||
@ -582,7 +576,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
||||
int fid = hit.id; // facet id
|
||||
|
||||
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
|
||||
if (is_point_clipped(hit_pos.cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
@ -759,9 +753,12 @@ void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
|
||||
int idx = 0;
|
||||
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
|
||||
Eigen::Matrix<float, 1, 3> cc;
|
||||
m_AABB.squared_distance(m_V, m_F, pp, idx, cc);
|
||||
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0)));
|
||||
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0)));
|
||||
m_AABB.squared_distance(
|
||||
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
|
||||
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
|
||||
pp, idx, cc);
|
||||
Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]);
|
||||
Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]);
|
||||
m_editing_mode_cache[i].normal = a.cross(b);
|
||||
}
|
||||
|
||||
@ -1067,8 +1064,7 @@ void GLGizmoSlaSupports::on_set_state()
|
||||
m_clipping_plane_distance = 0.f;
|
||||
// Release triangle mesh slicer and the AABB spatial search structure.
|
||||
m_AABB.deinit();
|
||||
m_V = Eigen::MatrixXf();
|
||||
m_F = Eigen::MatrixXi();
|
||||
m_its = nullptr;
|
||||
m_tms.reset();
|
||||
m_supports_tms.reset();
|
||||
});
|
||||
|
@ -35,10 +35,11 @@ private:
|
||||
const float RenderPointScale = 1.f;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
Eigen::MatrixXf m_V; // vertices
|
||||
Eigen::MatrixXi m_F; // facets indices
|
||||
igl::AABB<Eigen::MatrixXf,3> m_AABB;
|
||||
typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
|
||||
typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
|
||||
igl::AABB<MapMatrixXfUnaligned, 3> m_AABB;
|
||||
const TriangleMesh* m_mesh;
|
||||
const indexed_triangle_set* m_its;
|
||||
mutable const TriangleMesh* m_supports_mesh;
|
||||
mutable std::vector<Vec2f> m_triangles;
|
||||
mutable std::vector<Vec2f> m_supports_triangles;
|
||||
@ -131,6 +132,11 @@ private:
|
||||
|
||||
protected:
|
||||
void on_set_state() override;
|
||||
virtual void on_set_hover_id()
|
||||
{
|
||||
if ((int)m_editing_mode_cache.size() <= m_hover_id)
|
||||
m_hover_id = -1;
|
||||
}
|
||||
void on_start_dragging(const Selection& selection) override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override;
|
||||
|
||||
|
@ -54,7 +54,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
#endif // _WIN32
|
||||
|
||||
// initialize status bar
|
||||
m_statusbar = new ProgressStatusBar(this);
|
||||
m_statusbar.reset(new ProgressStatusBar(this));
|
||||
m_statusbar->embed(this);
|
||||
m_statusbar->set_status_text(_(L("Version")) + " " +
|
||||
SLIC3R_VERSION +
|
||||
@ -103,6 +103,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
event.Veto();
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_plater) m_plater->stop_jobs();
|
||||
|
||||
// Weird things happen as the Paint messages are floating around the windows being destructed.
|
||||
// Avoid the Paint messages by hiding the main window.
|
||||
@ -138,6 +140,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
||||
update_ui_from_settings(); // FIXME (?)
|
||||
}
|
||||
|
||||
MainFrame::~MainFrame() = default;
|
||||
|
||||
void MainFrame::update_title()
|
||||
{
|
||||
wxString title = wxEmptyString;
|
||||
@ -976,7 +980,8 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
|
||||
if (! boost::algorithm::ends_with(opt_key, "_settings_id"))
|
||||
tab->get_config()->option(opt_key)->set(config.option(opt_key));
|
||||
}
|
||||
wxGetApp().load_current_presets();
|
||||
|
||||
wxGetApp().load_current_presets();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ protected:
|
||||
|
||||
public:
|
||||
MainFrame();
|
||||
~MainFrame() {}
|
||||
~MainFrame();
|
||||
|
||||
Plater* plater() { return m_plater; }
|
||||
|
||||
@ -126,7 +126,7 @@ public:
|
||||
Plater* m_plater { nullptr };
|
||||
wxNotebook* m_tabpanel { nullptr };
|
||||
wxProgressDialog* m_progress_dialog { nullptr };
|
||||
ProgressStatusBar* m_statusbar { nullptr };
|
||||
std::unique_ptr<ProgressStatusBar> m_statusbar;
|
||||
};
|
||||
|
||||
} // GUI
|
||||
|
@ -276,7 +276,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
||||
// add sidetext if any
|
||||
if (option.sidetext != "") {
|
||||
auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
|
||||
/*wxSize(sidetext_width*wxGetApp().em_unit(), -1)*/wxDefaultSize, wxALIGN_LEFT);
|
||||
wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1) /*wxDefaultSize*/, wxALIGN_LEFT);
|
||||
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||
sidetext->SetFont(wxGetApp().normal_font());
|
||||
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
||||
|
@ -5,9 +5,11 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <future>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
@ -1253,8 +1255,220 @@ struct Plater::priv
|
||||
Preview *preview;
|
||||
|
||||
BackgroundSlicingProcess background_process;
|
||||
bool arranging;
|
||||
bool rotoptimizing;
|
||||
|
||||
// A class to handle UI jobs like arranging and optimizing rotation.
|
||||
// These are not instant jobs, the user has to be informed about their
|
||||
// state in the status progress indicator. On the other hand they are
|
||||
// separated from the background slicing process. Ideally, these jobs should
|
||||
// run when the background process is not running.
|
||||
//
|
||||
// TODO: A mechanism would be useful for blocking the plater interactions:
|
||||
// objects would be frozen for the user. In case of arrange, an animation
|
||||
// could be shown, or with the optimize orientations, partial results
|
||||
// could be displayed.
|
||||
class Job: public wxEvtHandler {
|
||||
int m_range = 100;
|
||||
std::future<void> m_ftr;
|
||||
priv *m_plater = nullptr;
|
||||
std::atomic<bool> m_running {false}, m_canceled {false};
|
||||
bool m_finalized = false;
|
||||
|
||||
void run() {
|
||||
m_running.store(true); process(); m_running.store(false);
|
||||
|
||||
// ensure to call the last status to finalize the job
|
||||
update_status(status_range(), "");
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// status range for a particular job
|
||||
virtual int status_range() const { return 100; }
|
||||
|
||||
// status update, to be used from the work thread (process() method)
|
||||
void update_status(int st, const wxString& msg = "") {
|
||||
auto evt = new wxThreadEvent(); evt->SetInt(st); evt->SetString(msg);
|
||||
wxQueueEvent(this, evt);
|
||||
}
|
||||
|
||||
priv& plater() { return *m_plater; }
|
||||
bool was_canceled() const { return m_canceled.load(); }
|
||||
|
||||
// Launched just before start(), a job can use it to prepare internals
|
||||
virtual void prepare() {}
|
||||
|
||||
// Launched when the job is finished. It refreshes the 3dscene by def.
|
||||
virtual void finalize() {
|
||||
// Do a full refresh of scene tree, including regenerating
|
||||
// all the GLVolumes. FIXME The update function shall just
|
||||
// reload the modified matrices.
|
||||
if(! was_canceled())
|
||||
plater().update(true);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Job(priv *_plater): m_plater(_plater)
|
||||
{
|
||||
Bind(wxEVT_THREAD, [this](const wxThreadEvent& evt){
|
||||
auto msg = evt.GetString();
|
||||
if(! msg.empty()) plater().statusbar()->set_status_text(msg);
|
||||
|
||||
if(m_finalized) return;
|
||||
|
||||
plater().statusbar()->set_progress(evt.GetInt());
|
||||
if(evt.GetInt() == status_range()) {
|
||||
|
||||
// set back the original range and cancel callback
|
||||
plater().statusbar()->set_range(m_range);
|
||||
plater().statusbar()->set_cancel_callback();
|
||||
wxEndBusyCursor();
|
||||
|
||||
finalize();
|
||||
|
||||
// dont do finalization again for the same process
|
||||
m_finalized = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Job(const Job&) = delete;
|
||||
Job(Job&&) = default;
|
||||
Job& operator=(const Job&) = delete;
|
||||
Job& operator=(Job&&) = default;
|
||||
|
||||
virtual void process() = 0;
|
||||
|
||||
void start() { // Start the job. No effect if the job is already running
|
||||
if(! m_running.load()) {
|
||||
|
||||
prepare();
|
||||
|
||||
// Save the current status indicatior range and push the new one
|
||||
m_range = plater().statusbar()->get_range();
|
||||
plater().statusbar()->set_range(status_range());
|
||||
|
||||
// init cancellation flag and set the cancel callback
|
||||
m_canceled.store(false);
|
||||
plater().statusbar()->set_cancel_callback( [this](){
|
||||
m_canceled.store(true);
|
||||
});
|
||||
|
||||
m_finalized = false;
|
||||
|
||||
// Changing cursor to busy
|
||||
wxBeginBusyCursor();
|
||||
|
||||
try { // Execute the job
|
||||
m_ftr = std::async(std::launch::async, &Job::run, this);
|
||||
} catch(std::exception& ) {
|
||||
update_status(status_range(),
|
||||
_(L("ERROR: not enough resources to execute a new job.")));
|
||||
}
|
||||
|
||||
// The state changes will be undone when the process hits the
|
||||
// last status value, in the status update handler (see ctor)
|
||||
}
|
||||
}
|
||||
|
||||
// To wait for the running job and join the threads. False is returned
|
||||
// if the timeout has been reached and the job is still running. Call
|
||||
// cancel() before this fn if you want to explicitly end the job.
|
||||
bool join(int timeout_ms = 0) const {
|
||||
if(!m_ftr.valid()) return true;
|
||||
|
||||
if(timeout_ms <= 0)
|
||||
m_ftr.wait();
|
||||
else if(m_ftr.wait_for(std::chrono::milliseconds(timeout_ms)) ==
|
||||
std::future_status::timeout)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_running() const { return m_running.load(); }
|
||||
void cancel() { m_canceled.store(true); }
|
||||
};
|
||||
|
||||
enum class Jobs : size_t {
|
||||
Arrange,
|
||||
Rotoptimize
|
||||
};
|
||||
|
||||
// Jobs defined inside the group class will be managed so that only one can
|
||||
// run at a time. Also, the background process will be stopped if a job is
|
||||
// started.
|
||||
class ExclusiveJobGroup {
|
||||
|
||||
static const int ABORT_WAIT_MAX_MS = 10000;
|
||||
|
||||
priv * m_plater;
|
||||
|
||||
class ArrangeJob : public Job
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
protected:
|
||||
void prepare() override
|
||||
{
|
||||
count = 0;
|
||||
for (auto obj : plater().model.objects)
|
||||
count += int(obj->instances.size());
|
||||
}
|
||||
|
||||
public:
|
||||
using Job::Job;
|
||||
int status_range() const override { return count; }
|
||||
void set_count(int c) { count = c; }
|
||||
void process() override;
|
||||
} arrange_job{m_plater};
|
||||
|
||||
class RotoptimizeJob : public Job
|
||||
{
|
||||
public:
|
||||
using Job::Job;
|
||||
void process() override;
|
||||
} rotoptimize_job{m_plater};
|
||||
|
||||
std::vector<std::reference_wrapper<Job>> m_jobs{arrange_job,
|
||||
rotoptimize_job};
|
||||
|
||||
public:
|
||||
|
||||
ExclusiveJobGroup(priv *_plater): m_plater(_plater) {}
|
||||
|
||||
void start(Jobs jid) {
|
||||
m_plater->background_process.stop();
|
||||
stop_all();
|
||||
m_jobs[size_t(jid)].get().start();
|
||||
}
|
||||
|
||||
void cancel_all() { for (Job& j : m_jobs) j.cancel(); }
|
||||
|
||||
void join_all(int wait_ms = 0)
|
||||
{
|
||||
std::vector<bool> aborted(m_jobs.size(), false);
|
||||
|
||||
for (size_t jid = 0; jid < m_jobs.size(); ++jid)
|
||||
aborted[jid] = m_jobs[jid].get().join(wait_ms);
|
||||
|
||||
if (!all_of(aborted))
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not abort a job!";
|
||||
}
|
||||
|
||||
void stop_all() { cancel_all(); join_all(ABORT_WAIT_MAX_MS); }
|
||||
|
||||
const Job& get(Jobs jobid) const { return m_jobs[size_t(jobid)]; }
|
||||
|
||||
bool is_any_running() const
|
||||
{
|
||||
return std::any_of(m_jobs.begin(),
|
||||
m_jobs.end(),
|
||||
[](const Job &j) { return j.is_running(); });
|
||||
}
|
||||
|
||||
} m_ui_jobs{this};
|
||||
|
||||
bool delayed_scene_refresh;
|
||||
std::string delayed_error_message;
|
||||
|
||||
@ -1429,8 +1643,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
{
|
||||
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
|
||||
|
||||
arranging = false;
|
||||
rotoptimizing = false;
|
||||
background_process.set_fff_print(&fff_print);
|
||||
background_process.set_sla_print(&sla_print);
|
||||
background_process.set_gcode_preview_data(&gcode_preview_data);
|
||||
@ -1517,6 +1729,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, [this](wxKeyEvent& evt) { preview->move_double_slider(evt); });
|
||||
|
||||
q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this);
|
||||
q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this);
|
||||
@ -1604,7 +1817,7 @@ void Plater::priv::update_ui_from_settings()
|
||||
|
||||
ProgressStatusBar* Plater::priv::statusbar()
|
||||
{
|
||||
return main_frame->m_statusbar;
|
||||
return main_frame->m_statusbar.get();
|
||||
}
|
||||
|
||||
std::string Plater::priv::get_config(const std::string &key) const
|
||||
@ -1670,6 +1883,22 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
if (load_config && !config_loaded.empty()) {
|
||||
// Based on the printer technology field found in the loaded config, select the base for the config,
|
||||
PrinterTechnology printer_technology = Preset::printer_technology(config_loaded);
|
||||
|
||||
// We can't to load SLA project if there is at least one multi-part object on the bed
|
||||
if (printer_technology == ptSLA)
|
||||
{
|
||||
const ModelObjectPtrs& objects = q->model().objects;
|
||||
for (auto object : objects)
|
||||
if (object->volumes.size() > 1)
|
||||
{
|
||||
Slic3r::GUI::show_info(nullptr,
|
||||
_(L("You can't to load SLA project if there is at least one multi-part object on the bed")) + "\n\n" +
|
||||
_(L("Please check your object list before preset changing.")),
|
||||
_(L("Attention!")));
|
||||
return obj_idxs;
|
||||
}
|
||||
}
|
||||
|
||||
config.apply(printer_technology == ptFFF ?
|
||||
static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :
|
||||
static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults()));
|
||||
@ -2125,59 +2354,45 @@ void Plater::priv::mirror(Axis axis)
|
||||
|
||||
void Plater::priv::arrange()
|
||||
{
|
||||
if (arranging) { return; }
|
||||
arranging = true;
|
||||
Slic3r::ScopeGuard arranging_guard([this]() { arranging = false; });
|
||||
m_ui_jobs.start(Jobs::Arrange);
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
// This method will find an optimal orientation for the currently selected item
|
||||
// Very similar in nature to the arrange method above...
|
||||
void Plater::priv::sla_optimize_rotation() {
|
||||
m_ui_jobs.start(Jobs::Rotoptimize);
|
||||
}
|
||||
|
||||
this->background_process.stop();
|
||||
void Plater::priv::ExclusiveJobGroup::ArrangeJob::process() {
|
||||
// TODO: we should decide whether to allow arrange when the search is
|
||||
// running we should probably disable explicit slicing and background
|
||||
// processing
|
||||
|
||||
unsigned count = 0;
|
||||
for(auto obj : model.objects) count += obj->instances.size();
|
||||
static const auto arrangestr = _(L("Arranging"));
|
||||
|
||||
auto prev_range = statusbar()->get_range();
|
||||
statusbar()->set_range(count);
|
||||
|
||||
auto statusfn = [this, count] (unsigned st, const std::string& msg) {
|
||||
/* // In case we would run the arrange asynchronously
|
||||
wxCommandEvent event(EVT_PROGRESS_BAR);
|
||||
event.SetInt(st);
|
||||
event.SetString(msg);
|
||||
wxQueueEvent(this->q, event.Clone()); */
|
||||
statusbar()->set_progress(count - st);
|
||||
statusbar()->set_status_text(_(msg));
|
||||
|
||||
// ok, this is dangerous, but we are protected by the flag
|
||||
// 'arranging' and the arrange button is also disabled.
|
||||
// This call is needed for the cancel button to work.
|
||||
wxYieldIfNeeded();
|
||||
};
|
||||
|
||||
statusbar()->set_cancel_callback([this, statusfn](){
|
||||
arranging = false;
|
||||
statusfn(0, L("Arranging canceled"));
|
||||
});
|
||||
|
||||
static const std::string arrangestr = L("Arranging");
|
||||
auto &config = plater().config;
|
||||
auto &view3D = plater().view3D;
|
||||
auto &model = plater().model;
|
||||
|
||||
// FIXME: I don't know how to obtain the minimum distance, it depends
|
||||
// on printer technology. I guess the following should work but it crashes.
|
||||
double dist = 6; //PrintConfig::min_object_distance(config);
|
||||
if(printer_technology == ptFFF) {
|
||||
double dist = 6; // PrintConfig::min_object_distance(config);
|
||||
if (plater().printer_technology == ptFFF) {
|
||||
dist = PrintConfig::min_object_distance(config);
|
||||
}
|
||||
|
||||
auto min_obj_distance = coord_t(dist/SCALING_FACTOR);
|
||||
auto min_obj_distance = coord_t(dist / SCALING_FACTOR);
|
||||
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>(
|
||||
"bed_shape");
|
||||
|
||||
assert(bed_shape_opt);
|
||||
auto& bedpoints = bed_shape_opt->values;
|
||||
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
auto & bedpoints = bed_shape_opt->values;
|
||||
Polyline bed;
|
||||
bed.points.reserve(bedpoints.size());
|
||||
for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
|
||||
statusfn(0, arrangestr);
|
||||
update_status(0, arrangestr);
|
||||
|
||||
arr::WipeTowerInfo wti = view3D->get_canvas3d()->get_wipe_tower_info();
|
||||
|
||||
@ -2193,129 +2408,116 @@ void Plater::priv::arrange()
|
||||
bed,
|
||||
hint,
|
||||
false, // create many piles not just one pile
|
||||
[statusfn](unsigned st) { statusfn(st, arrangestr); },
|
||||
[this] () { return !arranging; });
|
||||
} catch(std::exception& /*e*/) {
|
||||
GUI::show_error(this->q, L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid."));
|
||||
[this](unsigned st) {
|
||||
if (st > 0)
|
||||
update_status(count - int(st), arrangestr);
|
||||
},
|
||||
[this]() { return was_canceled(); });
|
||||
} catch (std::exception & /*e*/) {
|
||||
GUI::show_error(plater().q,
|
||||
L("Could not arrange model objects! "
|
||||
"Some geometries may be invalid."));
|
||||
}
|
||||
|
||||
update_status(count,
|
||||
was_canceled() ? _(L("Arranging canceled."))
|
||||
: _(L("Arranging done.")));
|
||||
|
||||
// it remains to move the wipe tower:
|
||||
view3D->get_canvas3d()->arrange_wipe_tower(wti);
|
||||
|
||||
statusfn(0, L("Arranging done."));
|
||||
statusbar()->set_range(prev_range);
|
||||
statusbar()->set_cancel_callback(); // remove cancel button
|
||||
|
||||
// Do a full refresh of scene tree, including regenerating all the GLVolumes.
|
||||
//FIXME The update function shall just reload the modified matrices.
|
||||
update(true);
|
||||
}
|
||||
|
||||
// This method will find an optimal orientation for the currently selected item
|
||||
// Very similar in nature to the arrange method above...
|
||||
void Plater::priv::sla_optimize_rotation() {
|
||||
|
||||
// TODO: we should decide whether to allow arrange when the search is
|
||||
// running we should probably disable explicit slicing and background
|
||||
// processing
|
||||
|
||||
if (rotoptimizing) { return; }
|
||||
rotoptimizing = true;
|
||||
Slic3r::ScopeGuard rotoptimizing_guard([this]() { rotoptimizing = false; });
|
||||
|
||||
int obj_idx = get_selected_object_idx();
|
||||
void Plater::priv::ExclusiveJobGroup::RotoptimizeJob::process()
|
||||
{
|
||||
int obj_idx = plater().get_selected_object_idx();
|
||||
if (obj_idx < 0) { return; }
|
||||
|
||||
ModelObject * o = model.objects[size_t(obj_idx)];
|
||||
|
||||
background_process.stop();
|
||||
|
||||
auto prev_range = statusbar()->get_range();
|
||||
statusbar()->set_range(100);
|
||||
|
||||
auto stfn = [this] (unsigned st, const std::string& msg) {
|
||||
statusbar()->set_progress(int(st));
|
||||
statusbar()->set_status_text(msg);
|
||||
|
||||
// could be problematic, but we need the cancel button.
|
||||
wxYieldIfNeeded();
|
||||
};
|
||||
|
||||
statusbar()->set_cancel_callback([this, stfn](){
|
||||
rotoptimizing = false;
|
||||
stfn(0, L("Orientation search canceled"));
|
||||
});
|
||||
ModelObject *o = plater().model.objects[size_t(obj_idx)];
|
||||
|
||||
auto r = sla::find_best_rotation(
|
||||
*o, .005f,
|
||||
[stfn](unsigned s) { stfn(s, L("Searching for optimal orientation")); },
|
||||
[this](){ return !rotoptimizing; }
|
||||
);
|
||||
*o,
|
||||
.005f,
|
||||
[this](unsigned s) {
|
||||
if (s < 100)
|
||||
update_status(int(s),
|
||||
_(L("Searching for optimal orientation")));
|
||||
},
|
||||
[this]() { return was_canceled(); });
|
||||
|
||||
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
const auto *bed_shape_opt = plater().config->opt<ConfigOptionPoints>(
|
||||
"bed_shape");
|
||||
assert(bed_shape_opt);
|
||||
|
||||
auto& bedpoints = bed_shape_opt->values;
|
||||
Polyline bed; bed.points.reserve(bedpoints.size());
|
||||
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
auto & bedpoints = bed_shape_opt->values;
|
||||
Polyline bed;
|
||||
bed.points.reserve(bedpoints.size());
|
||||
for (auto &v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
|
||||
|
||||
double mindist = 6.0; // FIXME
|
||||
double offs = mindist / 2.0 - EPSILON;
|
||||
double offs = mindist / 2.0 - EPSILON;
|
||||
|
||||
if(rotoptimizing) // wasn't canceled
|
||||
for(ModelInstance * oi : o->instances) {
|
||||
oi->set_rotation({r[X], r[Y], r[Z]});
|
||||
if (!was_canceled()) // wasn't canceled
|
||||
for (ModelInstance *oi : o->instances) {
|
||||
oi->set_rotation({r[X], r[Y], r[Z]});
|
||||
|
||||
auto trchull = o->convex_hull_2d(oi->get_transformation().get_matrix());
|
||||
auto trchull = o->convex_hull_2d(
|
||||
oi->get_transformation().get_matrix());
|
||||
|
||||
namespace opt = libnest2d::opt;
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.relative_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
stopcr.stop_score = 0.0;
|
||||
opt::GeneticOptimizer solver(stopcr);
|
||||
Polygon pbed(bed);
|
||||
namespace opt = libnest2d::opt;
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.relative_score_difference = 0.01;
|
||||
stopcr.max_iterations = 10000;
|
||||
stopcr.stop_score = 0.0;
|
||||
opt::GeneticOptimizer solver(stopcr);
|
||||
Polygon pbed(bed);
|
||||
|
||||
auto bin = pbed.bounding_box();
|
||||
double binw = bin.size()(X) * SCALING_FACTOR - offs;
|
||||
double binh = bin.size()(Y) * SCALING_FACTOR - offs;
|
||||
auto bin = pbed.bounding_box();
|
||||
double binw = bin.size()(X) * SCALING_FACTOR - offs;
|
||||
double binh = bin.size()(Y) * SCALING_FACTOR - offs;
|
||||
|
||||
auto result = solver.optimize_min([&trchull, binw, binh](double rot){
|
||||
auto chull = trchull;
|
||||
chull.rotate(rot);
|
||||
auto result = solver.optimize_min(
|
||||
[&trchull, binw, binh](double rot) {
|
||||
auto chull = trchull;
|
||||
chull.rotate(rot);
|
||||
|
||||
auto bb = chull.bounding_box();
|
||||
double bbw = bb.size()(X) * SCALING_FACTOR;
|
||||
double bbh = bb.size()(Y) * SCALING_FACTOR;
|
||||
auto bb = chull.bounding_box();
|
||||
double bbw = bb.size()(X) * SCALING_FACTOR;
|
||||
double bbh = bb.size()(Y) * SCALING_FACTOR;
|
||||
|
||||
auto wdiff = bbw - binw;
|
||||
auto hdiff = bbh - binh;
|
||||
double diff = 0;
|
||||
if(wdiff < 0 && hdiff < 0) diff = wdiff + hdiff;
|
||||
if(wdiff > 0) diff += wdiff;
|
||||
if(hdiff > 0) diff += hdiff;
|
||||
auto wdiff = bbw - binw;
|
||||
auto hdiff = bbh - binh;
|
||||
double diff = 0;
|
||||
if (wdiff < 0 && hdiff < 0) diff = wdiff + hdiff;
|
||||
if (wdiff > 0) diff += wdiff;
|
||||
if (hdiff > 0) diff += hdiff;
|
||||
|
||||
return diff;
|
||||
}, opt::initvals(0.0), opt::bound(-PI/2, PI/2));
|
||||
return diff;
|
||||
},
|
||||
opt::initvals(0.0),
|
||||
opt::bound(-PI / 2, PI / 2));
|
||||
|
||||
double r = std::get<0>(result.optimum);
|
||||
double r = std::get<0>(result.optimum);
|
||||
|
||||
Vec3d rt = oi->get_rotation(); rt(Z) += r;
|
||||
oi->set_rotation(rt);
|
||||
Vec3d rt = oi->get_rotation();
|
||||
rt(Z) += r;
|
||||
oi->set_rotation(rt);
|
||||
|
||||
arr::WipeTowerInfo wti; // useless in SLA context
|
||||
arr::find_new_position(plater().model,
|
||||
o->instances,
|
||||
coord_t(mindist / SCALING_FACTOR),
|
||||
bed,
|
||||
wti);
|
||||
|
||||
// Correct the z offset of the object which was corrupted be
|
||||
// the rotation
|
||||
o->ensure_on_bed();
|
||||
|
||||
update_status(100, _(L("Orientation found.")));
|
||||
}
|
||||
else {
|
||||
update_status(100, _(L("Orientation search canceled.")));
|
||||
}
|
||||
|
||||
arr::WipeTowerInfo wti; // useless in SLA context
|
||||
arr::find_new_position(model, o->instances, coord_t(mindist/SCALING_FACTOR), bed, wti);
|
||||
|
||||
// Correct the z offset of the object which was corrupted be the rotation
|
||||
o->ensure_on_bed();
|
||||
|
||||
stfn(0, L("Orientation found."));
|
||||
statusbar()->set_range(prev_range);
|
||||
statusbar()->set_cancel_callback();
|
||||
|
||||
update(true);
|
||||
}
|
||||
|
||||
void Plater::priv::split_object()
|
||||
@ -2496,7 +2698,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
||||
// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState.
|
||||
bool Plater::priv::restart_background_process(unsigned int state)
|
||||
{
|
||||
if (arranging || rotoptimizing) {
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
// Avoid a race condition
|
||||
return false;
|
||||
}
|
||||
@ -2727,7 +2929,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||
{
|
||||
if (evt.status.percent >= -1) {
|
||||
if (arranging || rotoptimizing) {
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
// Avoid a race condition
|
||||
return;
|
||||
}
|
||||
@ -3208,7 +3410,7 @@ bool Plater::priv::can_fix_through_netfabb() const
|
||||
|
||||
bool Plater::priv::can_increase_instances() const
|
||||
{
|
||||
if (arranging || rotoptimizing) {
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3218,7 +3420,7 @@ bool Plater::priv::can_increase_instances() const
|
||||
|
||||
bool Plater::priv::can_decrease_instances() const
|
||||
{
|
||||
if (arranging || rotoptimizing) {
|
||||
if (m_ui_jobs.is_any_running()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3238,7 +3440,7 @@ bool Plater::priv::can_split_to_volumes() const
|
||||
|
||||
bool Plater::priv::can_arrange() const
|
||||
{
|
||||
return !model.objects.empty() && !arranging;
|
||||
return !model.objects.empty() && !m_ui_jobs.is_any_running();
|
||||
}
|
||||
|
||||
bool Plater::priv::can_layers_editing() const
|
||||
@ -3305,6 +3507,7 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
|
||||
|
||||
void Plater::new_project()
|
||||
{
|
||||
p->select_view_3D("3D");
|
||||
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
|
||||
}
|
||||
|
||||
@ -3365,6 +3568,8 @@ void Plater::load_files(const std::vector<std::string>& input_files, bool load_m
|
||||
|
||||
void Plater::update() { p->update(); }
|
||||
|
||||
void Plater::stop_jobs() { p->m_ui_jobs.stop_all(); }
|
||||
|
||||
void Plater::update_ui_from_settings() { p->update_ui_from_settings(); }
|
||||
|
||||
void Plater::select_view(const std::string& direction) { p->select_view(direction); }
|
||||
@ -3565,7 +3770,7 @@ void Plater::export_stl(bool extended, bool selection_only)
|
||||
else
|
||||
{
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh;
|
||||
mesh = model_object->volumes[volume->volume_idx()]->mesh();
|
||||
mesh.transform(volume->get_volume_transformation().get_matrix());
|
||||
mesh.translate(-model_object->origin_translation.cast<float>());
|
||||
}
|
||||
@ -3671,7 +3876,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||
if (!path.Lower().EndsWith(".3mf"))
|
||||
return;
|
||||
|
||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||
const std::string path_u8 = into_u8(path);
|
||||
wxBusyCursor wait;
|
||||
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
|
||||
@ -3687,6 +3892,9 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||
|
||||
void Plater::reslice()
|
||||
{
|
||||
// Stop arrange and (or) optimize rotation tasks.
|
||||
this->stop_jobs();
|
||||
|
||||
//FIXME Don't reslice if export of G-code or sending to OctoPrint is running.
|
||||
// bitmask of UpdateBackgroundProcessReturnState
|
||||
unsigned int state = this->p->update_background_process(true);
|
||||
@ -3722,7 +3930,7 @@ void Plater::reslice_SLA_supports(const ModelObject &object)
|
||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE)
|
||||
this->p->view3D->reload_scene(false);
|
||||
|
||||
if (this->p->background_process.empty() || (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID))
|
||||
if (this->p->background_process.empty() || (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID))
|
||||
// Nothing to do on empty input or invalid configuration.
|
||||
return;
|
||||
|
||||
|
@ -144,6 +144,7 @@ public:
|
||||
void load_files(const std::vector<std::string>& input_files, bool load_model = true, bool load_config = true);
|
||||
|
||||
void update();
|
||||
void stop_jobs();
|
||||
void select_view(const std::string& direction);
|
||||
void select_view_3D(const std::string& name);
|
||||
|
||||
|
@ -781,7 +781,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
||||
if (i == 0)
|
||||
suffix[0] = 0;
|
||||
else
|
||||
sprintf(suffix, "%d", i);
|
||||
sprintf(suffix, "%d", (int)i);
|
||||
std::string new_name = name + suffix;
|
||||
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
|
||||
new_name, std::move(cfg), i == 0);
|
||||
@ -837,7 +837,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
|
||||
return preset_name_dst;
|
||||
// Try to generate another name.
|
||||
char buf[64];
|
||||
sprintf(buf, " (%d)", i);
|
||||
sprintf(buf, " (%d)", (int)i);
|
||||
preset_name_dst = preset_name_src + buf + bundle_name;
|
||||
}
|
||||
}
|
||||
@ -1379,7 +1379,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
|
||||
for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
|
||||
char suffix[64];
|
||||
if (i > 0)
|
||||
sprintf(suffix, "_%d", i);
|
||||
sprintf(suffix, "_%d", (int)i);
|
||||
else
|
||||
suffix[0] = 0;
|
||||
c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl;
|
||||
|
@ -168,6 +168,11 @@ void ProgressStatusBar::set_status_text(const char *txt)
|
||||
this->set_status_text(wxString::FromUTF8(txt));
|
||||
}
|
||||
|
||||
wxString ProgressStatusBar::get_status_text() const
|
||||
{
|
||||
return self->GetStatusText();
|
||||
}
|
||||
|
||||
void ProgressStatusBar::show_cancel_button()
|
||||
{
|
||||
if(m_cancelbutton) m_cancelbutton->Show();
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class wxTimer;
|
||||
class wxGauge;
|
||||
@ -51,6 +52,7 @@ public:
|
||||
void set_status_text(const wxString& txt);
|
||||
void set_status_text(const std::string& txt);
|
||||
void set_status_text(const char *txt);
|
||||
wxString get_status_text() const;
|
||||
|
||||
// Temporary methods to satisfy Perl side
|
||||
void show_cancel_button();
|
||||
|
@ -296,6 +296,9 @@ void Selection::clear()
|
||||
if (!m_valid)
|
||||
return;
|
||||
|
||||
if (m_list.empty())
|
||||
return;
|
||||
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
(*m_volumes)[i]->selected = false;
|
||||
|
@ -333,6 +333,8 @@ private:
|
||||
void render_sidebar_rotation_hint(Axis axis) const;
|
||||
void render_sidebar_scale_hint(Axis axis) const;
|
||||
void render_sidebar_size_hint(Axis axis, double length) const;
|
||||
|
||||
public:
|
||||
enum SyncRotationType {
|
||||
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
|
||||
SYNC_ROTATION_NONE = 0,
|
||||
@ -343,6 +345,8 @@ private:
|
||||
};
|
||||
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
|
||||
void synchronize_unselected_volumes();
|
||||
|
||||
private:
|
||||
void ensure_on_bed();
|
||||
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
|
||||
|
||||
|
@ -1854,13 +1854,17 @@ void TabPrinter::build_fff()
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
auto dlg = new BedShapeDialog(this);
|
||||
dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
load_key_value("bed_shape", dlg->GetValue());
|
||||
update_changed_ui();
|
||||
}
|
||||
}));
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return sizer;
|
||||
};
|
||||
@ -2056,11 +2060,15 @@ void TabPrinter::build_sla()
|
||||
|
||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
||||
{
|
||||
auto dlg = new BedShapeDialog(this);
|
||||
dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg->ShowModal() == wxID_OK) {
|
||||
load_key_value("bed_shape", dlg->GetValue());
|
||||
update_changed_ui();
|
||||
BedShapeDialog dlg(this);
|
||||
dlg.build_dialog(m_config->option<ConfigOptionPoints>("bed_shape"));
|
||||
if (dlg.ShowModal() == wxID_OK) {
|
||||
std::vector<Vec2d> shape = dlg.GetValue();
|
||||
if (!shape.empty())
|
||||
{
|
||||
load_key_value("bed_shape", shape);
|
||||
update_changed_ui();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -586,7 +586,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||
ItemAdded(parent_item, child);
|
||||
|
||||
root->m_volumes_cnt++;
|
||||
if (insert_position > 0) insert_position++;
|
||||
if (insert_position >= 0) insert_position++;
|
||||
}
|
||||
|
||||
const auto node = new ObjectDataViewModelNode(root, name, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt);
|
||||
|
@ -389,10 +389,10 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
|
||||
throw std::runtime_error(L("Repaired 3MF file does not contain any volume"));
|
||||
if (model.objects.front()->volumes.size() > 1)
|
||||
throw std::runtime_error(L("Repaired 3MF file contains more than one volume"));
|
||||
meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh));
|
||||
meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh()));
|
||||
}
|
||||
for (size_t i = 0; i < volumes.size(); ++ i) {
|
||||
volumes[i]->mesh = std::move(meshes_repaired[i]);
|
||||
volumes[i]->set_mesh(std::move(meshes_repaired[i]));
|
||||
volumes[i]->set_new_unique_id();
|
||||
}
|
||||
model_object.invalidate_bounding_box();
|
||||
|
@ -384,7 +384,13 @@ void Serial::reset_line_num()
|
||||
|
||||
bool Serial::read_line(unsigned timeout, std::string &line, error_code &ec)
|
||||
{
|
||||
auto &io_service = get_io_service();
|
||||
auto& io_service =
|
||||
#if BOOST_VERSION >= 107000
|
||||
//FIXME this is most certainly wrong!
|
||||
(boost::asio::io_context&)this->get_executor().context();
|
||||
#else
|
||||
this->get_io_service();
|
||||
#endif
|
||||
asio::deadline_timer timer(io_service);
|
||||
char c = 0;
|
||||
bool fail = false;
|
||||
|
@ -119,8 +119,6 @@ target_include_directories(XS PRIVATE src ${LIBDIR}/libslic3r)
|
||||
target_compile_definitions(XS PRIVATE -DSLIC3RXS)
|
||||
set_target_properties(XS PROPERTIES PREFIX "") # Prevent cmake from generating libXS.so instead of XS.so
|
||||
|
||||
target_link_libraries(XS ${Boost_LIBRARIES})
|
||||
|
||||
if (APPLE)
|
||||
# -liconv: boost links to libiconv by default
|
||||
target_link_libraries(XS "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||
@ -156,12 +154,6 @@ if (WIN32)
|
||||
target_link_libraries(XS ${PERL_LIBRARY})
|
||||
endif()
|
||||
|
||||
target_link_libraries(XS ${Boost_LIBRARIES})
|
||||
target_link_libraries(XS ${TBB_LIBRARIES})
|
||||
# target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
||||
target_link_libraries(XS ${EXPAT_LIBRARIES})
|
||||
# target_link_libraries(XS ${GLEW_LIBRARIES})
|
||||
|
||||
# Install the XS.pm and XS.{so,dll,bundle} into the local-lib directory.
|
||||
set(PERL_LOCAL_LIB_DIR "../../local-lib/lib/perl5/${PerlEmbed_ARCHNAME}")
|
||||
add_custom_command(
|
||||
@ -181,10 +173,6 @@ if(APPLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(SLIC3R_PROFILE)
|
||||
target_link_libraries(Shiny)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# Here we associate some additional properties with the MSVC project to enable compilation and debugging out of the box.
|
||||
get_filename_component(PROPS_PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)
|
||||
|
@ -69,7 +69,10 @@ extern "C" {
|
||||
#undef fwrite
|
||||
#undef fclose
|
||||
#undef sleep
|
||||
#undef snprintf
|
||||
#undef strerror
|
||||
#undef test
|
||||
#undef times
|
||||
#undef accept
|
||||
#undef wait
|
||||
|
||||
|
@ -253,7 +253,7 @@ ModelMaterial::attributes()
|
||||
Ref<DynamicPrintConfig> config()
|
||||
%code%{ RETVAL = &THIS->config; %};
|
||||
Ref<TriangleMesh> mesh()
|
||||
%code%{ RETVAL = &THIS->mesh; %};
|
||||
%code%{ RETVAL = &THIS->mesh(); %};
|
||||
|
||||
bool modifier()
|
||||
%code%{ RETVAL = THIS->is_modifier(); %};
|
||||
|
@ -46,7 +46,6 @@ TriangleMesh::ReadFromPerl(vertices, facets)
|
||||
SV* facets
|
||||
CODE:
|
||||
stl_file &stl = THIS->stl;
|
||||
stl.error = 0;
|
||||
stl.stats.type = inmemory;
|
||||
|
||||
// count facets and allocate memory
|
||||
@ -99,20 +98,18 @@ SV*
|
||||
TriangleMesh::vertices()
|
||||
CODE:
|
||||
if (!THIS->repaired) CONFESS("vertices() requires repair()");
|
||||
|
||||
if (THIS->stl.v_shared == NULL)
|
||||
stl_generate_shared_vertices(&(THIS->stl));
|
||||
THIS->require_shared_vertices();
|
||||
|
||||
// vertices
|
||||
AV* vertices = newAV();
|
||||
av_extend(vertices, THIS->stl.stats.shared_vertices);
|
||||
for (int i = 0; i < THIS->stl.stats.shared_vertices; i++) {
|
||||
av_extend(vertices, THIS->its.vertices.size());
|
||||
for (size_t i = 0; i < THIS->its.vertices.size(); i++) {
|
||||
AV* vertex = newAV();
|
||||
av_store(vertices, i, newRV_noinc((SV*)vertex));
|
||||
av_extend(vertex, 2);
|
||||
av_store(vertex, 0, newSVnv(THIS->stl.v_shared[i](0)));
|
||||
av_store(vertex, 1, newSVnv(THIS->stl.v_shared[i](1)));
|
||||
av_store(vertex, 2, newSVnv(THIS->stl.v_shared[i](2)));
|
||||
av_store(vertex, 0, newSVnv(THIS->its.vertices[i](0)));
|
||||
av_store(vertex, 1, newSVnv(THIS->its.vertices[i](1)));
|
||||
av_store(vertex, 2, newSVnv(THIS->its.vertices[i](2)));
|
||||
}
|
||||
|
||||
RETVAL = newRV_noinc((SV*)vertices);
|
||||
@ -123,9 +120,7 @@ SV*
|
||||
TriangleMesh::facets()
|
||||
CODE:
|
||||
if (!THIS->repaired) CONFESS("facets() requires repair()");
|
||||
|
||||
if (THIS->stl.v_shared == NULL)
|
||||
stl_generate_shared_vertices(&(THIS->stl));
|
||||
THIS->require_shared_vertices();
|
||||
|
||||
// facets
|
||||
AV* facets = newAV();
|
||||
@ -134,9 +129,9 @@ TriangleMesh::facets()
|
||||
AV* facet = newAV();
|
||||
av_store(facets, i, newRV_noinc((SV*)facet));
|
||||
av_extend(facet, 2);
|
||||
av_store(facet, 0, newSVnv(THIS->stl.v_indices[i].vertex[0]));
|
||||
av_store(facet, 1, newSVnv(THIS->stl.v_indices[i].vertex[1]));
|
||||
av_store(facet, 2, newSVnv(THIS->stl.v_indices[i].vertex[2]));
|
||||
av_store(facet, 0, newSVnv(THIS->its.indices[i][0]));
|
||||
av_store(facet, 1, newSVnv(THIS->its.indices[i][1]));
|
||||
av_store(facet, 2, newSVnv(THIS->its.indices[i][2]));
|
||||
}
|
||||
|
||||
RETVAL = newRV_noinc((SV*)facets);
|
||||
|
Loading…
Reference in New Issue
Block a user