From 1cc7bd9976dc96eb119c0b80f9c8a9a2dab6aa7c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 25 Oct 2019 15:48:01 +0200 Subject: [PATCH 001/130] Fix openvdb dependency in libslic3r --- CMakeLists.txt | 3 -- cmake/modules/FindOpenVDB.cmake | 56 ++++++++++++++++++++++---------- cmake/modules/OpenVDBUtils.cmake | 8 +++-- sandboxes/openvdb/CMakeLists.txt | 6 ++++ 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b22f10016..ecae7e3c9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,9 +402,6 @@ if(SLIC3R_STATIC) set(USE_BLOSC TRUE) endif() -#find_package(OpenVDB 5.0 COMPONENTS openvdb) -#slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) - # libslic3r, PrusaSlicer GUI and the PrusaSlicer executable. add_subdirectory(src) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 9afe8a2356..dd4ff5b20e 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -108,6 +108,18 @@ if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() +if(OpenVDB_FIND_QUIETLY) + set (_quiet "QUIET") +else() + set (_quiet "") +endif() + +if(OpenVDB_FIND_REQUIRED) + set (_required "REQUIRED") +else() + set (_required "") +endif() + # Include utility functions for version information include(${CMAKE_CURRENT_LIST_DIR}/OpenVDBUtils.cmake) @@ -146,7 +158,7 @@ set(_OPENVDB_ROOT_SEARCH_DIR "") # Additionally try and use pkconfig to find OpenVDB -find_package(PkgConfig) +find_package(PkgConfig ${_quiet} ${_required}) pkg_check_modules(PC_OpenVDB QUIET OpenVDB) # ------------------------------------------------------------------------ @@ -250,7 +262,7 @@ OPENVDB_ABI_VERSION_FROM_PRINT( ABI OpenVDB_ABI ) -if(NOT OpenVDB_FIND_QUIET) +if(NOT OpenVDB_FIND_QUIETLY) if(NOT OpenVDB_ABI) message(WARNING "Unable to determine OpenVDB ABI version from OpenVDB " "installation. The library major version \"${OpenVDB_MAJOR_VERSION}\" " @@ -268,7 +280,17 @@ endif() # Add standard dependencies -find_package(IlmBase COMPONENTS Half) +macro(just_fail msg) + set(OpenVDB_FOUND FALSE) + if(OpenVDB_FIND_REQUIRED) + message(FATAL_ERROR msg) + elseif(NOT OpenVDB_FIND_QUIETLY) + message(ERROR msg) + endif() + return() +endmacro() + +find_package(IlmBase QUIET COMPONENTS Half) if(NOT IlmBase_FOUND) pkg_check_modules(IlmBase QUIET IlmBase) endif() @@ -276,20 +298,20 @@ if (IlmBase_FOUND AND NOT TARGET IlmBase::Half) message(STATUS "Falling back to IlmBase found by pkg-config...") find_library(IlmHalf_LIBRARY NAMES Half) - if(IlmHalf_LIBRARY-NOTFOUND) - message(FATAL_ERROR "IlmBase::Half can not be found!") + if(IlmHalf_LIBRARY-NOTFOUND OR NOT IlmBase_INCLUDE_DIRS) + just_fail("IlmBase::Half can not be found!") endif() add_library(IlmBase::Half UNKNOWN IMPORTED) set_target_properties(IlmBase::Half PROPERTIES IMPORTED_LOCATION "${IlmHalf_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES ${IlmBase_INCLUDE_DIRS}) + INTERFACE_INCLUDE_DIRECTORIES "${IlmBase_INCLUDE_DIRS}") elseif(NOT IlmBase_FOUND) - message(FATAL_ERROR "IlmBase::Half can not be found!") + just_fail("IlmBase::Half can not be found!") endif() -find_package(TBB REQUIRED COMPONENTS tbb) -find_package(ZLIB REQUIRED) -find_package(Boost REQUIRED COMPONENTS iostreams system) +find_package(TBB ${_quiet} ${_required} COMPONENTS tbb) +find_package(ZLIB ${_quiet} ${_required}) +find_package(Boost ${_quiet} ${_required} COMPONENTS iostreams system ) # Use GetPrerequisites to see which libraries this OpenVDB lib has linked to # which we can query for optional deps. This basically runs ldd/otoll/objdump @@ -350,7 +372,7 @@ unset(_OPENVDB_PREREQUISITE_LIST) unset(_HAS_DEP) if(OpenVDB_USES_BLOSC) - find_package(Blosc ) + find_package(Blosc QUIET) if(NOT Blosc_FOUND OR NOT TARGET Blosc::blosc) message(STATUS "find_package could not find Blosc. Using fallback blosc search...") find_path(Blosc_INCLUDE_DIR blosc.h) @@ -362,25 +384,25 @@ if(OpenVDB_USES_BLOSC) IMPORTED_LOCATION "${Blosc_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES ${Blosc_INCLUDE_DIR}) elseif() - message(FATAL_ERROR "Blosc library can not be found!") + just_fail("Blosc library can not be found!") endif() endif() endif() if(OpenVDB_USES_LOG4CPLUS) - find_package(Log4cplus REQUIRED) + find_package(Log4cplus ${_quiet} ${_required}) endif() if(OpenVDB_USES_ILM) - find_package(IlmBase REQUIRED) + find_package(IlmBase ${_quiet} ${_required}) endif() if(OpenVDB_USES_EXR) - find_package(OpenEXR REQUIRED) + find_package(OpenEXR ${_quiet} ${_required}) endif() if(UNIX) - find_package(Threads REQUIRED) + find_package(Threads ${_quiet} ${_required}) endif() # Set deps. Note that the order here is important. If we're building against @@ -481,7 +503,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) endif() endforeach() -if(OpenVDB_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) +if(OpenVDB_FOUND AND NOT OpenVDB_FIND_QUIETLY) message(STATUS "OpenVDB libraries: ${OpenVDB_LIBRARIES}") endif() diff --git a/cmake/modules/OpenVDBUtils.cmake b/cmake/modules/OpenVDBUtils.cmake index bb3ce6e65d..f64eda6f2c 100644 --- a/cmake/modules/OpenVDBUtils.cmake +++ b/cmake/modules/OpenVDBUtils.cmake @@ -125,7 +125,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT) cmake_parse_arguments(_VDB "QUIET" "ABI" "" ${ARGN}) if(NOT EXISTS ${OPENVDB_PRINT}) - message(WARNING "vdb_print not found! ${OPENVDB_PRINT}") + if(NOT OpenVDB_FIND_QUIETLY) + message(WARNING "vdb_print not found! ${OPENVDB_PRINT}") + endif() return() endif() @@ -148,7 +150,9 @@ function(OPENVDB_ABI_VERSION_FROM_PRINT OPENVDB_PRINT) endif() if(${_VDB_PRINT_RETURN_STATUS}) - message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") + if(NOT OpenVDB_FIND_QUIETLY) + message(WARNING "vdb_print returned with status ${_VDB_PRINT_RETURN_STATUS}") + endif() return() endif() diff --git a/sandboxes/openvdb/CMakeLists.txt b/sandboxes/openvdb/CMakeLists.txt index 184452e833..250c6cc68b 100644 --- a/sandboxes/openvdb/CMakeLists.txt +++ b/sandboxes/openvdb/CMakeLists.txt @@ -1,2 +1,8 @@ add_executable(openvdb_example openvdb_example.cpp) + +find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb) +slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) + target_link_libraries(openvdb_example libslic3r) +target_link_libraries(openvdb_example OpenVDB::openvdb) + From 39df8a5eded7dcc6e19142f0dbc468cf73b4f458 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 25 Oct 2019 16:19:50 +0200 Subject: [PATCH 002/130] Enable example test --- tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7e8db00bf3..e84d312e9a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,4 +27,4 @@ add_subdirectory(libslic3r) add_subdirectory(timeutils) add_subdirectory(fff_print) add_subdirectory(sla_print) -# add_subdirectory(example) +add_subdirectory(example) From 6f8ce122878823d1b703dfaaab9c6d2101809ed9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Oct 2019 13:33:29 +0100 Subject: [PATCH 003/130] Useful verbose test output on console with Catch2 --- tests/CMakeLists.txt | 2 +- tests/catch_main.hpp | 51 ++++++++++++++++++++++++ tests/example/example_tests_main.cpp | 2 +- tests/fff_print/fff_print_tests.cpp | 3 +- tests/libnest2d/libnest2d_tests_main.cpp | 4 +- tests/libslic3r/libslic3r_tests.cpp | 3 +- tests/sla_print/sla_print_tests.cpp | 3 +- tests/timeutils/timeutils_tests_main.cpp | 3 +- 8 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 tests/catch_main.hpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e84d312e9a..3afc1c3e26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) add_library(Catch2::Catch2 ALIAS Catch2) include(Catch) -set(CATCH_EXTRA_ARGS "--durations yes" CACHE STRING "Extra arguments for catch2 test suites.") +set(CATCH_EXTRA_ARGS "-r usefulconsole" CACHE STRING "Extra arguments for catch2 test suites.") add_library(test_common INTERFACE) target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE) diff --git a/tests/catch_main.hpp b/tests/catch_main.hpp new file mode 100644 index 0000000000..e49ff40b4a --- /dev/null +++ b/tests/catch_main.hpp @@ -0,0 +1,51 @@ +#ifndef CATCH_MAIN +#define CATCH_MAIN + +#define CATCH_CONFIG_EXTERNAL_INTERFACES +#define CATCH_CONFIG_MAIN +#include + +namespace Catch { +struct VerboseConsoleReporter : public ConsoleReporter { + double duration = 0.; + using ConsoleReporter::ConsoleReporter; + + void testCaseStarting(TestCaseInfo const& _testInfo) override + { + Colour::use(Colour::Cyan); + stream << "Testing "; + Colour::use(Colour::None); + stream << _testInfo.name << std::endl; + ConsoleReporter::testCaseStarting(_testInfo); + } + + void sectionStarting(const SectionInfo &_sectionInfo) override + { + if (_sectionInfo.name != currentTestCaseInfo->name) + stream << _sectionInfo.name << std::endl; + + ConsoleReporter::sectionStarting(_sectionInfo); + } + + void sectionEnded(const SectionStats &_sectionStats) override { + duration += _sectionStats.durationInSeconds; + ConsoleReporter::sectionEnded(_sectionStats); + } + + void testCaseEnded(TestCaseStats const& stats) override + { + if (stats.totals.assertions.allOk()) { + Colour::use(Colour::BrightGreen); + stream << "Passed"; + Colour::use(Colour::None); + stream << " in " << duration << " [seconds]\n" << std::endl; + } + + duration = 0.; + ConsoleReporter::testCaseEnded(stats); + } +}; +CATCH_REGISTER_REPORTER( "verboseconsole", VerboseConsoleReporter ) +} + +#endif // CATCH_MAIN diff --git a/tests/example/example_tests_main.cpp b/tests/example/example_tests_main.cpp index d612f323cb..32e8d02b76 100644 --- a/tests/example/example_tests_main.cpp +++ b/tests/example/example_tests_main.cpp @@ -1,5 +1,5 @@ #define CATCH_CONFIG_MAIN -#include +#include TEST_CASE("Is example succesful", "[example]") { REQUIRE(true); diff --git a/tests/fff_print/fff_print_tests.cpp b/tests/fff_print/fff_print_tests.cpp index 5e9b82f80b..46358e5eba 100644 --- a/tests/fff_print/fff_print_tests.cpp +++ b/tests/fff_print/fff_print_tests.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_MAIN -#include +#include #include "libslic3r/libslic3r.h" diff --git a/tests/libnest2d/libnest2d_tests_main.cpp b/tests/libnest2d/libnest2d_tests_main.cpp index 252bea47fe..c7259ae537 100644 --- a/tests/libnest2d/libnest2d_tests_main.cpp +++ b/tests/libnest2d/libnest2d_tests_main.cpp @@ -1,9 +1,7 @@ -#define CATCH_CONFIG_MAIN -#include +#include #include - #include #include "printer_parts.hpp" //#include diff --git a/tests/libslic3r/libslic3r_tests.cpp b/tests/libslic3r/libslic3r_tests.cpp index 907304f57a..caf5b3b9ac 100644 --- a/tests/libslic3r/libslic3r_tests.cpp +++ b/tests/libslic3r/libslic3r_tests.cpp @@ -1,5 +1,4 @@ -#define CATCH_CONFIG_MAIN -#include +#include #include "libslic3r/libslic3r.h" diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index f41fd3200a..229eb42676 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -1,5 +1,4 @@ -#define CATCH_CONFIG_MAIN -#include +#include #include #include diff --git a/tests/timeutils/timeutils_tests_main.cpp b/tests/timeutils/timeutils_tests_main.cpp index c3827374e5..9989f98716 100644 --- a/tests/timeutils/timeutils_tests_main.cpp +++ b/tests/timeutils/timeutils_tests_main.cpp @@ -1,5 +1,4 @@ -#define CATCH_CONFIG_MAIN -#include +#include #include "libslic3r/Time.hpp" From d63ae1c6085f02fa64b250b01071a3ec94406504 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Oct 2019 16:27:53 +0100 Subject: [PATCH 004/130] Simple openvdb conversion test. --- CMakeLists.txt | 5 +++ sandboxes/openvdb/CMakeLists.txt | 11 +++---- tests/CMakeLists.txt | 3 +- tests/hollowing/CMakeLists.txt | 8 +++++ tests/hollowing/hollowing_tests.cpp | 49 +++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 tests/hollowing/CMakeLists.txt create mode 100644 tests/hollowing/hollowing_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ecae7e3c9a..c6ddbb0f9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,6 +402,11 @@ if(SLIC3R_STATIC) set(USE_BLOSC TRUE) endif() +find_package(OpenVDB 5.0 COMPONENTS openvdb) +if(OpenVDB_FOUND) + slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) +endif() + # libslic3r, PrusaSlicer GUI and the PrusaSlicer executable. add_subdirectory(src) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console) diff --git a/sandboxes/openvdb/CMakeLists.txt b/sandboxes/openvdb/CMakeLists.txt index 250c6cc68b..c32d6c8d63 100644 --- a/sandboxes/openvdb/CMakeLists.txt +++ b/sandboxes/openvdb/CMakeLists.txt @@ -1,8 +1,7 @@ -add_executable(openvdb_example openvdb_example.cpp) +if(TARGET OpenVDB::openvdb) + add_executable(openvdb_example openvdb_example.cpp) -find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb) -slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) - -target_link_libraries(openvdb_example libslic3r) -target_link_libraries(openvdb_example OpenVDB::openvdb) + target_link_libraries(openvdb_example libslic3r) + target_link_libraries(openvdb_example OpenVDB::openvdb) +endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3afc1c3e26..2dfe05dad3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,4 +27,5 @@ add_subdirectory(libslic3r) add_subdirectory(timeutils) add_subdirectory(fff_print) add_subdirectory(sla_print) -add_subdirectory(example) +add_subdirectory(hollowing) +#add_subdirectory(example) diff --git a/tests/hollowing/CMakeLists.txt b/tests/hollowing/CMakeLists.txt new file mode 100644 index 0000000000..e6f01b0e83 --- /dev/null +++ b/tests/hollowing/CMakeLists.txt @@ -0,0 +1,8 @@ +if(TARGET OpenVDB::openvdb) + add_executable(hollowing_tests hollowing_tests.cpp) + + find_package(GTest REQUIRED) + + target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) + target_compile_definitions(hollowing_tests PRIVATE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") +endif() diff --git a/tests/hollowing/hollowing_tests.cpp b/tests/hollowing/hollowing_tests.cpp new file mode 100644 index 0000000000..2ba61a63dd --- /dev/null +++ b/tests/hollowing/hollowing_tests.cpp @@ -0,0 +1,49 @@ +#include +#include + +#include +#include +#include +#include "libslic3r/Format/OBJ.hpp" + +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR R"(\)" +#else +#define PATH_SEPARATOR R"(/)" +#endif + +class TriangleMeshDataAdapter { +public: + Slic3r::TriangleMesh mesh; + + size_t polygonCount() const { return mesh.its.indices.size(); } + size_t pointCount() const { return mesh.its.vertices.size(); } + size_t vertexCount(size_t) const { return 3; } + + // Return position pos in local grid index space for polygon n and vertex v + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const { + auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); + Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); + pos = {double(p.x()), double(p.y()), p.z()}; + } +}; + +static Slic3r::TriangleMesh load_model(const std::string &obj_filename) +{ + Slic3r::TriangleMesh mesh; + auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; + Slic3r::load_obj(fpath.c_str(), &mesh); + return mesh; +} + +TEST(Hollowing, LoadObject) { + TriangleMeshDataAdapter mesh{load_model("20mm_cube.obj")}; + auto ptr = openvdb::tools::meshToVolume(mesh, {}); + + ASSERT_TRUE(ptr); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 99dd6acc7954fa3c7c1086b4658c78f13d6fea61 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 10:47:04 +0100 Subject: [PATCH 005/130] Make verboseconsole the default reporter. --- tests/CMakeLists.txt | 2 +- tests/catch_main.hpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2dfe05dad3..9453fa3143 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ target_include_directories(Catch2 INTERFACE ${CMAKE_CURRENT_LIST_DIR}) add_library(Catch2::Catch2 ALIAS Catch2) include(Catch) -set(CATCH_EXTRA_ARGS "-r usefulconsole" CACHE STRING "Extra arguments for catch2 test suites.") +set(CATCH_EXTRA_ARGS "" CACHE STRING "Extra arguments for catch2 test suites.") add_library(test_common INTERFACE) target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)" CATCH_CONFIG_FAST_COMPILE) diff --git a/tests/catch_main.hpp b/tests/catch_main.hpp index e49ff40b4a..5ab71fdd74 100644 --- a/tests/catch_main.hpp +++ b/tests/catch_main.hpp @@ -3,6 +3,7 @@ #define CATCH_CONFIG_EXTERNAL_INTERFACES #define CATCH_CONFIG_MAIN +#define CATCH_CONFIG_DEFAULT_REPORTER "verboseconsole" #include namespace Catch { @@ -45,7 +46,9 @@ struct VerboseConsoleReporter : public ConsoleReporter { ConsoleReporter::testCaseEnded(stats); } }; + CATCH_REGISTER_REPORTER( "verboseconsole", VerboseConsoleReporter ) -} + +} // namespace Catch #endif // CATCH_MAIN From 259e058491e877d46064eb962fa31e3231bf1d50 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 12:38:23 +0100 Subject: [PATCH 006/130] openvdb conversion experiments. --- tests/hollowing/CMakeLists.txt | 9 +++++---- tests/hollowing/hollowing_tests.cpp | 26 +++++++++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/tests/hollowing/CMakeLists.txt b/tests/hollowing/CMakeLists.txt index e6f01b0e83..79c12baef8 100644 --- a/tests/hollowing/CMakeLists.txt +++ b/tests/hollowing/CMakeLists.txt @@ -1,8 +1,9 @@ if(TARGET OpenVDB::openvdb) add_executable(hollowing_tests hollowing_tests.cpp) - find_package(GTest REQUIRED) - - target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) - target_compile_definitions(hollowing_tests PRIVATE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") + #find_package(GTest REQUIRED) + #target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) + #target_compile_definitions(hollowing_tests PRIVATE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") + + target_link_libraries(hollowing_tests test_common libslic3r OpenVDB::openvdb) endif() diff --git a/tests/hollowing/hollowing_tests.cpp b/tests/hollowing/hollowing_tests.cpp index 2ba61a63dd..846f7e831b 100644 --- a/tests/hollowing/hollowing_tests.cpp +++ b/tests/hollowing/hollowing_tests.cpp @@ -1,8 +1,9 @@ #include -#include +#include #include #include +#include #include #include "libslic3r/Format/OBJ.hpp" @@ -36,14 +37,25 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -TEST(Hollowing, LoadObject) { +TEST_CASE("Load object", "[Hollowing]") { TriangleMeshDataAdapter mesh{load_model("20mm_cube.obj")}; auto ptr = openvdb::tools::meshToVolume(mesh, {}); - ASSERT_TRUE(ptr); + REQUIRE(ptr); + + std::vector points; + std::vector quad_indices; + std::vector triangle_indices; + + openvdb::tools::volumeToMesh(*ptr, points, triangle_indices, quad_indices, 0.0, 1.0, true); + + std::cout << "Triangle count: " << triangle_indices.size() << std::endl; + std::cout << "Quad count: " << quad_indices.size() << std::endl; + std::cout << "Point count: " << points.size() << " vs " << mesh.mesh.its.vertices.size() << std::endl; } -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} +//int main(int argc, char **argv) +//{ +// ::testing::InitGoogleTest(&argc, argv); +// return RUN_ALL_TESTS(); +//} From 5c5a3948f96f34e6ab78ce390468607b764060c1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 11:34:19 +0100 Subject: [PATCH 007/130] fix openvdb platform forwarding on Windows --- deps/deps-windows.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 08e10758d3..26763e878e 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -309,6 +309,7 @@ ExternalProject_Add(dep_blosc GIT_TAG v1.17.0 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DBUILD_SHARED_LIBS=OFF @@ -332,6 +333,7 @@ ExternalProject_Add(dep_openexr GIT_TAG v2.4.0 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DBUILD_SHARED_LIBS=OFF @@ -352,6 +354,7 @@ ExternalProject_Add(dep_openvdb GIT_TAG v6.2.1 DEPENDS dep_blosc dep_openexr dep_tbb CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DCMAKE_DEBUG_POSTFIX=d From 189bd1a8cefb9ef93c3632ee0e5b7ffead0a98aa Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 14:50:06 +0100 Subject: [PATCH 008/130] Fixes for release only build of dependencies on windows --- deps/blosc-mods.patch | 135 +++++++------- deps/deps-windows.cmake | 44 +++-- deps/openvdb-mods.patch | 379 +++++++++++++++++++++------------------- 3 files changed, 279 insertions(+), 279 deletions(-) diff --git a/deps/blosc-mods.patch b/deps/blosc-mods.patch index 2289459ab5..9a91b4974c 100644 --- a/deps/blosc-mods.patch +++ b/deps/blosc-mods.patch @@ -1,23 +1,22 @@ -From 24640a466b28dfda26069096554676e8c0b6d090 Mon Sep 17 00:00:00 2001 +From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001 From: tamasmeszaros -Date: Tue, 22 Oct 2019 11:29:05 +0200 -Subject: [PATCH] Install.dll in prefix/bin and add config export to cmake - build +Date: Wed, 30 Oct 2019 12:54:52 +0100 +Subject: [PATCH] Blosc 1.17 fixes and cmake config script --- - CMakeLists.txt | 112 ++++++++++++++++++++---------------- - blosc/CMakeLists.txt | 121 ++++++++++----------------------------- + CMakeLists.txt | 105 +++++++++++++++++----------------- + blosc/CMakeLists.txt | 118 +++++++++------------------------------ cmake/FindLZ4.cmake | 6 +- cmake/FindSnappy.cmake | 8 ++- cmake/FindZstd.cmake | 8 ++- - cmake_config.cmake.in | 33 +++++++++++ - internal-complibs/CMakeLists.txt | 30 ++++++++++ - 7 files changed, 173 insertions(+), 145 deletions(-) + cmake_config.cmake.in | 24 ++++++++ + internal-complibs/CMakeLists.txt | 35 ++++++++++++ + 7 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 cmake_config.cmake.in create mode 100644 internal-complibs/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt -index 59d9fab..bdc0dda 100644 +index 59d9fab..e9134c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ @@ -29,36 +28,11 @@ index 59d9fab..bdc0dda 100644 if (NOT CMAKE_VERSION VERSION_LESS 3.3) cmake_policy(SET CMP0063 NEW) endif() -@@ -124,55 +124,37 @@ option(PREFER_EXTERNAL_ZSTD +@@ -124,55 +124,30 @@ option(PREFER_EXTERNAL_ZSTD set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") -+set(PRIVATE_LIBS "") -+set(PUBLIC_LIBS "") -+set(PUBLIC_PACKAGES "" CACHE INTERNAL "") -+macro(use_package _pkg _tgt) -+ string(TOUPPER ${_pkg} _PKG) -+ if(NOT DEACTIVATE_${_PKG}) -+ if(PREFER_EXTERNAL_${_PKG}) -+ find_package(${_pkg}) -+ if (NOT ${_pkg}_FOUND ) -+ message(STATUS "No ${_pkg} found. Using internal sources.") -+ endif() -+ else() -+ message(STATUS "Using ${_pkg} internal sources.") -+ endif(PREFER_EXTERNAL_${_PKG}) -+ # HAVE_${_pkg} will be set to true because even if the library is -+ # not found, we will use the included sources for it -+ set(HAVE_${_PKG} TRUE) -+ if (${_pkg}_FOUND) -+ list(APPEND PUBLIC_LIBS ${_pkg}::${_tgt}) -+ set(PUBLIC_PACKAGES "${PUBLIC_PACKAGES};${_pkg}" CACHE INTERNAL "") -+ else() -+ list(APPEND PRIVATE_LIBS ${_pkg}::${_tgt}) -+ endif() -+ endif(NOT DEACTIVATE_${_PKG}) -+endmacro() - +- -if(NOT DEACTIVATE_LZ4) - if(PREFER_EXTERNAL_LZ4) - find_package(LZ4) @@ -107,6 +81,25 @@ index 59d9fab..bdc0dda 100644 - # not found, we will use the included sources for it - set(HAVE_ZSTD TRUE) -endif (NOT DEACTIVATE_ZSTD) ++set(LIBS "") ++macro(use_package _pkg _tgt) ++ string(TOUPPER ${_pkg} _PKG) ++ if(NOT DEACTIVATE_${_PKG}) ++ if(PREFER_EXTERNAL_${_PKG}) ++ find_package(${_pkg}) ++ if (NOT ${_pkg}_FOUND ) ++ message(STATUS "No ${_pkg} found. Using internal sources.") ++ endif() ++ else() ++ message(STATUS "Using ${_pkg} internal sources.") ++ endif(PREFER_EXTERNAL_${_PKG}) ++ # HAVE_${_pkg} will be set to true because even if the library is ++ # not found, we will use the included sources for it ++ set(HAVE_${_PKG} TRUE) ++ list(APPEND LIBS ${_pkg}::${_tgt}) ++ endif(NOT DEACTIVATE_${_PKG}) ++endmacro() ++ +set(ZLIB_ROOT $ENV{ZLIB_ROOT}) +use_package(ZLIB ZLIB) +use_package(LZ4 LZ4) @@ -115,7 +108,7 @@ index 59d9fab..bdc0dda 100644 # create the config.h file configure_file ("blosc/config.h.in" "blosc/config.h" ) -@@ -316,6 +298,7 @@ endif() +@@ -316,6 +291,7 @@ endif() # subdirectories @@ -123,7 +116,15 @@ index 59d9fab..bdc0dda 100644 add_subdirectory(blosc) if(BUILD_TESTS) -@@ -338,10 +321,41 @@ if (BLOSC_INSTALL) +@@ -328,7 +304,6 @@ if(BUILD_BENCHMARKS) + add_subdirectory(bench) + endif(BUILD_BENCHMARKS) + +- + # uninstall target + if (BLOSC_INSTALL) + configure_file( +@@ -338,10 +313,38 @@ if (BLOSC_INSTALL) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/blosc.pc" DESTINATION lib/pkgconfig COMPONENT DEV) @@ -157,16 +158,13 @@ index 59d9fab..bdc0dda 100644 + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmakeexports/BloscConfigVersion.cmake" -+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLZ4.cmake" -+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindZstd.cmake" -+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSnappy.cmake" + DESTINATION lib/cmake/Blosc COMPONENT DEV) + add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() diff --git a/blosc/CMakeLists.txt b/blosc/CMakeLists.txt -index 1d1bebe..16aff02 100644 +index 1d1bebe..f554abe 100644 --- a/blosc/CMakeLists.txt +++ b/blosc/CMakeLists.txt @@ -1,52 +1,11 @@ @@ -222,19 +220,17 @@ index 1d1bebe..16aff02 100644 # library sources set(SOURCES blosc.c blosclz.c fastcopy.c shuffle-generic.c bitshuffle-generic.c -@@ -73,53 +32,15 @@ if(WIN32) +@@ -73,53 +32,13 @@ if(WIN32) message(STATUS "using the internal pthread library for win32 systems.") set(SOURCES ${SOURCES} win32/pthread.c) else(NOT Threads_FOUND) - set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) -+ list(APPEND PUBLIC_LIBS Threads::Threads) -+ set(PUBLIC_PACKAGES "${PUBLIC_PACKAGES};Threads" CACHE INTERNAL "") ++ list(APPEND LIBS Threads::Threads) endif(NOT Threads_FOUND) else(WIN32) find_package(Threads REQUIRED) - set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) -+ list(APPEND PUBLIC_LIBS Threads::Threads) -+ set(PUBLIC_PACKAGES "${PUBLIC_PACKAGES};Threads" CACHE INTERNAL "") ++ list(APPEND LIBS Threads::Threads) endif(WIN32) -if(NOT DEACTIVATE_LZ4) @@ -280,7 +276,7 @@ index 1d1bebe..16aff02 100644 # targets if (BUILD_SHARED) add_library(blosc_shared SHARED ${SOURCES}) -@@ -191,14 +112,18 @@ if (BUILD_TESTS) +@@ -191,14 +110,17 @@ if (BUILD_TESTS) endif() endif() @@ -289,8 +285,7 @@ index 1d1bebe..16aff02 100644 if (BUILD_SHARED) - target_link_libraries(blosc_shared ${LIBS}) - target_include_directories(blosc_shared PUBLIC ${BLOSC_INCLUDE_DIRS}) -+ target_link_libraries(blosc_shared PUBLIC ${PUBLIC_LIBS}) -+ target_link_libraries(blosc_shared PRIVATE ${PRIVATE_LIBS}) ++ target_link_libraries(blosc_shared PRIVATE ${LIBS}) + target_include_directories(blosc_shared PUBLIC $) + target_link_libraries(blosc INTERFACE blosc_shared) endif() @@ -298,19 +293,19 @@ index 1d1bebe..16aff02 100644 if (BUILD_TESTS) - target_link_libraries(blosc_shared_testing ${LIBS}) - target_include_directories(blosc_shared_testing PUBLIC ${BLOSC_INCLUDE_DIRS}) -+ target_link_libraries(blosc_shared_testing ${PUBLIC_LIBS} ${PRIVATE_LIBS}) ++ target_link_libraries(blosc_shared_testing PRIVATE ${LIBS}) + target_include_directories(blosc_shared_testing PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) endif() if(BUILD_STATIC) -@@ -207,17 +132,31 @@ if(BUILD_STATIC) +@@ -207,17 +129,31 @@ if(BUILD_STATIC) if (MSVC) set_target_properties(blosc_static PROPERTIES PREFIX lib) endif() - target_link_libraries(blosc_static ${LIBS}) - target_include_directories(blosc_static PUBLIC ${BLOSC_INCLUDE_DIRS}) -+ target_link_libraries(blosc_static PUBLIC ${PUBLIC_LIBS}) -+ target_link_libraries(blosc_static PRIVATE ${PRIVATE_LIBS}) ++ # With the static library, cmake has to deal with transitive dependencies ++ target_link_libraries(blosc_static PRIVATE ${LIBS}) + target_include_directories(blosc_static PUBLIC $) + if (NOT BUILD_SHARED) + target_link_libraries(blosc INTERFACE blosc_static) @@ -398,24 +393,15 @@ index 7db4bb9..cabc2f8 100644 \ No newline at end of file diff --git a/cmake_config.cmake.in b/cmake_config.cmake.in new file mode 100644 -index 0000000..b4ede30 +index 0000000..0f6af24 --- /dev/null +++ b/cmake_config.cmake.in -@@ -0,0 +1,33 @@ +@@ -0,0 +1,24 @@ +include(CMakeFindDependencyMacro) + -+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) -+ -+set(_deps "@PUBLIC_PACKAGES@") -+ -+foreach(pkg ${_deps}) -+ # no minimum versions are required by upstream -+ find_dependency(${pkg}) -+endforeach() -+ +include("${CMAKE_CURRENT_LIST_DIR}/BloscTargets.cmake") + -+function(remap_configs from_Cfg to_Cfg) ++function(_blosc_remap_configs from_Cfg to_Cfg) + string(TOUPPER ${from_Cfg} from_CFG) + string(TOLOWER ${from_Cfg} from_cfg) + @@ -432,21 +418,25 @@ index 0000000..b4ede30 +# MSVC will try to link RelWithDebInfo or MinSizeRel target with debug config +# if no matching installation is present which would result in link errors. +if(MSVC) -+ remap_configs(RelWithDebInfo Release) -+ remap_configs(MinSizeRel Release) ++ _blosc_remap_configs(RelWithDebInfo Release) ++ _blosc_remap_configs(MinSizeRel Release) +endif() diff --git a/internal-complibs/CMakeLists.txt b/internal-complibs/CMakeLists.txt new file mode 100644 -index 0000000..5b23484 +index 0000000..4586efa --- /dev/null +++ b/internal-complibs/CMakeLists.txt -@@ -0,0 +1,30 @@ +@@ -0,0 +1,35 @@ +macro(add_lib_target pkg tgt incdir files) + string(TOUPPER ${pkg} TGT) + if(NOT DEACTIVATE_${TGT} AND NOT ${pkg}_FOUND) ++ add_library(${tgt}_objs OBJECT ${files}) + add_library(${tgt} INTERFACE) ++ target_include_directories(${tgt}_objs PRIVATE $) + target_include_directories(${tgt} INTERFACE $) -+ target_sources(${tgt} INTERFACE "$") ++ #set_target_properties(${tgt} PROPERTIES INTERFACE_SOURCES "$") ++ set_target_properties(${tgt}_objs PROPERTIES POSITION_INDEPENDENT_CODE ON) ++ target_sources(${tgt} INTERFACE "$>") + add_library(${pkg}::${tgt} ALIAS ${tgt}) + + # This creates dummy (empty) interface targets in the exported config. @@ -471,6 +461,7 @@ index 0000000..5b23484 +file(GLOB ZSTD_FILES ${ZSTD_DIR}/common/*.c ${ZSTD_DIR}/compress/*.c ${ZSTD_DIR}/decompress/*.c) +add_lib_target(Zstd Zstd ${ZSTD_DIR} "${ZSTD_FILES}") +target_include_directories(Zstd INTERFACE $) ++target_include_directories(Zstd_objs PRIVATE $) \ No newline at end of file -- 2.16.2.windows.1 diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 26763e878e..fcb300062c 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -81,7 +81,6 @@ ExternalProject_Add(dep_boost INSTALL_COMMAND "" # b2 does that already ) - ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" @@ -99,22 +98,22 @@ ExternalProject_Add(dep_tbb add_debug_dep(dep_tbb) -ExternalProject_Add(dep_gtest - EXCLUDE_FROM_ALL 1 - 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 - -DCMAKE_POSITION_INDEPENDENT_CODE=ON - "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" - BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - INSTALL_COMMAND "" -) +# ExternalProject_Add(dep_gtest +# EXCLUDE_FROM_ALL 1 +# 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 +# -DCMAKE_POSITION_INDEPENDENT_CODE=ON +# "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" +# BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj +# INSTALL_COMMAND "" +# ) -add_debug_dep(dep_gtest) +# add_debug_dep(dep_gtest) ExternalProject_Add(dep_cereal EXCLUDE_FROM_ALL 1 @@ -181,7 +180,6 @@ if (${DEP_DEBUG}) ) endif () - if (${DEPS_BITS} EQUAL 32) set(DEP_LIBCURL_TARGET "x86") else () @@ -305,8 +303,8 @@ endif () ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY https://github.com/Blosc/c-blosc.git - GIT_TAG v1.17.0 + URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip + URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" @@ -331,7 +329,7 @@ ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git GIT_TAG v2.4.0 - DEPENDS dep_zlib + DEPENDS CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -350,9 +348,9 @@ add_debug_dep(dep_openexr) ExternalProject_Add(dep_openvdb EXCLUDE_FROM_ALL 1 - GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git - GIT_TAG v6.2.1 - DEPENDS dep_blosc dep_openexr dep_tbb + URL https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.2.1.zip + URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 + DEPENDS dep_blosc dep_openexr dep_tbb dep_boost CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index a05076e986..60687b8d17 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,4 +1,4 @@ -From ee867b9f226412c0f3b83fa01cd43539acc4ed95 Mon Sep 17 00:00:00 2001 +From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Oct 2019 17:42:50 +0200 Subject: [PATCH] Build fixes for PrusaSlicer integration @@ -7,17 +7,17 @@ Subject: [PATCH] Build fixes for PrusaSlicer integration CMakeLists.txt | 3 - cmake/FindBlosc.cmake | 218 --------------- cmake/FindCppUnit.cmake | 4 +- - cmake/FindIlmBase.cmake | 337 ----------------------- + cmake/FindIlmBase.cmake | 337 ---------------------- cmake/FindOpenEXR.cmake | 329 ---------------------- cmake/FindOpenVDB.cmake | 19 +- - cmake/FindTBB.cmake | 593 ++++++++++++++++++++-------------------- + cmake/FindTBB.cmake | 605 ++++++++++++++++++++-------------------- openvdb/CMakeLists.txt | 13 +- openvdb/Grid.cc | 3 + openvdb/PlatformConfig.h | 9 +- openvdb/cmd/CMakeLists.txt | 4 +- openvdb/unittest/CMakeLists.txt | 3 +- openvdb/unittest/TestFile.cc | 2 +- - 13 files changed, 325 insertions(+), 1212 deletions(-) + 13 files changed, 336 insertions(+), 1213 deletions(-) delete mode 100644 cmake/FindBlosc.cmake delete mode 100644 cmake/FindIlmBase.cmake delete mode 100644 cmake/FindOpenEXR.cmake @@ -1011,10 +1011,10 @@ index 63a2eda..6211071 100644 endforeach() diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake -index bdf9c81..ffdee03 100644 +index bdf9c81..c6bdec9 100644 --- a/cmake/FindTBB.cmake +++ b/cmake/FindTBB.cmake -@@ -1,333 +1,322 @@ +@@ -1,333 +1,332 @@ -# Copyright (c) DreamWorks Animation LLC +# The MIT License (MIT) # @@ -1205,10 +1205,6 @@ index bdf9c81..ffdee03 100644 +# +# This module will also create the "tbb" target that may be used when building +# executables and libraries. -+ -+include(FindPackageHandleStandardArgs) -+ -+if(NOT TBB_FOUND) -mark_as_advanced( - Tbb_INCLUDE_DIR @@ -1227,6 +1223,39 @@ index bdf9c81..ffdee03 100644 - foreach(COMPONENT ${TBB_FIND_COMPONENTS}) - if(NOT ${COMPONENT} IN_LIST _TBB_COMPONENT_LIST) - list(APPEND _IGNORED_COMPONENTS ${COMPONENT}) +- endif() +- endforeach() ++unset(TBB_FOUND CACHE) ++unset(TBB_INCLUDE_DIRS CACHE) ++unset(TBB_LIBRARIES) ++unset(TBB_LIBRARIES_DEBUG) ++unset(TBB_LIBRARIES_RELEASE) + +- if(_IGNORED_COMPONENTS) +- message(STATUS "Ignoring unknown components of TBB:") +- foreach(COMPONENT ${_IGNORED_COMPONENTS}) +- message(STATUS " ${COMPONENT}") +- endforeach() +- list(REMOVE_ITEM TBB_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) +- endif() +-else() +- set(_TBB_COMPONENTS_PROVIDED FALSE) +- set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST}) +-endif() ++include(FindPackageHandleStandardArgs) ++ ++find_package(Threads QUIET REQUIRED) + +-# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var) +-set(_TBB_ROOT_SEARCH_DIR "") ++if(NOT TBB_FOUND) + +-if(TBB_ROOT) +- list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT}) +-else() +- set(_ENV_TBB_ROOT $ENV{TBB_ROOT}) +- if(_ENV_TBB_ROOT) +- list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT}) + ################################## + # Check the build type + ################################## @@ -1241,7 +1270,8 @@ index bdf9c81..ffdee03 100644 + set(TBB_BUILD_TYPE DEBUG) + else() + set(TBB_BUILD_TYPE RELEASE) -+ endif() + endif() +-endif() + + ################################## + # Set the TBB search directories @@ -1261,7 +1291,53 @@ index bdf9c81..ffdee03 100644 + else() + set(TBB_ARCHITECTURE "ia32") + endif() -+ + +-# Additionally try and use pkconfig to find Tbb +- +-find_package(PkgConfig) +-pkg_check_modules(PC_Tbb QUIET tbb) +- +-# ------------------------------------------------------------------------ +-# Search for tbb include DIR +-# ------------------------------------------------------------------------ +- +-set(_TBB_INCLUDE_SEARCH_DIRS "") +-list(APPEND _TBB_INCLUDE_SEARCH_DIRS +- ${TBB_INCLUDEDIR} +- ${_TBB_ROOT_SEARCH_DIR} +- ${PC_Tbb_INCLUDE_DIRS} +- ${SYSTEM_LIBRARY_PATHS} +-) +- +-# Look for a standard tbb header file. +-find_path(Tbb_INCLUDE_DIR tbb/tbb_stddef.h +- NO_DEFAULT_PATH +- PATHS ${_TBB_INCLUDE_SEARCH_DIRS} +- PATH_SUFFIXES include +-) +- +-if(EXISTS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h") +- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" +- _tbb_version_major_string REGEX "#define TBB_VERSION_MAJOR " +- ) +- string(REGEX REPLACE "#define TBB_VERSION_MAJOR" "" +- _tbb_version_major_string "${_tbb_version_major_string}" +- ) +- string(STRIP "${_tbb_version_major_string}" Tbb_VERSION_MAJOR) +- +- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" +- _tbb_version_minor_string REGEX "#define TBB_VERSION_MINOR " +- ) +- string(REGEX REPLACE "#define TBB_VERSION_MINOR" "" +- _tbb_version_minor_string "${_tbb_version_minor_string}" +- ) +- string(STRIP "${_tbb_version_minor_string}" Tbb_VERSION_MINOR) +- +- unset(_tbb_version_major_string) +- unset(_tbb_version_minor_string) +- +- set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR}) +-endif() + # Set the TBB search library path search suffix based on the version of VC + if(WINDOWS_STORE) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") @@ -1273,15 +1349,11 @@ index bdf9c81..ffdee03 100644 + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") + elseif(MSVC10) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") - endif() -- endforeach() ++ endif() -- if(_IGNORED_COMPONENTS) -- message(STATUS "Ignoring unknown components of TBB:") -- foreach(COMPONENT ${_IGNORED_COMPONENTS}) -- message(STATUS " ${COMPONENT}") -- endforeach() -- list(REMOVE_ITEM TBB_FIND_COMPONENTS ${_IGNORED_COMPONENTS}) +-# ------------------------------------------------------------------------ +-# Search for TBB lib DIR +-# ------------------------------------------------------------------------ + # Add the library path search suffix for the VC independent version of TBB + list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") + @@ -1332,110 +1404,19 @@ index bdf9c81..ffdee03 100644 + string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" + TBB_INTERFACE_VERSION "${_tbb_version_file}") + set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") - endif() --else() -- set(_TBB_COMPONENTS_PROVIDED FALSE) -- set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST}) --endif() ++ endif() --# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var) --set(_TBB_ROOT_SEARCH_DIR "") +-set(_TBB_LIBRARYDIR_SEARCH_DIRS "") + ################################## + # Find TBB components + ################################## --if(TBB_ROOT) -- list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT}) --else() -- set(_ENV_TBB_ROOT $ENV{TBB_ROOT}) -- if(_ENV_TBB_ROOT) -- list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT}) +-# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order + if(TBB_VERSION VERSION_LESS 4.3) + set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) + else() + set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) - endif() --endif() - --# Additionally try and use pkconfig to find Tbb -- --find_package(PkgConfig) --pkg_check_modules(PC_Tbb QUIET tbb) -- --# ------------------------------------------------------------------------ --# Search for tbb include DIR --# ------------------------------------------------------------------------ -- --set(_TBB_INCLUDE_SEARCH_DIRS "") --list(APPEND _TBB_INCLUDE_SEARCH_DIRS -- ${TBB_INCLUDEDIR} -- ${_TBB_ROOT_SEARCH_DIR} -- ${PC_Tbb_INCLUDE_DIRS} -- ${SYSTEM_LIBRARY_PATHS} --) -- --# Look for a standard tbb header file. --find_path(Tbb_INCLUDE_DIR tbb/tbb_stddef.h -- NO_DEFAULT_PATH -- PATHS ${_TBB_INCLUDE_SEARCH_DIRS} -- PATH_SUFFIXES include --) -- --if(EXISTS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h") -- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" -- _tbb_version_major_string REGEX "#define TBB_VERSION_MAJOR " -- ) -- string(REGEX REPLACE "#define TBB_VERSION_MAJOR" "" -- _tbb_version_major_string "${_tbb_version_major_string}" -- ) -- string(STRIP "${_tbb_version_major_string}" Tbb_VERSION_MAJOR) -- -- file(STRINGS "${Tbb_INCLUDE_DIR}/tbb/tbb_stddef.h" -- _tbb_version_minor_string REGEX "#define TBB_VERSION_MINOR " -- ) -- string(REGEX REPLACE "#define TBB_VERSION_MINOR" "" -- _tbb_version_minor_string "${_tbb_version_minor_string}" -- ) -- string(STRIP "${_tbb_version_minor_string}" Tbb_VERSION_MINOR) -- -- unset(_tbb_version_major_string) -- unset(_tbb_version_minor_string) -- -- set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR}) --endif() -+ if(TBB_STATIC) -+ set(TBB_STATIC_SUFFIX "_static") + endif() -+ -+ # Find each component -+ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) -+ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") - --# ------------------------------------------------------------------------ --# Search for TBB lib DIR --# ------------------------------------------------------------------------ -+ # Search for the libraries -+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} -+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} -+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH -+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - --set(_TBB_LIBRARYDIR_SEARCH_DIRS "") -+ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug -+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} -+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH -+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - --# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order -+ if(TBB_${_comp}_LIBRARY_DEBUG) -+ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") -+ endif() -+ if(TBB_${_comp}_LIBRARY_RELEASE) -+ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") -+ endif() -+ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) -+ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") -+ endif() -set(_TBB_LIBRARYDIR_SEARCH_DIRS "") -list(APPEND _TBB_LIBRARYDIR_SEARCH_DIRS @@ -1444,41 +1425,35 @@ index bdf9c81..ffdee03 100644 - ${PC_Tbb_LIBRARY_DIRS} - ${SYSTEM_LIBRARY_PATHS} -) -+ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") -+ set(TBB_${_comp}_FOUND TRUE) -+ else() -+ set(TBB_${_comp}_FOUND FALSE) -+ endif() ++ if(TBB_STATIC) ++ set(TBB_STATIC_SUFFIX "_static") ++ endif() -set(TBB_PATH_SUFFIXES - lib64 - lib -) -+ # Mark internal variables as advanced -+ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) -+ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) -+ mark_as_advanced(TBB_${_comp}_LIBRARY) ++ # Find each component ++ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) ++ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") -# platform branching -+ endif() -+ endforeach() ++ unset(TBB_${_comp}_LIBRARY_DEBUG CACHE) ++ unset(TBB_${_comp}_LIBRARY_RELEASE CACHE) -if(UNIX) - list(INSERT TBB_PATH_SUFFIXES 0 lib/x86_64-linux-gnu) -endif() -+ ################################## -+ # Set compile flags and libraries -+ ################################## ++ # Search for the libraries ++ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} ++ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH ++ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) -if(APPLE) - if(TBB_FOR_CLANG) - list(INSERT TBB_PATH_SUFFIXES 0 lib/libc++) -+ set(TBB_DEFINITIONS_RELEASE "") -+ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1") -+ -+ if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) -+ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") - endif() +- endif() -elseif(WIN32) - if(MSVC10) - set(TBB_VC_DIR vc10) @@ -1498,45 +1473,26 @@ index bdf9c81..ffdee03 100644 - else() - list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc4.4) - endif() -+ -+ if(NOT MSVC AND NOT TBB_LIBRARIES) -+ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE}) - endif() +- endif() -endif() ++ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug ++ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH ++ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) -if(UNIX AND TBB_USE_STATIC_LIBS) - set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -endif() -+ if (MSVC AND TBB_STATIC) -+ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE) -+ endif () -+ -+ unset (TBB_STATIC_SUFFIX) -+ -+ find_package_handle_standard_args(TBB -+ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES -+ HANDLE_COMPONENTS -+ VERSION_VAR TBB_VERSION) -+ -+ ################################## -+ # Create targets -+ ################################## -+ -+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) -+ add_library(TBB::tbb UNKNOWN IMPORTED) -+ set_target_properties(TBB::tbb PROPERTIES -+ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} -+ IMPORTED_LOCATION ${TBB_LIBRARIES}) -+ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) -+ set_target_properties(TBB::tbb PROPERTIES -+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${TBB_DEFINITIONS_RELEASE}>" -+ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} -+ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE} -+ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} -+ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} -+ ) -+ endif() ++ if(TBB_${_comp}_LIBRARY_DEBUG) ++ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") ++ endif() ++ if(TBB_${_comp}_LIBRARY_RELEASE) ++ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") ++ endif() ++ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) ++ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") ++ endif() -set(Tbb_LIB_COMPONENTS "") - @@ -1559,19 +1515,84 @@ index bdf9c81..ffdee03 100644 - # Extract the directory and apply the matched text (in brackets) - get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY) - set(Tbb_${COMPONENT}_LIBRARY "${Tbb_${COMPONENT}_DIR}/${CMAKE_MATCH_1}") -- endif() -+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") -+ find_package(Threads QUIET REQUIRED) -+ set_target_properties(TBB::tbb PROPERTIES INTERFACE_LINK_LIBRARIES "${CMAKE_DL_LIBS};Threads::Threads") ++ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") ++ set(TBB_${_comp}_FOUND TRUE) ++ else() ++ set(TBB_${_comp}_FOUND FALSE) + endif() ++ ++ # Mark internal variables as advanced ++ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) ++ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) ++ mark_as_advanced(TBB_${_comp}_LIBRARY) ++ endif() - endif() +- endif() ++ endforeach() - list(APPEND Tbb_LIB_COMPONENTS ${Tbb_${COMPONENT}_LIBRARY}) -- ++ ################################## ++ # Set compile flags and libraries ++ ################################## + - if(Tbb_${COMPONENT}_LIBRARY) - set(TBB_${COMPONENT}_FOUND TRUE) - else() - set(TBB_${COMPONENT}_FOUND FALSE) ++ set(TBB_DEFINITIONS_RELEASE "") ++ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1") ++ ++ if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) ++ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") ++ endif() ++ ++ if(NOT MSVC AND NOT TBB_LIBRARIES) ++ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE}) + endif() +-endforeach() + +-if(UNIX AND TBB_USE_STATIC_LIBS) +- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) +- unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) +-endif() ++ set(TBB_DEFINITIONS "") ++ if (MSVC AND TBB_STATIC) ++ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE) ++ endif () ++ ++ unset (TBB_STATIC_SUFFIX) ++ ++ find_package_handle_standard_args(TBB ++ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES ++ FAIL_MESSAGE "TBB library cannot be found. Consider set TBBROOT environment variable." ++ HANDLE_COMPONENTS ++ VERSION_VAR TBB_VERSION) ++ ++ ################################## ++ # Create targets ++ ################################## ++ ++ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) ++ add_library(TBB::tbb UNKNOWN IMPORTED) ++ set_target_properties(TBB::tbb PROPERTIES ++ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}" ++ INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}" ++ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} ++ IMPORTED_LOCATION ${TBB_LIBRARIES}) ++ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) ++ set_target_properties(TBB::tbb PROPERTIES ++ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${TBB_DEFINITIONS_RELEASE}>" ++ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} ++ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE} ++ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} ++ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} ++ ) ++ endif() ++ endif() + +-# ------------------------------------------------------------------------ +-# Cache and set TBB_FOUND +-# ------------------------------------------------------------------------ + mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) + + unset(TBB_ARCHITECTURE) @@ -1588,18 +1609,8 @@ index bdf9c81..ffdee03 100644 + message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") + message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") + message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") - endif() --endforeach() ++ endif() --if(UNIX AND TBB_USE_STATIC_LIBS) -- set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) -- unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) --endif() -- --# ------------------------------------------------------------------------ --# Cache and set TBB_FOUND --# ------------------------------------------------------------------------ -- -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(TBB - FOUND_VAR TBB_FOUND From 06b2f8c210a92e78a4b51d5548d83de0a056464e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 15:37:08 +0100 Subject: [PATCH 009/130] Omit blosc packing in deps build --- deps/deps-windows.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index fcb300062c..fa2a78b037 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -318,6 +318,7 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON + -DBLOSC_IS_SUBPROJECT=OFF PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" From af89655437a1e5976d00b7897ef5cf8d8c6d8ae0 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 16:21:01 +0100 Subject: [PATCH 010/130] Try to fix patching by reverting to git repos in download steps Patching does not remove or rename files. --- deps/deps-windows.cmake | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index fa2a78b037..4f8bbc1ab5 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -303,8 +303,10 @@ endif () ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 - URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip - URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 + #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip + #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 + GIT_REPOSITORY https://github.com/Blosc/c-blosc.git + GIT_TAG v1.17.0 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" @@ -318,7 +320,8 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - -DBLOSC_IS_SUBPROJECT=OFF + -DBLOSC_IS_SUBPROJECT:BOOL=ON + -DBLOSC_INSTALL:BOOL=ON PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" @@ -349,8 +352,10 @@ add_debug_dep(dep_openexr) ExternalProject_Add(dep_openvdb EXCLUDE_FROM_ALL 1 - URL https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.2.1.zip - URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 + #URL https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.2.1.zip + #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 + GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git + GIT_TAG v6.2.1 DEPENDS dep_blosc dep_openexr dep_tbb dep_boost CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" From 64a5696539cee8bdcf5880a7258cfea3f8b40359 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 17:19:43 +0100 Subject: [PATCH 011/130] Re-enable example test --- tests/CMakeLists.txt | 2 +- tests/example/example_tests_main.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9453fa3143..0d2266dc56 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,4 +28,4 @@ add_subdirectory(timeutils) add_subdirectory(fff_print) add_subdirectory(sla_print) add_subdirectory(hollowing) -#add_subdirectory(example) +add_subdirectory(example) diff --git a/tests/example/example_tests_main.cpp b/tests/example/example_tests_main.cpp index 32e8d02b76..426d1ffef7 100644 --- a/tests/example/example_tests_main.cpp +++ b/tests/example/example_tests_main.cpp @@ -1,4 +1,3 @@ -#define CATCH_CONFIG_MAIN #include TEST_CASE("Is example succesful", "[example]") { From ce9c2c5dd4839318640b24141077511587d98471 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Oct 2019 18:08:16 +0100 Subject: [PATCH 012/130] Add explicit ZLIB system package requirement on Unix systems. We already depended on that but is wasn't stated in the deps script. --- deps/deps-unix-common.cmake | 4 +++- deps/deps-windows.cmake | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index 0df535fc31..c6caab987c 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -7,6 +7,8 @@ else () set(TBB_MINGW_WORKAROUND "") endif () +find_package(ZLIB REQUIRED) + ExternalProject_Add(dep_tbb EXCLUDE_FROM_ALL 1 URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" @@ -105,7 +107,7 @@ ExternalProject_Add(dep_blosc -DBUILD_STATIC=ON -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF - -DPREFER_EXTERNAL_ZLIB=OFF + -DPREFER_EXTERNAL_ZLIB=ON PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 4f8bbc1ab5..3f722ea992 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -333,7 +333,7 @@ ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git GIT_TAG v2.4.0 - DEPENDS + DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS From 2165537fa59c2ab600e0a0d8fa03eed672c1e4a7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 31 Oct 2019 14:36:33 +0100 Subject: [PATCH 013/130] Do some hollowing finally. --- src/libslic3r/SLA/SLABoilerPlate.hpp | 70 ++++++++++++---- src/libslic3r/SLA/SLAPad.cpp | 4 +- src/libslic3r/SLA/SLASupportTreeBuilder.cpp | 18 ++-- tests/hollowing/CMakeLists.txt | 2 +- tests/hollowing/hollowing_test_main.cpp | 1 + tests/hollowing/hollowing_tests.cpp | 51 ++++------- tests/hollowing/openvdb_utils.cpp | 93 +++++++++++++++++++++ tests/hollowing/openvdb_utils.hpp | 25 ++++++ 8 files changed, 202 insertions(+), 62 deletions(-) create mode 100644 tests/hollowing/hollowing_test_main.cpp create mode 100644 tests/hollowing/openvdb_utils.cpp create mode 100644 tests/hollowing/openvdb_utils.hpp diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index d7ce26bb2b..1bb1943ef1 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -12,25 +12,35 @@ #include "SLASpatIndex.hpp" namespace Slic3r { + +typedef Eigen::Matrix Vec4i; + namespace sla { /// Intermediate struct for a 3D mesh struct Contour3D { Pointf3s points; - std::vector indices; + std::vector faces3; + std::vector faces4; Contour3D& merge(const Contour3D& ctr) { - auto s3 = coord_t(points.size()); - auto s = indices.size(); + auto N = coord_t(points.size()); + auto N_f3 = faces3.size(); + auto N_f4 = faces4.size(); points.insert(points.end(), ctr.points.begin(), ctr.points.end()); - indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); + faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end()); + faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end()); - for(size_t n = s; n < indices.size(); n++) { - auto& idx = indices[n]; idx.x() += s3; idx.y() += s3; idx.z() += s3; + for(size_t n = N_f3; n < faces3.size(); n++) { + auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N; } + for(size_t n = N_f4; n < faces4.size(); n++) { + auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N; + } + return *this; } @@ -38,10 +48,10 @@ struct Contour3D { { const size_t offs = points.size(); points.insert(points.end(), triangles.begin(), triangles.end()); - indices.reserve(indices.size() + points.size() / 3); + faces3.reserve(faces3.size() + points.size() / 3); for(int i = int(offs); i < int(points.size()); i += 3) - indices.emplace_back(i, i + 1, i + 2); + faces3.emplace_back(i, i + 1, i + 2); return *this; } @@ -53,10 +63,16 @@ struct Contour3D { stream << "v " << p.transpose() << "\n"; } - for(auto& f : indices) { + for(auto& f : faces3) { stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; } + + for(auto& f : faces4) { + stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n"; + } } + + bool empty() const { return points.empty() || (faces4.empty() && faces3.empty()); } }; using ClusterEl = std::vector; @@ -82,19 +98,45 @@ ClusteredPoints cluster( // Calculate the normals for the selected points (from 'points' set) on the // mesh. This will call squared distance for each point. PointSet normals(const PointSet& points, - const EigenMesh3D& mesh, + const EigenMesh3D& convert_mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}, const std::vector& selected_points = {}); /// Mesh from an existing contour. -inline TriangleMesh mesh(const Contour3D& ctour) { - return {ctour.points, ctour.indices}; +inline TriangleMesh convert_mesh(const Contour3D& ctour) { + return {ctour.points, ctour.faces3}; } /// Mesh from an evaporating 3D contour -inline TriangleMesh mesh(Contour3D&& ctour) { - return {std::move(ctour.points), std::move(ctour.indices)}; +inline TriangleMesh convert_mesh(Contour3D&& ctour) { + return {std::move(ctour.points), std::move(ctour.faces3)}; +} + +inline Contour3D convert_mesh(const TriangleMesh &trmesh) { + Contour3D ret; + ret.points.reserve(trmesh.its.vertices.size()); + ret.faces3.reserve(trmesh.its.indices.size()); + + for (auto &v : trmesh.its.vertices) + ret.points.emplace_back(v.cast()); + + std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(), + std::back_inserter(ret.faces3)); + + return ret; +} + +inline Contour3D convert_mesh(TriangleMesh &&trmesh) { + Contour3D ret; + ret.points.reserve(trmesh.its.vertices.size()); + + for (auto &v : trmesh.its.vertices) + ret.points.emplace_back(v.cast()); + + ret.faces3.swap(trmesh.its.indices); + + return ret; } } diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp index 7cd9eb4e42..d0e802d842 100644 --- a/src/libslic3r/SLA/SLAPad.cpp +++ b/src/libslic3r/SLA/SLAPad.cpp @@ -69,7 +69,7 @@ Contour3D walls( // Shorthand for the vertex arrays auto& upts = upper.points, &lpts = lower.points; - auto& rpts = ret.points; auto& ind = ret.indices; + auto& rpts = ret.points; auto& ind = ret.faces3; // If the Z levels are flipped, or the offset difference is negative, we // will interpret that as the triangles normals should be inverted. @@ -677,7 +677,7 @@ void create_pad(const ExPolygons &sup_blueprint, ThrowOnCancel thr) { Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); - out.merge(mesh(std::move(t))); + out.merge(convert_mesh(std::move(t))); } std::string PadConfig::validate() const diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp index 2e0310ed8d..0c276738a2 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp @@ -12,7 +12,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { if(rho <= 1e-6 && rho >= -1e-6) return ret; auto& vertices = ret.points; - auto& facets = ret.indices; + auto& facets = ret.faces3; // Algorithm: // Add points one-by-one to the sphere grid and form facets using relative @@ -102,7 +102,7 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) auto steps = int(ssteps); auto& points = ret.points; - auto& indices = ret.indices; + auto& indices = ret.faces3; points.reserve(2*ssteps); double a = 2*PI/steps; @@ -211,8 +211,8 @@ Head::Head(double r_big_mm, coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; - mesh.indices.emplace_back(i1s1, i2s1, i2s2); - mesh.indices.emplace_back(i1s1, i2s2, i1s2); + mesh.faces3.emplace_back(i1s1, i2s1, i2s2); + mesh.faces3.emplace_back(i1s1, i2s2, i1s2); } auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); @@ -220,8 +220,8 @@ Head::Head(double r_big_mm, auto i1s2 = coord_t(s1.points.size()); auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; - mesh.indices.emplace_back(i2s2, i2s1, i1s1); - mesh.indices.emplace_back(i1s2, i2s2, i1s1); + mesh.faces3.emplace_back(i2s2, i2s1, i1s1); + mesh.faces3.emplace_back(i1s2, i2s2, i1s1); // To simplify further processing, we translate the mesh so that the // last vertex of the pointing sphere (the pinpoint) will be at (0,0,0) @@ -240,7 +240,7 @@ Pillar::Pillar(const Vec3d &jp, const Vec3d &endp, double radius, size_t st): // move the data. Contour3D body = cylinder(radius, height, st, endp); mesh.points.swap(body.points); - mesh.indices.swap(body.indices); + mesh.faces3.swap(body.faces3); } } @@ -275,7 +275,7 @@ Pillar &Pillar::add_base(double baseheight, double radius) base.points.emplace_back(endpt); base.points.emplace_back(ep); - auto& indices = base.indices; + auto& indices = base.faces3; auto hcenter = int(base.points.size() - 1); auto lcenter = int(base.points.size() - 2); auto offs = int(steps); @@ -466,7 +466,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const return m_meshcache; } - m_meshcache = mesh(merged); + m_meshcache = convert_mesh(merged); // The mesh will be passed by const-pointer to TriangleMeshSlicer, // which will need this. diff --git a/tests/hollowing/CMakeLists.txt b/tests/hollowing/CMakeLists.txt index 79c12baef8..9679a1b777 100644 --- a/tests/hollowing/CMakeLists.txt +++ b/tests/hollowing/CMakeLists.txt @@ -1,5 +1,5 @@ if(TARGET OpenVDB::openvdb) - add_executable(hollowing_tests hollowing_tests.cpp) + add_executable(hollowing_tests hollowing_test_main.cpp hollowing_tests.cpp openvdb_utils.cpp openvdb_utils.hpp) #find_package(GTest REQUIRED) #target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) diff --git a/tests/hollowing/hollowing_test_main.cpp b/tests/hollowing/hollowing_test_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/hollowing/hollowing_test_main.cpp @@ -0,0 +1 @@ +#include diff --git a/tests/hollowing/hollowing_tests.cpp b/tests/hollowing/hollowing_tests.cpp index 846f7e831b..a7b2874eb0 100644 --- a/tests/hollowing/hollowing_tests.cpp +++ b/tests/hollowing/hollowing_tests.cpp @@ -1,10 +1,8 @@ #include -#include +#include +#include -#include -#include -#include -#include +#include "openvdb_utils.hpp" #include "libslic3r/Format/OBJ.hpp" #if defined(WIN32) || defined(_WIN32) @@ -13,22 +11,6 @@ #define PATH_SEPARATOR R"(/)" #endif -class TriangleMeshDataAdapter { -public: - Slic3r::TriangleMesh mesh; - - size_t polygonCount() const { return mesh.its.indices.size(); } - size_t pointCount() const { return mesh.its.vertices.size(); } - size_t vertexCount(size_t) const { return 3; } - - // Return position pos in local grid index space for polygon n and vertex v - void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const { - auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); - Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); - pos = {double(p.x()), double(p.y()), p.z()}; - } -}; - static Slic3r::TriangleMesh load_model(const std::string &obj_filename) { Slic3r::TriangleMesh mesh; @@ -38,24 +20,21 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) } TEST_CASE("Load object", "[Hollowing]") { - TriangleMeshDataAdapter mesh{load_model("20mm_cube.obj")}; - auto ptr = openvdb::tools::meshToVolume(mesh, {}); + Slic3r::TriangleMesh mesh = load_model("20mm_cube.obj"); + + Slic3r::sla::Contour3D imesh = Slic3r::sla::convert_mesh(mesh); + auto ptr = Slic3r::meshToVolume(imesh, {}); REQUIRE(ptr); - std::vector points; - std::vector quad_indices; - std::vector triangle_indices; + Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -1., 0.0, true); - openvdb::tools::volumeToMesh(*ptr, points, triangle_indices, quad_indices, 0.0, 1.0, true); + REQUIRE(!omesh.empty()); - std::cout << "Triangle count: " << triangle_indices.size() << std::endl; - std::cout << "Quad count: " << quad_indices.size() << std::endl; - std::cout << "Point count: " << points.size() << " vs " << mesh.mesh.its.vertices.size() << std::endl; + std::fstream outfile{"out.obj", std::ios::out}; + omesh.to_obj(outfile); + + imesh.merge(omesh); + std::fstream merged_outfile("merged_out.obj", std::ios::out); + imesh.to_obj(merged_outfile); } - -//int main(int argc, char **argv) -//{ -// ::testing::InitGoogleTest(&argc, argv); -// return RUN_ALL_TESTS(); -//} diff --git a/tests/hollowing/openvdb_utils.cpp b/tests/hollowing/openvdb_utils.cpp new file mode 100644 index 0000000000..38e96a885d --- /dev/null +++ b/tests/hollowing/openvdb_utils.cpp @@ -0,0 +1,93 @@ +#include "openvdb_utils.hpp" + +namespace Slic3r { + +class TriangleMeshDataAdapter { +public: + const TriangleMesh &mesh; + + size_t polygonCount() const { return mesh.its.indices.size(); } + size_t pointCount() const { return mesh.its.vertices.size(); } + size_t vertexCount(size_t) const { return 3; } + + // Return position pos in local grid index space for polygon n and vertex v + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +}; + +class Contour3DDataAdapter { +public: + const sla::Contour3D &mesh; + + size_t polygonCount() const { return mesh.faces3.size() + mesh.faces4.size(); } + size_t pointCount() const { return mesh.points.size(); } + size_t vertexCount(size_t n) const { return n < mesh.faces3.size() ? 3 : 4; } + + // Return position pos in local grid index space for polygon n and vertex v + void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +}; + +void TriangleMeshDataAdapter::getIndexSpacePoint(size_t n, + size_t v, + openvdb::Vec3d &pos) const +{ + auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); + Slic3r::Vec3d p = mesh.its.vertices[vidx].cast(); + pos = {p.x(), p.y(), p.z()}; +} + +void Contour3DDataAdapter::getIndexSpacePoint(size_t n, + size_t v, + openvdb::Vec3d &pos) const +{ + size_t vidx = 0; + if (n < mesh.faces3.size()) vidx = size_t(mesh.faces3[n](Eigen::Index(v))); + else vidx = size_t(mesh.faces4[n - mesh.faces3.size()](Eigen::Index(v))); + + Slic3r::Vec3d p = mesh.points[vidx]; + pos = {p.x(), p.y(), p.z()}; +} + +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, + const openvdb::math::Transform &tr) +{ + return openvdb::tools::meshToVolume( + TriangleMeshDataAdapter{mesh}, tr); +} + +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr) +{ + return openvdb::tools::meshToVolume( + Contour3DDataAdapter{mesh}, tr); +} + +inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } +inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } +inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } +inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } + +sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + std::vector points; + std::vector triangles; + std::vector quads; + + openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue, + adaptivity, relaxDisorientedTriangles); + + sla::Contour3D ret; + ret.points.reserve(points.size()); + ret.faces3.reserve(triangles.size()); + ret.faces4.reserve(quads.size()); + + for (auto &v : points) ret.points.emplace_back(to_vec3d(v)); + for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); + for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); + + return ret; +} + +} // namespace Slic3r diff --git a/tests/hollowing/openvdb_utils.hpp b/tests/hollowing/openvdb_utils.hpp new file mode 100644 index 0000000000..a2c02c0783 --- /dev/null +++ b/tests/hollowing/openvdb_utils.hpp @@ -0,0 +1,25 @@ +#ifndef OPENVDB_UTILS_HPP +#define OPENVDB_UTILS_HPP + +#include +#include +#include +#include +#include + +namespace Slic3r { + +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, + const openvdb::math::Transform &tr); + +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr); + +sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue = 0.0, + double adaptivity = 0.0, + bool relaxDisorientedTriangles = true); + +} // namespace Slic3r + +#endif // OPENVDB_UTILS_HPP From 62983850c549215be667e7e7072008bf9291b66f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 1 Nov 2019 10:56:39 +0100 Subject: [PATCH 014/130] Remove git update step from deps where patch is applied. --- deps/deps-unix-common.cmake | 45 +++---------- deps/deps-windows.cmake | 49 +++----------- deps/igl-mods.patch | 128 ------------------------------------ 3 files changed, 19 insertions(+), 203 deletions(-) delete mode 100644 deps/igl-mods.patch diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index c6caab987c..eae319efc6 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -61,42 +61,14 @@ ExternalProject_Add(dep_qhull -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 + UPDATE_COMMAND "" + PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch ) -ExternalProject_Add(dep_libigl - EXCLUDE_FROM_ALL 1 - URL "https://github.com/libigl/libigl/archive/v2.0.0.tar.gz" - URL_HASH SHA256=42518e6b106c7209c73435fd260ed5d34edeb254852495b4c95dce2d95401328 - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - -DLIBIGL_BUILD_PYTHON=OFF - -DLIBIGL_BUILD_TESTS=OFF - -DLIBIGL_BUILD_TUTORIALS=OFF - -DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC} - -DLIBIGL_WITHOUT_COPYLEFT=OFF - -DLIBIGL_WITH_CGAL=OFF - -DLIBIGL_WITH_COMISO=OFF - -DLIBIGL_WITH_CORK=OFF - -DLIBIGL_WITH_EMBREE=OFF - -DLIBIGL_WITH_MATLAB=OFF - -DLIBIGL_WITH_MOSEK=OFF - -DLIBIGL_WITH_OPENGL=OFF - -DLIBIGL_WITH_OPENGL_GLFW=OFF - -DLIBIGL_WITH_OPENGL_GLFW_IMGUI=OFF - -DLIBIGL_WITH_PNG=OFF - -DLIBIGL_WITH_PYTHON=OFF - -DLIBIGL_WITH_TETGEN=OFF - -DLIBIGL_WITH_TRIANGLE=OFF - -DLIBIGL_WITH_XML=OFF - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/igl-mods.patch -) - - ExternalProject_Add(dep_blosc EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git - GIT_TAG v1.17.0 + GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 DEPENDS CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local @@ -108,13 +80,14 @@ ExternalProject_Add(dep_blosc -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DPREFER_EXTERNAL_ZLIB=ON - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch + UPDATE_COMMAND "" + PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch ) ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git - GIT_TAG v2.4.0 + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DBUILD_SHARED_LIBS=OFF @@ -123,12 +96,13 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF + UPDATE_COMMAND "" ) ExternalProject_Add(dep_openvdb EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git - GIT_TAG v6.2.1 + GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 DEPENDS dep_blosc dep_openexr dep_tbb CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local @@ -142,5 +116,6 @@ ExternalProject_Add(dep_openvdb -DOPENVDB_CORE_STATIC=ON -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON - PATCH_COMMAND ${GIT_EXECUTABLE} apply ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch + UPDATE_COMMAND "" + PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch ) \ No newline at end of file diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index 3f722ea992..514a90a9ec 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -226,7 +226,8 @@ ExternalProject_Add(dep_qhull -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 + UPDATE_COMMAND "" + PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -243,41 +244,6 @@ endif () find_package(Git REQUIRED) -ExternalProject_Add(dep_libigl - EXCLUDE_FROM_ALL 1 - URL "https://github.com/libigl/libigl/archive/v2.0.0.tar.gz" - URL_HASH SHA256=42518e6b106c7209c73435fd260ed5d34edeb254852495b4c95dce2d95401328 - CMAKE_GENERATOR "${DEP_MSVC_GEN}" - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local - -DLIBIGL_BUILD_PYTHON=OFF - -DLIBIGL_BUILD_TESTS=OFF - -DLIBIGL_BUILD_TUTORIALS=OFF - -DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC} - -DLIBIGL_WITHOUT_COPYLEFT=OFF - -DLIBIGL_WITH_CGAL=OFF - -DLIBIGL_WITH_COMISO=OFF - -DLIBIGL_WITH_CORK=OFF - -DLIBIGL_WITH_EMBREE=OFF - -DLIBIGL_WITH_MATLAB=OFF - -DLIBIGL_WITH_MOSEK=OFF - -DLIBIGL_WITH_OPENGL=OFF - -DLIBIGL_WITH_OPENGL_GLFW=OFF - -DLIBIGL_WITH_OPENGL_GLFW_IMGUI=OFF - -DLIBIGL_WITH_PNG=OFF - -DLIBIGL_WITH_PYTHON=OFF - -DLIBIGL_WITH_TETGEN=OFF - -DLIBIGL_WITH_TRIANGLE=OFF - -DLIBIGL_WITH_XML=OFF - -DCMAKE_POSITION_INDEPENDENT_CODE=ON - -DCMAKE_DEBUG_POSTFIX=d - PATCH_COMMAND ${GIT_EXECUTABLE} apply --ignore-space-change --ignore-whitespace ${CMAKE_CURRENT_SOURCE_DIR}/igl-mods.patch - BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj - INSTALL_COMMAND "" -) - -add_debug_dep(dep_libigl) - ExternalProject_Add(dep_wxwidgets EXCLUDE_FROM_ALL 1 GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets" @@ -306,7 +272,7 @@ ExternalProject_Add(dep_blosc #URL https://github.com/Blosc/c-blosc/archive/v1.17.0.zip #URL_HASH SHA256=7463a1df566704f212263312717ab2c36b45d45cba6cd0dccebf91b2cc4b4da9 GIT_REPOSITORY https://github.com/Blosc/c-blosc.git - GIT_TAG v1.17.0 + GIT_TAG e63775855294b50820ef44d1b157f4de1cc38d3e #v1.17.0 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" @@ -322,6 +288,7 @@ ExternalProject_Add(dep_blosc -DPREFER_EXTERNAL_ZLIB=ON -DBLOSC_IS_SUBPROJECT:BOOL=ON -DBLOSC_INSTALL:BOOL=ON + UPDATE_COMMAND "" PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" @@ -332,7 +299,7 @@ add_debug_dep(dep_blosc) ExternalProject_Add(dep_openexr EXCLUDE_FROM_ALL 1 GIT_REPOSITORY https://github.com/openexr/openexr.git - GIT_TAG v2.4.0 + GIT_TAG eae0e337c9f5117e78114fd05f7a415819df413a #v2.4.0 DEPENDS dep_zlib CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" @@ -344,6 +311,7 @@ ExternalProject_Add(dep_openexr -DPYILMBASE_ENABLE:BOOL=OFF -DOPENEXR_VIEWERS_ENABLE:BOOL=OFF -DOPENEXR_BUILD_UTILS:BOOL=OFF + UPDATE_COMMAND "" BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj INSTALL_COMMAND "" ) @@ -355,8 +323,8 @@ ExternalProject_Add(dep_openvdb #URL https://github.com/AcademySoftwareFoundation/openvdb/archive/v6.2.1.zip #URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48 GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git - GIT_TAG v6.2.1 - DEPENDS dep_blosc dep_openexr dep_tbb dep_boost + GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1 + DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" CMAKE_ARGS @@ -372,6 +340,7 @@ ExternalProject_Add(dep_openvdb -DTBB_STATIC=ON -DOPENVDB_BUILD_VDB_PRINT=ON BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + UPDATE_COMMAND "" PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch INSTALL_COMMAND "" ) diff --git a/deps/igl-mods.patch b/deps/igl-mods.patch deleted file mode 100644 index b0ff9205d0..0000000000 --- a/deps/igl-mods.patch +++ /dev/null @@ -1,128 +0,0 @@ -diff --git a/cmake/libigl-config.cmake.in b/cmake/libigl-config.cmake.in -index 317c745c..f9808e1e 100644 ---- a/cmake/libigl-config.cmake.in -+++ b/cmake/libigl-config.cmake.in -@@ -2,28 +2,28 @@ - - include(${CMAKE_CURRENT_LIST_DIR}/libigl-export.cmake) - --if (TARGET igl::core) -- if (NOT TARGET Eigen3::Eigen) -- find_package(Eigen3 QUIET) -- if (NOT Eigen3_FOUND) -- # try with PkgCOnfig -- find_package(PkgConfig REQUIRED) -- pkg_check_modules(Eigen3 QUIET IMPORTED_TARGET eigen3) -- endif() -- -- if (NOT Eigen3_FOUND) -- message(FATAL_ERROR "Could not find required dependency Eigen3") -- set(libigl_core_FOUND FALSE) -- else() -- target_link_libraries(igl::core INTERFACE PkgConfig::Eigen3) -- set(libigl_core_FOUND TRUE) -- endif() -- else() -- target_link_libraries(igl::core INTERFACE Eigen3::Eigen) -- set(libigl_core_FOUND TRUE) -- endif() -- --endif() -+# if (TARGET igl::core) -+# if (NOT TARGET Eigen3::Eigen) -+# find_package(Eigen3 QUIET) -+# if (NOT Eigen3_FOUND) -+# # try with PkgCOnfig -+# find_package(PkgConfig REQUIRED) -+# pkg_check_modules(Eigen3 QUIET IMPORTED_TARGET eigen3) -+# endif() -+# -+# if (NOT Eigen3_FOUND) -+# message(FATAL_ERROR "Could not find required dependency Eigen3") -+# set(libigl_core_FOUND FALSE) -+# else() -+# target_link_libraries(igl::core INTERFACE PkgConfig::Eigen3) -+# set(libigl_core_FOUND TRUE) -+# endif() -+# else() -+# target_link_libraries(igl::core INTERFACE Eigen3::Eigen) -+# set(libigl_core_FOUND TRUE) -+# endif() -+# -+# endif() - - check_required_components(libigl) - -diff --git a/cmake/libigl.cmake b/cmake/libigl.cmake -index 4b11007a..47e6c395 100644 ---- a/cmake/libigl.cmake -+++ b/cmake/libigl.cmake -@@ -445,6 +445,7 @@ function(install_dir_files dir_name) - if(NOT LIBIGL_USE_STATIC_LIBRARY) - file(GLOB public_sources - ${CMAKE_CURRENT_SOURCE_DIR}/include/igl${subpath}/*.cpp -+ ${CMAKE_CURRENT_SOURCE_DIR}/include/igl${subpath}/*.c - ) - endif() - list(APPEND files_to_install ${public_sources}) -diff --git a/include/igl/AABB.cpp b/include/igl/AABB.cpp -index 09537335..92e90cb7 100644 ---- a/include/igl/AABB.cpp -+++ b/include/igl/AABB.cpp -@@ -1071,5 +1071,11 @@ template void igl::AABB, 3>::init, 2>::init >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&); - template double igl::AABB, 3>::squared_distance >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, double, int&, Eigen::PlainObjectBase >&) const; -+template float igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::squared_distance const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Matrix const&, int&, Eigen::PlainObjectBase >&) const; - template bool igl::AABB, 3>::intersect_ray >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, igl::Hit&) const; -+template bool igl::AABB, 3>::intersect_ray >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix const&, Eigen::Matrix const&, std::vector&) const; -+ -+template void igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::init const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&); -+ -+template bool igl::AABB const, 0, Eigen::Stride<0, 0> >, 3>::intersect_ray const, 0, Eigen::Stride<0, 0> > >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Matrix const&, Eigen::Matrix const&, std::vector >&) const; - #endif -diff --git a/include/igl/barycenter.cpp b/include/igl/barycenter.cpp -index 065f82aa..ec2d96cd 100644 ---- a/include/igl/barycenter.cpp -+++ b/include/igl/barycenter.cpp -@@ -54,4 +54,6 @@ template void igl::barycenter, Eigen::M - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); - template void igl::barycenter, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::PlainObjectBase >&); -+ -+template void igl::barycenter const, 0, Eigen::Stride<0, 0> >, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Matrix >(Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::PlainObjectBase >&); - #endif -diff --git a/include/igl/point_simplex_squared_distance.cpp b/include/igl/point_simplex_squared_distance.cpp -index 2b98bd28..c66d9ae1 100644 ---- a/include/igl/point_simplex_squared_distance.cpp -+++ b/include/igl/point_simplex_squared_distance.cpp -@@ -178,4 +178,6 @@ template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); - template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); - template void igl::point_simplex_squared_distance<2, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix, double, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::Matrix::Index, double&, Eigen::MatrixBase >&, Eigen::PlainObjectBase >&); -+ -+template void igl::point_simplex_squared_distance<3, Eigen::Matrix, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Map const, 0, Eigen::Stride<0, 0> >, float, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::Map const, 0, Eigen::Stride<0, 0> >::Index, float&, Eigen::MatrixBase >&); - #endif -diff --git a/include/igl/ray_box_intersect.cpp b/include/igl/ray_box_intersect.cpp -index 4a88b89e..b547f8f8 100644 ---- a/include/igl/ray_box_intersect.cpp -+++ b/include/igl/ray_box_intersect.cpp -@@ -147,4 +147,6 @@ IGL_INLINE bool igl::ray_box_intersect( - #ifdef IGL_STATIC_LIBRARY - // Explicit template instantiation - template bool igl::ray_box_intersect, Eigen::Matrix, double>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::AlignedBox const&, double const&, double const&, double&, double&); -+ -+template bool igl::ray_box_intersect, Eigen::Matrix, float>(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::AlignedBox const&, float const&, float const&, float&, float&); - #endif -diff --git a/include/igl/ray_mesh_intersect.cpp b/include/igl/ray_mesh_intersect.cpp -index 9a70a22b..4233e722 100644 ---- a/include/igl/ray_mesh_intersect.cpp -+++ b/include/igl/ray_mesh_intersect.cpp -@@ -83,4 +83,7 @@ IGL_INLINE bool igl::ray_mesh_intersect( - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, std::vector >&); - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Matrix >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, igl::Hit&); - template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, igl::Hit&); -+template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Matrix, Eigen::Block const, 1, -1, false> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 1, -1, false> > const&, std::vector >&); -+ -+template bool igl::ray_mesh_intersect, Eigen::Matrix, Eigen::Map const, 0, Eigen::Stride<0, 0> >, Eigen::Block const, 0, Eigen::Stride<0, 0> > const, 1, -1, true> >(Eigen::MatrixBase > const&, Eigen::MatrixBase > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > > const&, Eigen::MatrixBase const, 0, Eigen::Stride<0, 0> > const, 1, -1, true> > const&, std::vector >&); - #endif From 9dafc324f09c3bc6570ed74d6563c4d81908f450 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 1 Nov 2019 12:10:03 +0100 Subject: [PATCH 015/130] Fix compilation on MSVC --- tests/hollowing/openvdb_utils.cpp | 4 ++++ tests/hollowing/openvdb_utils.hpp | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/hollowing/openvdb_utils.cpp b/tests/hollowing/openvdb_utils.cpp index 38e96a885d..62d3a14d70 100644 --- a/tests/hollowing/openvdb_utils.cpp +++ b/tests/hollowing/openvdb_utils.cpp @@ -1,5 +1,9 @@ +#define NOMINMAX #include "openvdb_utils.hpp" +#include +#include + namespace Slic3r { class TriangleMeshDataAdapter { diff --git a/tests/hollowing/openvdb_utils.hpp b/tests/hollowing/openvdb_utils.hpp index a2c02c0783..9854d924da 100644 --- a/tests/hollowing/openvdb_utils.hpp +++ b/tests/hollowing/openvdb_utils.hpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include namespace Slic3r { From a8a5a884f9b5d69cb648ef56c3c62ce5ba787484 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 1 Nov 2019 15:31:26 +0100 Subject: [PATCH 016/130] Add OpenVDBUtils into libslic3r, hollwing tests in libslic3r_test --- src/libslic3r/CMakeLists.txt | 11 ++++++++++- .../libslic3r/OpenVDBUtils.cpp | 7 +++---- .../libslic3r/OpenVDBUtils.hpp | 6 +++--- tests/CMakeLists.txt | 3 +-- tests/hollowing/CMakeLists.txt | 9 --------- tests/hollowing/hollowing_test_main.cpp | 1 - tests/libslic3r/CMakeLists.txt | 6 ++++++ .../test_hollowing.cpp} | 2 +- 8 files changed, 24 insertions(+), 21 deletions(-) rename tests/hollowing/openvdb_utils.cpp => src/libslic3r/OpenVDBUtils.cpp (97%) rename tests/hollowing/openvdb_utils.hpp => src/libslic3r/OpenVDBUtils.hpp (90%) delete mode 100644 tests/hollowing/CMakeLists.txt delete mode 100644 tests/hollowing/hollowing_test_main.cpp rename tests/{hollowing/hollowing_tests.cpp => libslic3r/test_hollowing.cpp} (96%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 8b162a1d58..9ee6e60c52 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -9,6 +9,11 @@ if (MINGW) add_compile_options(-Wa,-mbig-obj) endif () +set(OpenVDBUtils_SOURCES "") +if (TARGET OpenVDB::openvdb) + set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp) +endif() + add_library(libslic3r STATIC pchheader.cpp pchheader.hpp @@ -174,6 +179,7 @@ add_library(libslic3r STATIC MinAreaBoundingBox.cpp miniz_extension.hpp miniz_extension.cpp + ${OpenVDBUtils_SOURCES} SLA/SLACommon.hpp SLA/SLABoilerPlate.hpp SLA/SLAPad.hpp @@ -222,10 +228,13 @@ target_link_libraries(libslic3r qhull semver TBB::tbb - # OpenVDB::openvdb ${CMAKE_DL_LIBS} ) +if (TARGET OpenVDB::openvdb) + target_link_libraries(libslic3r OpenVDB::openvdb) +endif() + if(WIN32) target_link_libraries(libslic3r Psapi.lib) endif() diff --git a/tests/hollowing/openvdb_utils.cpp b/src/libslic3r/OpenVDBUtils.cpp similarity index 97% rename from tests/hollowing/openvdb_utils.cpp rename to src/libslic3r/OpenVDBUtils.cpp index 62d3a14d70..a5d4f0db69 100644 --- a/tests/hollowing/openvdb_utils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -1,6 +1,5 @@ #define NOMINMAX -#include "openvdb_utils.hpp" - +#include "OpenVDBUtils.hpp" #include #include @@ -55,7 +54,7 @@ openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, const openvdb::math::Transform &tr) { return openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{mesh}, tr); + TriangleMeshDataAdapter{mesh}, tr); } openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, @@ -78,7 +77,7 @@ sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, std::vector points; std::vector triangles; std::vector quads; - + openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue, adaptivity, relaxDisorientedTriangles); diff --git a/tests/hollowing/openvdb_utils.hpp b/src/libslic3r/OpenVDBUtils.hpp similarity index 90% rename from tests/hollowing/openvdb_utils.hpp rename to src/libslic3r/OpenVDBUtils.hpp index 9854d924da..e424038876 100644 --- a/tests/hollowing/openvdb_utils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -1,5 +1,5 @@ -#ifndef OPENVDB_UTILS_HPP -#define OPENVDB_UTILS_HPP +#ifndef OPENVDBUTILS_HPP +#define OPENVDBUTILS_HPP #include #include @@ -20,4 +20,4 @@ sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, } // namespace Slic3r -#endif // OPENVDB_UTILS_HPP +#endif // OPENVDBUTILS_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0d2266dc56..f77b4bd25f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,5 +27,4 @@ add_subdirectory(libslic3r) add_subdirectory(timeutils) add_subdirectory(fff_print) add_subdirectory(sla_print) -add_subdirectory(hollowing) -add_subdirectory(example) +# add_subdirectory(example) diff --git a/tests/hollowing/CMakeLists.txt b/tests/hollowing/CMakeLists.txt deleted file mode 100644 index 9679a1b777..0000000000 --- a/tests/hollowing/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -if(TARGET OpenVDB::openvdb) - add_executable(hollowing_tests hollowing_test_main.cpp hollowing_tests.cpp openvdb_utils.cpp openvdb_utils.hpp) - - #find_package(GTest REQUIRED) - #target_link_libraries(hollowing_tests libslic3r OpenVDB::openvdb GTest::GTest GTest::Main) - #target_compile_definitions(hollowing_tests PRIVATE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)") - - target_link_libraries(hollowing_tests test_common libslic3r OpenVDB::openvdb) -endif() diff --git a/tests/hollowing/hollowing_test_main.cpp b/tests/hollowing/hollowing_test_main.cpp deleted file mode 100644 index b2aa80259d..0000000000 --- a/tests/hollowing/hollowing_test_main.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 98aab498e4..b4f5ba01ec 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -1,4 +1,5 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp test_3mf.cpp @@ -7,6 +8,11 @@ add_executable(${_TEST_NAME}_tests test_polygon.cpp test_stl.cpp ) + +if (TARGET OpenVDB::openvdb) + target_sources(${_TEST_NAME}_tests PRIVATE test_hollowing.cpp) +endif() + target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/hollowing/hollowing_tests.cpp b/tests/libslic3r/test_hollowing.cpp similarity index 96% rename from tests/hollowing/hollowing_tests.cpp rename to tests/libslic3r/test_hollowing.cpp index a7b2874eb0..0e211c33ce 100644 --- a/tests/hollowing/hollowing_tests.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -2,7 +2,7 @@ #include #include -#include "openvdb_utils.hpp" +#include "libslic3r/OpenVDBUtils.hpp" #include "libslic3r/Format/OBJ.hpp" #if defined(WIN32) || defined(_WIN32) From 7808d09d062b95c7c87c219916cb9b0e5d24eba6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 4 Nov 2019 14:33:29 +0100 Subject: [PATCH 017/130] SLA Contour3D expanded with conversions supporting quads. --- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Format/objparser.cpp | 29 + src/libslic3r/Format/objparser.hpp | 2 + src/libslic3r/SLA/SLABoilerPlate.hpp | 122 - src/libslic3r/SLA/SLACommon.cpp | 147 + src/libslic3r/SLA/SLACommon.hpp | 75 +- src/libslic3r/SLA/SLAPad.cpp | 2 +- src/libslic3r/SLA/SLASupportTreeBuilder.cpp | 2 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 20 + tests/data/extruder_idler_quads.obj | 7348 +++++++++++++++++++ tests/libslic3r/test_hollowing.cpp | 17 +- tests/sla_print/CMakeLists.txt | 2 +- tests/sla_print/sla_print_tests.cpp | 268 +- tests/sla_print/sla_print_tests_main.cpp | 1 + 14 files changed, 7777 insertions(+), 259 deletions(-) create mode 100644 src/libslic3r/SLA/SLACommon.cpp create mode 100644 tests/data/extruder_idler_quads.obj create mode 100644 tests/sla_print/sla_print_tests_main.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 9ee6e60c52..47e49eadb0 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -181,6 +181,7 @@ add_library(libslic3r STATIC miniz_extension.cpp ${OpenVDBUtils_SOURCES} SLA/SLACommon.hpp + SLA/SLACommon.cpp SLA/SLABoilerPlate.hpp SLA/SLAPad.hpp SLA/SLAPad.cpp diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp index c805667f81..8279c55308 100644 --- a/src/libslic3r/Format/objparser.cpp +++ b/src/libslic3r/Format/objparser.cpp @@ -355,6 +355,35 @@ bool objparse(const char *path, ObjData &data) return true; } +bool objparse(std::istream &stream, ObjData &data) +{ + try { + char buf[65536 * 2]; + size_t len = 0; + size_t lenPrev = 0; + while ((len = size_t(stream.read(buf + lenPrev, 65536).gcount())) != 0) { + len += lenPrev; + size_t lastLine = 0; + for (size_t i = 0; i < len; ++ i) + if (buf[i] == '\r' || buf[i] == '\n') { + buf[i] = 0; + char *c = buf + lastLine; + while (*c == ' ' || *c == '\t') + ++ c; + obj_parseline(c, data); + lastLine = i + 1; + } + lenPrev = len - lastLine; + memmove(buf, buf + lastLine, lenPrev); + } + } + catch (std::bad_alloc&) { + printf("Out of memory\r\n"); + } + + return true; +} + template bool savevector(FILE *pFile, const std::vector &v) { diff --git a/src/libslic3r/Format/objparser.hpp b/src/libslic3r/Format/objparser.hpp index 5fc25e297b..5f3f010e4a 100644 --- a/src/libslic3r/Format/objparser.hpp +++ b/src/libslic3r/Format/objparser.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace ObjParser { @@ -97,6 +98,7 @@ struct ObjData { }; extern bool objparse(const char *path, ObjData &data); +extern bool objparse(std::istream &stream, ObjData &data); extern bool objbinsave(const char *path, const ObjData &data); diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index 1bb1943ef1..9be91ac1d1 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -17,128 +17,6 @@ typedef Eigen::Matrix Vec4i; namespace sla { -/// Intermediate struct for a 3D mesh -struct Contour3D { - Pointf3s points; - std::vector faces3; - std::vector faces4; - - Contour3D& merge(const Contour3D& ctr) - { - auto N = coord_t(points.size()); - auto N_f3 = faces3.size(); - auto N_f4 = faces4.size(); - - points.insert(points.end(), ctr.points.begin(), ctr.points.end()); - faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end()); - faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end()); - - for(size_t n = N_f3; n < faces3.size(); n++) { - auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N; - } - - for(size_t n = N_f4; n < faces4.size(); n++) { - auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N; - } - - return *this; - } - - Contour3D& merge(const Pointf3s& triangles) - { - const size_t offs = points.size(); - points.insert(points.end(), triangles.begin(), triangles.end()); - faces3.reserve(faces3.size() + points.size() / 3); - - for(int i = int(offs); i < int(points.size()); i += 3) - faces3.emplace_back(i, i + 1, i + 2); - - return *this; - } - - // Write the index triangle structure to OBJ file for debugging purposes. - void to_obj(std::ostream& stream) - { - for(auto& p : points) { - stream << "v " << p.transpose() << "\n"; - } - - for(auto& f : faces3) { - stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; - } - - for(auto& f : faces4) { - stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n"; - } - } - - bool empty() const { return points.empty() || (faces4.empty() && faces3.empty()); } -}; - -using ClusterEl = std::vector; -using ClusteredPoints = std::vector; - -// Clustering a set of points by the given distance. -ClusteredPoints cluster(const std::vector& indices, - std::function pointfn, - double dist, - unsigned max_points); - -ClusteredPoints cluster(const PointSet& points, - double dist, - unsigned max_points); - -ClusteredPoints cluster( - const std::vector& indices, - std::function pointfn, - std::function predicate, - unsigned max_points); - - -// Calculate the normals for the selected points (from 'points' set) on the -// mesh. This will call squared distance for each point. -PointSet normals(const PointSet& points, - const EigenMesh3D& convert_mesh, - double eps = 0.05, // min distance from edges - std::function throw_on_cancel = [](){}, - const std::vector& selected_points = {}); - -/// Mesh from an existing contour. -inline TriangleMesh convert_mesh(const Contour3D& ctour) { - return {ctour.points, ctour.faces3}; -} - -/// Mesh from an evaporating 3D contour -inline TriangleMesh convert_mesh(Contour3D&& ctour) { - return {std::move(ctour.points), std::move(ctour.faces3)}; -} - -inline Contour3D convert_mesh(const TriangleMesh &trmesh) { - Contour3D ret; - ret.points.reserve(trmesh.its.vertices.size()); - ret.faces3.reserve(trmesh.its.indices.size()); - - for (auto &v : trmesh.its.vertices) - ret.points.emplace_back(v.cast()); - - std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(), - std::back_inserter(ret.faces3)); - - return ret; -} - -inline Contour3D convert_mesh(TriangleMesh &&trmesh) { - Contour3D ret; - ret.points.reserve(trmesh.its.vertices.size()); - - for (auto &v : trmesh.its.vertices) - ret.points.emplace_back(v.cast()); - - ret.faces3.swap(trmesh.its.indices); - - return ret; -} - } } diff --git a/src/libslic3r/SLA/SLACommon.cpp b/src/libslic3r/SLA/SLACommon.cpp new file mode 100644 index 0000000000..e6fbed7ec1 --- /dev/null +++ b/src/libslic3r/SLA/SLACommon.cpp @@ -0,0 +1,147 @@ +#include "SLACommon.hpp" +#include + +namespace Slic3r { namespace sla { + +Contour3D::Contour3D(const TriangleMesh &trmesh) +{ + points.reserve(trmesh.its.vertices.size()); + faces3.reserve(trmesh.its.indices.size()); + + for (auto &v : trmesh.its.vertices) + points.emplace_back(v.cast()); + + std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(), + std::back_inserter(faces3)); +} + +Contour3D::Contour3D(TriangleMesh &&trmesh) +{ + points.reserve(trmesh.its.vertices.size()); + + for (auto &v : trmesh.its.vertices) + points.emplace_back(v.cast()); + + faces3.swap(trmesh.its.indices); +} + +Contour3D::Contour3D(const EigenMesh3D &emesh) { + points.reserve(size_t(emesh.V().rows())); + faces3.reserve(size_t(emesh.F().rows())); + + for (int r = 0; r < emesh.V().rows(); r++) + points.emplace_back(emesh.V().row(r).cast()); + + for (int i = 0; i < emesh.F().rows(); i++) + faces3.emplace_back(emesh.F().row(i)); +} + +Contour3D &Contour3D::merge(const Contour3D &ctr) +{ + auto N = coord_t(points.size()); + auto N_f3 = faces3.size(); + auto N_f4 = faces4.size(); + + points.insert(points.end(), ctr.points.begin(), ctr.points.end()); + faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end()); + faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end()); + + for(size_t n = N_f3; n < faces3.size(); n++) { + auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N; + } + + for(size_t n = N_f4; n < faces4.size(); n++) { + auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N; + } + + return *this; +} + +Contour3D &Contour3D::merge(const Pointf3s &triangles) +{ + const size_t offs = points.size(); + points.insert(points.end(), triangles.begin(), triangles.end()); + faces3.reserve(faces3.size() + points.size() / 3); + + for(int i = int(offs); i < int(points.size()); i += 3) + faces3.emplace_back(i, i + 1, i + 2); + + return *this; +} + +void Contour3D::to_obj(std::ostream &stream) +{ + for(auto& p : points) + stream << "v " << p.transpose() << "\n"; + + for(auto& f : faces3) + stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; + + for(auto& f : faces4) + stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n"; +} + +void Contour3D::from_obj(std::istream &stream) +{ + ObjParser::ObjData data; + ObjParser::objparse(stream, data); + + points.reserve(data.coordinates.size() / 4 + 1); + auto &coords = data.coordinates; + for (size_t i = 0; i < coords.size(); i += 4) + points.emplace_back(coords[i], coords[i + 1], coords[i + 2]); + + Vec3i triangle; + Vec4i quad; + size_t v = 0; + while(v < data.vertices.size()) { + size_t N = 0; + size_t i = v; + while (data.vertices[v++].coordIdx != -1) ++N; + + std::function setfn; + if (N < 3 || N > 4) continue; + else if (N == 3) setfn = [&triangle](int k, int f) { triangle(k) = f; }; + else setfn = [&quad](int k, int f) { quad(k) = f; }; + + for (size_t j = 0; j < N; ++j) + setfn(int(j), data.vertices[i + j].coordIdx); + } +} + +TriangleMesh to_triangle_mesh(const Contour3D &ctour) { + if (ctour.faces4.empty()) return {ctour.points, ctour.faces3}; + + std::vector triangles; + + triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size()); + std::copy(ctour.faces3.begin(), ctour.faces3.end(), + std::back_inserter(triangles)); + + for (auto &quad : ctour.faces4) { + triangles.emplace_back(quad(0), quad(1), quad(2)); + triangles.emplace_back(quad(2), quad(3), quad(0)); + } + + return {ctour.points, std::move(triangles)}; +} + +TriangleMesh to_triangle_mesh(Contour3D &&ctour) { + if (ctour.faces4.empty()) + return {std::move(ctour.points), std::move(ctour.faces3)}; + + std::vector triangles; + + triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size()); + std::copy(ctour.faces3.begin(), ctour.faces3.end(), + std::back_inserter(triangles)); + + for (auto &quad : ctour.faces4) { + triangles.emplace_back(quad(0), quad(1), quad(2)); + triangles.emplace_back(quad(2), quad(3), quad(0)); + } + + return {std::move(ctour.points), std::move(triangles)}; +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 97b4596761..7cdc626629 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -5,6 +5,11 @@ #include #include +#include "SLASpatIndex.hpp" + +#include +#include + // #define SLIC3R_SLA_NEEDS_WINDTREE namespace Slic3r { @@ -12,8 +17,7 @@ namespace Slic3r { // Typedefs from Point.hpp typedef Eigen::Matrix Vec3f; typedef Eigen::Matrix Vec3d; - -class TriangleMesh; +typedef Eigen::Matrix Vec4i; namespace sla { @@ -59,9 +63,11 @@ struct SupportPoint bool operator==(const SupportPoint &sp) const { - return (pos == sp.pos) && head_front_radius == sp.head_front_radius && + float rdiff = std::abs(head_front_radius - sp.head_front_radius); + return (pos == sp.pos) && rdiff < float(EPSILON) && is_new_island == sp.is_new_island; } + bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); } template void serialize(Archive &ar) @@ -72,8 +78,11 @@ struct SupportPoint using SupportPoints = std::vector; +struct Contour3D; + /// An index-triangle structure for libIGL functions. Also serves as an -/// alternative (raw) input format for the SLASupportTree +/// alternative (raw) input format for the SLASupportTree. +// Implemented in SLASupportTreeIGL.cpp class EigenMesh3D { class AABBImpl; @@ -86,6 +95,7 @@ public: EigenMesh3D(const TriangleMesh&); EigenMesh3D(const EigenMesh3D& other); + EigenMesh3D(const Contour3D &other); EigenMesh3D& operator=(const EigenMesh3D&); ~EigenMesh3D(); @@ -180,6 +190,63 @@ public: using PointSet = Eigen::MatrixXd; + +/// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with +/// other meshes of this type and converting to and from other mesh formats. +struct Contour3D { + Pointf3s points; + std::vector faces3; + std::vector faces4; + + Contour3D() = default; + Contour3D(const TriangleMesh &trmesh); + Contour3D(TriangleMesh &&trmesh); + Contour3D(const EigenMesh3D &emesh); + + Contour3D& merge(const Contour3D& ctr); + Contour3D& merge(const Pointf3s& triangles); + + // Write the index triangle structure to OBJ file for debugging purposes. + void to_obj(std::ostream& stream); + void from_obj(std::istream &stream); + + inline bool empty() const { return points.empty() || (faces4.empty() && faces3.empty()); } +}; + +using ClusterEl = std::vector; +using ClusteredPoints = std::vector; + +// Clustering a set of points by the given distance. +ClusteredPoints cluster(const std::vector& indices, + std::function pointfn, + double dist, + unsigned max_points); + +ClusteredPoints cluster(const PointSet& points, + double dist, + unsigned max_points); + +ClusteredPoints cluster( + const std::vector& indices, + std::function pointfn, + std::function predicate, + unsigned max_points); + + +// Calculate the normals for the selected points (from 'points' set) on the +// mesh. This will call squared distance for each point. +PointSet normals(const PointSet& points, + const EigenMesh3D& convert_mesh, + double eps = 0.05, // min distance from edges + std::function throw_on_cancel = [](){}, + const std::vector& selected_points = {}); + +/// Mesh from an existing contour. +TriangleMesh to_triangle_mesh(const Contour3D& ctour); + +/// Mesh from an evaporating 3D contour +TriangleMesh to_triangle_mesh(Contour3D&& ctour); + } // namespace sla } // namespace Slic3r diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp index d0e802d842..264cfba9ff 100644 --- a/src/libslic3r/SLA/SLAPad.cpp +++ b/src/libslic3r/SLA/SLAPad.cpp @@ -677,7 +677,7 @@ void create_pad(const ExPolygons &sup_blueprint, ThrowOnCancel thr) { Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); - out.merge(convert_mesh(std::move(t))); + out.merge(to_triangle_mesh(std::move(t))); } std::string PadConfig::validate() const diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp index 0c276738a2..df51c6b5fb 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.cpp +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.cpp @@ -466,7 +466,7 @@ const TriangleMesh &SupportTreeBuilder::merged_mesh() const return m_meshcache; } - m_meshcache = convert_mesh(merged); + m_meshcache = to_triangle_mesh(merged); // The mesh will be passed by const-pointer to TriangleMeshSlicer, // which will need this. diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 05f8b19842..5e10c28c91 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -228,6 +228,26 @@ EigenMesh3D::EigenMesh3D(const EigenMesh3D &other): m_V(other.m_V), m_F(other.m_F), m_ground_level(other.m_ground_level), m_aabb( new AABBImpl(*other.m_aabb) ) {} +EigenMesh3D::EigenMesh3D(const Contour3D &other) +{ + m_V.resize(Eigen::Index(other.points.size()), 3); + m_F.resize(Eigen::Index(other.faces3.size() + 2 * other.faces4.size()), 3); + + for (Eigen::Index i = 0; i < Eigen::Index(other.points.size()); ++i) + m_V.row(i) = other.points[size_t(i)]; + + for (Eigen::Index i = 0; i < Eigen::Index(other.faces3.size()); ++i) + m_F.row(i) = other.faces3[size_t(i)]; + + size_t N = other.faces3.size() + 2 * other.faces4.size(); + for (size_t i = other.faces3.size(); i < N; i += 2) { + size_t quad_idx = (i - other.faces3.size()) / 2; + auto & quad = other.faces4[quad_idx]; + m_F.row(Eigen::Index(i)) = Vec3i{quad(0), quad(1), quad(2)}; + m_F.row(Eigen::Index(i + 1)) = Vec3i{quad(2), quad(3), quad(0)}; + } +} + EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) { m_V = other.m_V; diff --git a/tests/data/extruder_idler_quads.obj b/tests/data/extruder_idler_quads.obj new file mode 100644 index 0000000000..266cb242d1 --- /dev/null +++ b/tests/data/extruder_idler_quads.obj @@ -0,0 +1,7348 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object extruder_idler_quads.obj +# +# Vertices: 3660 +# Faces: 3672 +# +#### +v 61.288807 37.917557 6.068595 +v 61.312790 37.917618 6.193261 +v 61.356049 37.917774 6.504920 +v 46.677547 37.917866 9.519013 +v 55.420834 37.917618 7.326775 +v 55.396851 37.917557 7.202109 +v 70.876183 37.682098 2.970614 +v 46.216084 37.450668 7.120349 +v 61.171124 37.838562 5.456883 +v 70.404060 35.864510 0.516525 +v 70.345604 35.376511 0.212692 +v 54.692173 35.864510 3.539228 +v 54.633720 35.376511 3.235396 +v 61.171051 37.731289 7.795076 +v 55.864639 37.839470 8.386023 +v 55.279167 37.838562 6.590397 +v 46.326366 37.682098 7.693588 +v 70.566055 36.778572 1.358596 +v 70.661987 37.147907 1.857247 +v 54.854176 36.778572 4.381299 +v 46.112171 37.147907 6.580221 +v 55.054016 37.450668 5.420078 +v 60.659683 36.348526 2.798444 +v 54.767727 36.348526 3.931957 +v 45.929794 36.348526 5.632228 +v 50.387226 39.697979 28.801762 +v 50.382210 39.699635 28.775690 +v 48.877922 40.196674 20.956472 +v 48.879765 39.946739 20.966045 +v 48.897690 38.450691 21.059217 +v 48.672623 38.559273 19.889320 +v 48.627918 34.768574 19.656961 +v 48.967503 37.518620 21.422112 +v 49.148136 36.364048 22.361021 +v 49.308113 35.827209 23.192574 +v 49.224934 36.068989 22.760210 +v 50.379288 38.947979 28.760509 +v 50.332268 37.976688 28.516096 +v 50.291908 37.522064 28.306307 +v 50.557526 37.884193 29.686979 +v 49.863396 35.643738 26.078924 +v 49.771500 35.518578 25.601242 +v 50.513439 34.145573 29.457813 +v 46.316734 33.337154 7.643516 +v 45.795788 35.376511 4.935667 +v 46.366432 33.896751 7.901852 +v 45.854240 35.864510 5.239500 +v 46.306992 32.926773 7.592876 +v 46.316822 32.516430 7.643979 +v 46.420128 31.655691 8.180969 +v 46.451801 31.534010 8.345595 +v 46.486115 31.434301 8.523966 +v 46.599277 31.280785 9.112178 +v 46.099930 28.231632 6.516577 +v 46.638424 31.280876 9.315653 +v 47.221455 27.861065 12.346222 +v 46.715168 31.358620 9.714571 +v 46.785843 31.534855 10.081941 +v 47.732357 31.056692 15.001865 +v 46.930408 32.928387 10.833368 +v 46.927967 32.721592 10.820692 +v 47.124451 37.150532 11.842004 +v 47.220444 36.781719 12.340965 +v 46.817513 31.656748 10.246544 +v 46.870964 31.958406 10.524391 +v 46.891937 32.133430 10.633411 +v 47.306953 36.352104 12.790643 +v 46.391373 34.056538 8.031486 +v 46.016239 36.778572 6.081570 +v 46.441235 37.838562 8.290668 +v 46.598972 34.574272 9.110590 +v 46.560146 34.548199 8.908764 +v 46.558918 37.917557 8.902380 +v 46.714870 34.497074 9.713024 +v 47.020462 37.452759 11.301476 +v 46.910164 37.683620 10.728139 +v 46.870785 33.898132 10.523453 +v 46.891777 33.723171 10.632566 +v 46.751282 34.420887 9.902280 +v 51.630699 27.764736 35.265278 +v 52.147446 28.245863 37.951313 +v 46.795250 37.839470 10.130821 +v 71.227364 37.917866 4.796038 +v 76.205231 29.860657 30.670778 +v 70.878639 33.534252 2.983394 +v 70.916252 33.896751 3.178878 +v 70.969704 34.198410 3.456725 +v 71.001366 34.320305 3.621328 +v 70.991051 37.838562 3.567693 +v 71.108734 37.917557 4.179405 +v 71.109962 34.548199 4.185790 +v 71.226776 34.548508 4.792975 +v 71.345062 37.839470 5.407847 +v 71.459976 37.683620 6.005164 +v 71.301102 34.420887 5.179305 +v 71.441589 33.723171 5.909592 +v 70.479614 36.348526 0.909254 +v 70.866547 33.337154 2.920542 +v 70.765900 37.450668 2.397374 +v 70.916428 31.957125 3.179823 +v 70.304688 31.906664 0.000000 +v 71.001617 31.534010 3.622621 +v 71.035934 31.434301 3.800991 +v 71.149094 31.280785 4.389203 +v 71.110252 31.306643 4.187318 +v 71.227066 31.306923 4.794502 +v 71.301376 31.434967 5.180758 +v 71.264984 31.358620 4.991596 +v 71.458290 33.535702 5.996392 +v 71.477760 33.135204 6.097586 +v 71.480225 32.928387 6.110393 +v 72.579239 34.928131 11.823022 +v 73.058540 33.686779 14.314425 +v 71.367325 31.656748 5.523570 +v 73.568642 32.857552 16.965889 +v 73.831314 32.604336 18.331276 +v 75.124626 28.113617 25.053829 +v 74.363899 32.428364 21.099625 +v 74.895103 32.695507 23.860798 +v 75.156105 32.993713 25.217482 +v 75.149338 30.209538 25.182302 +v 76.679512 26.740294 33.136051 +v 76.697266 28.245863 33.228336 +v 76.180519 27.764736 30.542305 +v 71.996040 35.338497 8.791578 +v 72.012505 35.153580 8.877157 +v 56.978554 37.452759 9.385705 +v 57.088562 37.406780 9.449879 +v 71.674271 37.150532 7.119029 +v 60.551853 37.406780 8.783600 +v 59.598507 37.150532 9.442204 +v 47.382626 35.868492 13.183980 +v 71.856773 36.352104 8.067668 +v 71.770264 36.781719 7.617991 +v 59.439774 37.126163 9.506907 +v 58.300529 37.132675 9.716898 +v 58.834583 37.093670 9.668919 +v 71.570282 37.452759 6.578501 +v 71.932442 35.868492 8.461005 +v 47.446220 35.338497 13.514553 +v 44.992771 27.189064 13.242427 +v 51.780609 26.868217 17.170813 +v 61.232605 27.449877 5.863266 +v 58.929241 27.283329 9.023430 +v 58.809258 27.282513 9.059827 +v 58.359135 27.139223 11.484049 +v 58.309364 27.285778 9.102742 +v 55.616589 27.411655 7.567233 +v 47.938751 27.189064 12.675670 +v 54.076530 26.062111 29.879890 +v 54.026432 26.058466 29.948986 +v 53.712753 26.029385 30.483768 +v 53.552719 25.999935 30.994987 +v 53.514042 25.981487 31.303373 +v 53.514153 25.976261 31.388626 +v 50.928787 26.201115 28.217762 +v 53.514442 25.962425 31.614302 +v 68.806427 26.062111 27.046103 +v 68.623718 26.048828 27.297941 +v 68.756279 26.058466 27.115208 +v 68.515053 26.037975 27.495895 +v 62.093616 26.073893 28.145342 +v 68.338547 26.012682 27.942495 +v 68.254539 25.986547 28.385019 +v 68.244034 25.976261 28.554842 +v 68.218460 26.326206 22.850769 +v 73.165169 25.937754 28.236309 +v 73.194099 25.909863 28.685766 +v 73.183594 25.899576 28.855589 +v 72.995468 25.856739 29.590630 +v 72.923080 25.848146 29.744713 +v 72.631706 25.824013 30.194506 +v 71.988434 25.798077 30.741371 +v 71.553574 25.790627 30.946589 +v 71.338646 25.788862 31.016699 +v 70.807259 25.790627 31.090166 +v 70.411186 25.796120 31.076721 +v 70.327293 25.798077 31.060946 +v 70.105293 25.803261 31.019094 +v 58.193188 25.848146 32.578499 +v 68.283722 25.943062 29.088823 +v 68.361572 25.923698 29.389732 +v 68.626915 25.886189 29.950634 +v 68.759964 25.873442 30.132986 +v 58.265583 25.856739 32.424416 +v 68.965179 25.856739 30.365990 +v 69.023651 25.852247 30.428022 +v 69.089577 25.848146 30.482216 +v 69.263924 25.837296 30.625723 +v 69.527039 25.824013 30.791792 +v 69.604355 25.820885 30.827938 +v 68.476524 25.904634 29.678623 +v 58.435280 25.937754 31.070093 +v 58.463875 25.923698 31.293884 +v 58.464321 25.904634 31.604805 +v 58.369694 25.873442 32.131901 +v 68.810265 25.868631 30.201771 +v 69.454826 25.827658 30.746227 +v 57.951962 25.827658 32.959187 +v 69.808907 25.812611 30.923559 +v 57.688858 25.812611 33.255257 +v 70.023972 25.805826 30.992867 +v 57.514957 25.805826 33.399391 +v 57.109299 25.794794 33.657429 +v 56.904839 25.791292 33.753883 +v 56.823685 25.790627 33.780373 +v 56.608757 25.788862 33.850483 +v 56.302967 25.788862 33.909313 +v 56.077370 25.790627 33.923950 +v 55.375401 25.803261 33.852879 +v 54.874439 25.820885 33.661728 +v 54.080334 25.868631 33.035564 +v 52.746826 25.600410 37.667839 +v 54.359653 25.848146 33.316006 +v 54.533997 25.837296 33.459515 +v 54.797138 25.824013 33.625580 +v 55.079014 25.812611 33.757343 +v 55.766525 25.794794 33.915756 +v 55.681297 25.796120 33.910507 +v 73.076561 25.962425 27.850878 +v 72.993141 25.976261 27.641191 +v 72.811218 25.999935 27.289974 +v 71.911095 26.062111 26.448816 +v 71.833778 26.065239 26.412672 +v 71.629128 26.073511 26.317068 +v 70.716064 26.094831 26.144928 +v 70.630875 26.095497 26.150444 +v 69.598946 26.091330 26.416964 +v 69.521751 26.090002 26.453463 +v 66.578064 26.868217 14.324030 +v 66.011253 26.886908 14.128163 +v 60.892159 27.567686 4.006828 +v 61.173477 27.469414 5.555912 +v 61.073132 26.073893 28.341667 +v 58.403152 25.948370 30.903088 +v 58.263256 25.976261 30.474977 +v 58.081326 25.999935 30.123760 +v 57.948280 26.012682 29.941408 +v 57.618668 26.037975 29.592178 +v 57.684593 26.033876 29.646372 +v 57.444317 26.048828 29.448671 +v 57.181206 26.062111 29.282602 +v 57.103886 26.065239 29.246456 +v 56.684273 26.080296 29.081528 +v 56.899239 26.073511 29.150852 +v 55.986176 26.094831 28.978714 +v 55.675388 26.097260 28.998867 +v 54.869053 26.091330 29.250750 +v 54.791859 26.090002 29.287249 +v 54.719810 26.088047 29.333023 +v 54.463398 26.080296 29.508787 +v 53.421013 26.326206 25.697552 +v 54.529179 26.082863 29.454258 +v 67.902382 34.970417 12.655975 +v 47.462685 35.153580 13.600132 +v 54.822617 34.805954 17.855391 +v 58.905815 34.974163 14.325680 +v 54.142002 34.777916 18.443687 +v 56.180191 34.357391 24.912003 +v 64.640221 42.441532 22.165798 +v 64.498436 42.393948 21.428791 +v 64.108391 38.000809 19.401363 +v 65.105515 36.364048 19.291088 +v 64.965973 41.759834 23.859028 +v 65.182312 36.068989 19.690277 +v 64.884438 37.973030 18.141928 +v 64.924889 37.518620 18.352179 +v 64.831230 39.445396 17.865381 +v 64.038155 39.445889 19.036266 +v 65.353737 35.642532 20.581331 +v 65.445679 35.517838 21.059244 +v 65.633987 43.439590 22.038057 +v 64.077850 40.550362 19.242599 +v 64.837143 39.946739 17.896112 +v 65.265495 35.827209 20.122641 +v 64.330994 39.446640 20.558413 +v 64.055885 40.191998 19.128443 +v 64.350174 39.980480 20.658138 +v 64.243858 37.134884 20.105499 +v 64.108124 40.891331 19.399967 +v 64.364799 40.145279 20.734161 +v 64.042656 39.069904 19.059660 +v 64.078056 38.341614 19.243677 +v 64.339630 39.086071 20.603313 +v 64.350266 38.912914 20.658621 +v 64.405319 38.454250 20.944765 +v 64.488396 38.135056 21.376616 +v 64.430038 36.593735 21.073254 +v 64.301460 36.913589 20.404915 +v 64.520393 38.068111 21.542925 +v 64.587852 38.000175 21.893555 +v 64.569603 36.453186 21.798729 +v 64.640778 36.453377 22.168688 +v 64.711395 36.500771 22.535736 +v 64.721664 38.135670 22.589117 +v 64.846489 36.733505 23.237947 +v 64.804688 38.455292 23.020670 +v 64.826614 38.595646 23.134649 +v 65.063599 37.685196 24.366478 +v 64.845032 38.749439 23.230366 +v 64.243446 41.757961 20.103352 +v 64.405144 40.439426 20.943855 +v 64.300987 41.979553 20.402464 +v 64.458015 40.671249 21.218702 +v 64.488167 40.759056 21.375410 +v 64.429504 42.300076 21.070503 +v 64.553452 40.871548 21.714767 +v 64.569054 42.441341 21.795839 +v 64.656105 40.871815 22.248354 +v 64.689438 40.826614 22.421604 +v 64.845985 42.162468 23.235317 +v 64.908371 41.981129 23.559612 +v 64.804512 40.440468 23.019760 +v 64.844902 40.146526 23.229715 +v 64.859566 39.981804 23.305906 +v 64.876671 39.629799 23.394819 +v 64.751572 40.672024 22.744589 +v 64.779427 40.565052 22.889357 +v 65.101707 38.003387 24.564560 +v 65.153946 38.702721 24.836084 +v 65.131981 38.344357 24.721928 +v 65.167244 39.072823 24.905212 +v 65.153801 40.194855 24.835358 +v 64.363846 36.732250 20.729210 +v 64.966385 37.136757 23.861176 +v 66.068886 36.366562 24.298624 +v 65.820778 35.643738 23.008991 +v 66.289383 40.921688 25.444761 +v 66.318748 40.444027 25.597404 +v 64.975334 41.796913 18.614428 +v 64.146233 41.209522 19.598049 +v 64.363342 42.161217 20.726580 +v 65.728134 43.376877 22.527445 +v 64.779793 42.300983 22.891273 +v 64.056030 38.699863 19.129168 +v 64.975761 37.094627 18.616638 +v 64.146568 37.682812 19.599808 +v 64.191963 37.392635 19.835770 +v 64.042587 39.821896 19.059315 +v 64.498993 36.500225 21.431665 +v 64.780319 36.594643 22.894024 +v 66.137520 42.186985 24.655401 +v 65.101440 40.893909 24.563162 +v 64.191582 41.499920 19.833786 +v 65.035782 42.184120 18.928610 +v 64.924530 41.372654 18.350315 +v 65.353035 43.250977 20.577698 +v 65.991501 42.825726 23.896412 +v 66.068298 42.530670 24.295601 +v 64.710838 42.394493 22.532862 +v 65.171677 39.448830 24.928261 +v 66.336670 38.947979 25.690577 +v 65.017868 41.502083 24.128757 +v 65.063263 41.211906 24.364719 +v 66.318924 38.454502 25.598343 +v 65.018250 37.394798 24.130741 +v 64.908844 36.915165 23.562061 +v 65.992126 36.071102 23.899647 +v 65.634720 35.455376 22.041878 +v 65.131775 40.553104 24.720850 +v 65.167175 39.824814 24.904867 +v 66.336571 39.950649 25.690052 +v 65.539093 43.439342 21.544811 +v 68.963753 42.184120 18.172934 +v 65.264824 43.065830 20.119164 +v 65.181694 42.823616 19.687042 +v 68.852501 41.372654 17.594639 +v 68.782860 40.440216 17.232672 +v 64.884171 40.918030 18.140526 +v 70.264542 39.950649 24.934376 +v 70.267563 39.699635 24.950079 +v 65.444939 43.376144 21.055380 +v 65.104935 42.528164 19.288065 +v 69.032906 42.528164 18.532389 +v 64.854889 40.440216 17.988346 +v 68.903305 41.796913 17.858751 +v 69.748047 43.252190 22.249683 +v 65.820076 43.252190 23.005358 +v 65.908325 43.067505 23.464048 +v 70.217354 40.921688 24.689085 +v 70.246719 40.444027 24.841728 +v 66.198051 41.800091 24.970051 +v 70.065491 42.186985 23.899725 +v 66.248932 41.376099 25.234510 +v 70.176903 41.376099 24.478834 +v 65.539833 35.455128 21.548632 +v 65.733398 35.391171 22.554787 +v 65.737389 35.300861 22.575521 +v 65.728882 35.518578 22.531309 +v 65.908989 35.828888 23.467525 +v 66.514908 37.884193 26.617046 +v 66.289650 37.976688 25.446163 +v 66.249290 37.522064 25.236374 +v 66.138039 36.710598 24.658079 +v 66.198479 37.097805 24.972261 +v 66.339592 39.699635 25.705755 +v 66.342583 39.449322 25.721308 +v 64.837250 38.944069 17.896637 +v 64.855072 38.450691 17.989285 +v 65.036293 36.707745 18.931288 +v 64.597069 35.766628 16.648207 +v 64.835304 40.196674 17.886539 +v 70.272575 39.697979 24.976152 +v 68.773964 40.193138 17.186436 +v 64.845993 40.193138 17.942112 +v 69.192795 43.065830 19.363487 +v 69.274765 40.145279 19.789566 +v 69.315109 40.439426 19.999262 +v 69.367981 40.671249 20.274107 +v 69.398132 40.759056 20.430815 +v 69.467064 43.439342 20.789135 +v 69.372910 43.376144 20.299704 +v 69.430099 40.826176 20.596979 +v 69.497551 40.894455 20.947596 +v 69.566071 40.871815 21.303759 +v 69.531944 40.894543 21.126377 +v 69.561958 43.439590 21.282381 +v 69.656105 43.376877 21.771769 +v 69.599403 40.826614 21.477009 +v 69.836296 43.067505 22.708372 +v 69.919472 42.825726 23.140736 +v 69.689392 40.565052 21.944761 +v 69.996269 42.530670 23.539925 +v 69.714478 40.440468 22.075167 +v 69.754868 40.146526 22.285120 +v 70.126022 41.800091 24.214375 +v 69.736404 40.300220 22.189165 +v 68.763275 40.196674 17.130863 +v 68.812134 40.918030 17.384850 +v 69.249519 39.807266 19.658356 +v 69.243126 39.264919 19.625113 +v 69.109665 42.823616 18.931366 +v 69.281006 43.250977 19.822021 +v 69.240952 39.446640 19.613817 +v 69.315285 38.454250 20.000172 +v 69.340370 38.329666 20.130575 +v 68.904175 32.857552 17.863255 +v 68.646049 33.219078 16.521528 +v 68.513268 34.768574 15.831351 +v 69.368217 38.222706 20.275343 +v 69.166855 32.604336 19.228642 +v 69.699440 32.428364 21.996990 +v 69.566338 38.023182 21.305166 +v 70.442879 37.884193 25.861370 +v 69.736580 38.595646 22.190054 +v 69.769524 39.981804 22.361311 +v 69.769615 38.914249 22.361794 +v 69.780167 39.808659 22.416618 +v 69.786629 39.629799 22.450226 +v 70.230637 32.695507 24.758162 +v 67.914772 34.928131 12.720387 +v 68.394073 33.686779 15.211790 +v 66.470818 34.145573 26.387878 +v 64.642441 35.766621 16.639477 +v 64.630005 38.559273 16.819387 +v 44.275478 27.861065 12.912979 +v 46.509041 38.329666 24.522942 +v 46.462021 38.594498 24.278538 +v 46.418194 39.807266 24.050722 +v 45.931946 40.196674 21.523230 +v 46.428902 38.912914 24.106392 +v 45.951530 40.440216 21.625038 +v 45.942635 40.193138 21.578802 +v 46.443562 38.748192 24.182585 +v 46.483952 38.454250 24.392538 +v 46.567032 38.135056 24.824387 +v 47.555691 33.147514 29.963390 +v 46.800297 38.135670 26.036888 +v 46.948906 39.087452 26.809347 +v 47.088142 42.825726 27.533102 +v 46.938290 38.914249 26.754160 +v 47.234161 42.186985 28.292091 +v 47.345573 41.376099 28.871201 +v 46.883320 38.455292 26.468443 +v 47.611549 37.884193 30.253736 +v 47.415386 40.444027 29.234095 +v 46.957474 39.448078 26.853886 +v 47.441246 39.697979 29.368519 +v 46.132423 42.184120 22.565300 +v 46.566799 40.759056 24.823181 +v 46.598766 40.826176 24.989346 +v 46.632088 40.871548 25.162539 +v 46.541576 43.376144 24.692070 +v 46.666222 40.894455 25.339962 +v 46.700615 40.894543 25.518744 +v 46.734741 40.871815 25.696125 +v 46.858059 40.565052 26.337128 +v 46.905079 40.300220 26.581532 +v 46.923538 40.146526 26.677486 +v 46.948837 39.808659 26.808985 +v 47.004963 43.067505 27.100739 +v 45.655373 32.515556 20.085615 +v 48.601349 32.515556 19.518858 +v 46.635735 43.439342 25.181501 +v 49.581715 43.439342 24.614744 +v 50.034122 42.825726 26.966345 +v 46.361465 43.065830 23.755854 +v 46.449680 43.250977 24.214388 +v 48.967144 41.372654 21.420248 +v 48.926785 40.918030 21.210459 +v 45.980808 40.918030 21.777216 +v 49.017956 41.796913 21.684361 +v 46.824780 43.376877 26.164135 +v 47.433212 39.950649 29.326742 +v 47.436234 39.699635 29.342445 +v 49.487556 43.376144 24.125313 +v 46.201576 42.528164 22.924755 +v 46.278332 42.823616 23.323732 +v 48.897511 40.440216 21.058281 +v 48.888615 40.193138 21.012045 +v 46.071980 41.796913 22.251118 +v 46.021168 41.372654 21.987005 +v 46.730629 43.439590 25.674747 +v 47.164940 42.530670 27.932291 +v 46.916721 43.252190 26.642050 +v 49.950943 43.067505 26.533981 +v 50.361366 40.444027 28.667337 +v 50.379189 39.950649 28.759985 +v 50.180138 42.186985 27.725334 +v 50.240673 41.800091 28.039984 +v 47.294693 41.800091 28.606741 +v 50.291550 41.376099 28.304443 +v 47.386021 40.921688 29.081451 +v 50.331997 40.921688 28.514694 +v 49.676605 43.439590 25.107990 +v 49.677341 35.455376 25.111813 +v 49.582447 35.455128 24.618565 +v 49.862698 43.252190 26.075293 +v 50.607594 36.733505 25.977272 +v 50.541428 36.594643 25.633348 +v 49.396355 35.642532 23.651264 +v 50.669476 41.981129 26.298937 +v 50.094238 39.628387 23.308878 +v 49.799259 39.445889 21.775591 +v 49.838955 40.550362 21.981924 +v 49.869232 40.891331 22.139292 +v 50.144321 40.299072 23.569202 +v 50.166248 40.439426 23.683182 +v 50.004551 41.757961 22.842676 +v 49.817135 38.699863 21.868494 +v 50.219124 40.671249 23.958029 +v 50.100666 39.807266 23.342276 +v 50.111282 39.980480 23.397465 +v 50.249271 40.759056 24.114737 +v 50.062096 41.979553 23.141790 +v 50.191307 40.564144 23.813421 +v 50.281239 40.826176 24.280899 +v 50.190613 42.300076 23.809828 +v 50.314560 40.871548 24.454092 +v 50.401329 42.441532 24.905123 +v 50.417213 40.871815 24.987679 +v 50.471947 42.394493 25.272188 +v 50.450542 40.826614 25.160929 +v 50.482540 40.759663 25.327238 +v 50.540531 40.565052 25.628683 +v 50.862545 40.893909 27.302488 +v 50.824368 41.211906 27.104046 +v 49.839165 38.341614 21.983002 +v 50.111374 38.912914 23.397947 +v 49.869499 38.000809 22.140690 +v 50.004963 37.134884 22.844826 +v 50.191513 38.329666 23.814495 +v 50.166424 38.454250 23.684092 +v 50.219364 38.222706 23.959265 +v 50.281502 38.068111 24.282249 +v 50.260098 36.500225 24.170990 +v 50.383350 38.000263 24.811661 +v 50.401886 36.453377 24.908012 +v 50.330711 36.453186 24.538054 +v 50.472500 36.500771 25.275061 +v 50.450802 38.068542 25.162279 +v 50.482769 38.135670 25.328442 +v 50.669949 36.915165 26.301388 +v 50.540737 38.330574 25.629755 +v 50.892879 40.553104 27.460176 +v 50.637775 39.629799 26.134146 +v 50.779354 37.394798 26.870068 +v 50.914909 40.194855 27.574684 +v 50.587723 38.595646 25.873976 +v 50.862812 38.003387 27.303885 +v 50.620762 38.914249 26.045713 +v 50.631378 39.087452 26.100901 +v 50.932781 39.448830 27.667585 +v 50.727493 37.136757 26.600500 +v 50.111500 36.366562 27.368559 +v 50.361546 38.454502 28.668276 +v 50.893085 38.344357 27.461254 +v 50.915051 38.702721 27.575411 +v 49.907337 41.209522 22.337376 +v 48.879868 38.944069 20.966570 +v 49.953072 37.392635 22.575094 +v 49.907677 37.682812 22.339132 +v 49.078915 36.707745 22.001223 +v 50.062565 36.913589 23.144239 +v 50.191143 36.593735 23.812578 +v 50.124954 36.732250 23.468536 +v 49.770756 43.376877 25.597380 +v 50.607086 42.162468 25.974642 +v 50.540901 42.300983 25.630598 +v 50.778973 41.502083 26.868084 +v 50.034744 36.071102 26.969580 +v 49.951611 35.828888 26.537458 +v 50.180653 36.710598 27.728012 +v 50.928349 39.072823 27.644539 +v 49.078400 42.184120 21.998545 +v 48.927055 37.973030 21.211861 +v 49.018383 37.094627 21.686571 +v 49.488297 35.517838 24.129177 +v 50.110920 42.530670 27.365534 +v 50.727081 41.759834 26.598352 +v 50.928280 39.824814 27.644192 +v 50.385201 39.449322 28.791241 +v 50.824703 37.685196 27.105803 +v 50.241096 37.097805 28.042196 +v 49.803696 39.821896 21.798639 +v 48.873852 39.445396 20.935314 +v 49.307442 43.065830 23.189098 +v 49.952690 41.499920 22.573111 +v 49.803761 39.069904 21.798986 +v 49.395657 43.250977 23.647631 +v 49.147552 42.528164 22.357998 +v 49.224308 42.823616 22.756975 +v 50.124451 42.161217 23.465906 +v 49.816994 40.191998 21.867767 +v 50.330158 42.441341 24.535164 +v 50.259544 42.393948 24.168116 +v 68.149811 34.257607 13.942123 +v 72.566849 34.970417 11.758610 +v 72.814278 34.257607 13.044758 +v 73.310516 33.219078 15.624163 +v 74.630486 32.506702 22.485336 +v 69.432487 32.460957 20.609421 +v 74.096954 32.460957 19.712055 +v 69.966026 32.506702 23.382702 +v 70.491646 32.993713 26.114845 +v 64.621979 40.894543 22.070972 +v 64.587585 40.894455 21.892191 +v 50.348694 40.894455 24.631517 +v 50.383087 40.894543 24.810297 +v 69.788803 39.448078 22.461519 +v 46.955303 39.629799 26.842592 +v 69.340164 40.564144 20.129501 +v 64.430199 40.564144 21.074097 +v 46.483776 40.439426 24.391628 +v 64.430405 38.329666 21.075171 +v 64.364929 38.748192 20.734812 +v 69.274887 38.748192 19.790218 +v 50.126034 38.748192 23.474138 +v 69.661537 40.672024 21.799994 +v 64.339561 39.807266 20.602951 +v 69.243095 39.628387 19.624958 +v 46.411766 39.628387 24.017324 +v 46.443436 40.145279 24.181932 +v 64.458260 38.222706 21.219938 +v 69.398361 38.135056 20.432020 +v 50.249504 38.135056 24.115940 +v 46.536892 38.222706 24.667709 +v 69.463684 38.022903 20.771578 +v 69.430359 38.068111 20.598330 +v 50.314831 38.022903 24.455498 +v 69.293350 38.594498 19.886171 +v 50.144493 38.594498 23.570091 +v 69.249596 39.086071 19.658720 +v 69.260231 38.912914 19.714025 +v 50.100735 39.086071 23.342640 +v 64.870201 39.808659 23.361214 +v 64.520134 40.826176 21.541573 +v 69.463417 40.871548 20.770172 +v 46.508835 40.564144 24.521868 +v 46.536652 40.671249 24.666473 +v 64.333130 39.628387 20.569553 +v 46.409622 39.446640 24.006184 +v 50.092098 39.446640 23.297737 +v 69.260139 39.980480 19.713545 +v 50.125908 40.145279 23.473488 +v 46.428810 39.980480 24.105911 +v 64.383217 40.299072 20.829878 +v 69.293182 40.299072 19.885283 +v 46.461849 40.299072 24.277649 +v 46.599026 38.068111 24.990696 +v 64.553726 38.022903 21.716173 +v 50.348957 38.000175 24.632881 +v 46.632359 38.022903 25.163944 +v 64.383385 38.594498 20.830767 +v 64.333160 39.264919 20.569706 +v 46.418262 39.086071 24.051086 +v 46.411797 39.264919 24.017479 +v 50.094269 39.264919 23.309032 +v 46.883144 40.440468 26.467533 +v 50.565620 40.440468 25.759087 +v 69.714653 38.455292 22.076077 +v 46.905251 38.595646 26.582420 +v 50.565792 38.455292 25.759996 +v 50.620667 39.981804 26.045231 +v 46.938194 39.981804 26.753677 +v 69.497810 38.000175 20.948959 +v 46.666485 38.000175 25.341326 +v 50.587551 40.300220 25.873085 +v 50.606010 40.146526 25.969040 +v 64.779633 38.330574 22.890430 +v 69.599663 38.068542 21.478359 +v 69.631630 38.135670 21.644522 +v 46.768330 38.068542 25.870726 +v 50.631310 39.808659 26.100538 +v 64.721436 40.759663 22.587912 +v 69.631393 40.759663 21.643318 +v 46.768070 40.826614 25.869375 +v 46.830208 40.672024 26.192360 +v 50.512680 40.672024 25.483913 +v 46.800064 40.759663 26.035685 +v 64.826447 40.300220 23.133760 +v 69.786659 39.266331 22.450378 +v 64.878838 39.448078 23.406115 +v 50.637802 39.266331 26.134300 +v 50.639946 39.448078 26.145439 +v 64.876694 39.266331 23.394974 +v 46.955330 39.266331 26.842745 +v 64.751808 38.223469 22.745825 +v 69.661774 38.223469 21.801229 +v 69.689598 38.330574 21.945835 +v 46.830444 38.223469 26.193596 +v 46.858265 38.330574 26.338202 +v 50.512917 38.223469 25.485149 +v 64.656380 38.023182 22.249760 +v 64.689697 38.068542 22.422955 +v 46.735012 38.023182 25.697533 +v 64.622246 38.000263 22.072336 +v 69.532204 38.000263 21.127741 +v 46.700878 38.000263 25.520107 +v 50.417484 38.023182 24.989086 +v 69.754990 38.749439 22.285770 +v 64.859657 38.914249 23.306389 +v 50.606136 38.749439 25.969690 +v 46.923664 38.749439 26.678137 +v 64.870270 39.087452 23.361576 +v 69.780235 39.087452 22.416981 +v 61.316620 34.574280 6.299973 +v 61.312920 34.574375 6.490880 +v 55.456875 34.574280 7.427290 +v 61.036880 32.926773 4.759091 +v 61.039318 33.133568 4.771767 +v 70.859245 33.133568 2.882577 +v 55.144924 32.926773 5.892605 +v 60.995525 31.783306 7.656045 +v 60.987606 31.798721 7.674161 +v 71.395844 31.798721 5.671790 +v 55.257820 34.198410 6.479429 +v 70.866638 32.516430 2.921004 +v 71.072044 34.496571 3.988700 +v 46.522228 34.496571 8.711675 +v 46.485836 34.420193 8.522511 +v 70.895279 33.721729 3.069858 +v 46.328823 33.534252 7.706368 +v 55.183392 33.721729 6.092562 +v 55.166759 33.534252 6.006098 +v 70.941185 34.056538 3.308511 +v 61.096321 33.896751 5.068068 +v 61.058811 32.319458 4.873092 +v 70.878738 32.319458 2.983902 +v 46.328922 32.319458 7.706877 +v 70.859268 32.719955 2.882709 +v 61.046711 32.516430 4.810194 +v 46.908474 33.535702 10.719367 +v 56.492821 33.535702 8.875499 +v 56.404926 33.723171 8.802396 +v 71.420784 31.958406 5.801417 +v 60.925636 31.958406 7.820507 +v 46.846027 31.798721 10.394764 +v 61.272781 34.524712 5.985285 +v 61.256676 34.524712 5.988383 +v 55.360161 34.496571 7.011404 +v 61.312759 34.574272 6.279903 +v 71.148788 34.574272 4.387615 +v 61.273930 34.548199 6.078078 +v 55.453011 34.574272 7.407220 +v 55.414185 34.548199 7.205395 +v 46.419884 34.198410 8.179700 +v 71.035652 34.420193 3.799536 +v 55.323772 34.420193 6.822240 +v 46.451553 34.320305 8.344303 +v 46.309429 33.133568 7.605552 +v 55.147362 33.133568 5.905281 +v 61.075348 33.721729 4.959048 +v 46.345459 33.721729 7.792832 +v 61.274227 31.306643 6.079606 +v 55.414478 31.306643 7.206923 +v 61.181690 31.534010 5.511811 +v 55.289734 31.534010 6.645325 +v 70.941391 31.797485 3.309555 +v 70.969948 31.655691 3.457995 +v 46.391571 31.797485 8.032529 +v 55.258064 31.655691 6.480699 +v 61.235847 31.392776 5.793306 +v 71.072342 31.358046 3.990242 +v 46.522526 31.358046 8.713217 +v 55.376564 31.358046 7.009848 +v 61.274078 31.280785 6.288991 +v 55.418308 31.304075 7.226835 +v 46.560440 31.306643 8.910293 +v 70.895439 32.131989 3.070702 +v 46.366615 31.957125 7.902797 +v 46.345619 32.131989 7.793677 +v 55.183556 32.131989 6.093406 +v 70.856804 32.926773 2.869901 +v 46.309456 32.719955 7.605683 +v 56.676979 32.928387 8.958290 +v 46.927944 33.135204 10.820560 +v 71.470390 33.338730 6.059290 +v 60.891953 33.535702 8.029180 +v 46.920574 33.338730 10.782265 +v 60.946449 33.723171 7.928683 +v 71.367081 34.199467 5.522300 +v 46.845825 34.057674 10.393715 +v 61.258160 34.420887 7.111399 +v 71.335411 34.321148 5.357674 +v 55.812225 34.420887 8.159105 +v 46.785595 34.321148 10.080648 +v 71.441757 32.133430 5.910437 +v 56.384117 31.958406 8.694220 +v 56.476284 32.133430 8.789543 +v 71.470482 32.518005 6.059753 +v 60.814278 32.320908 8.044650 +v 46.920666 32.518005 10.782727 +v 56.570694 32.320908 8.861045 +v 60.751659 32.928387 8.174390 +v 71.335663 31.534855 5.358966 +v 61.176445 31.434967 7.128626 +v 46.751560 31.434967 9.903733 +v 55.776115 31.358620 7.971396 +v 71.188240 31.280876 4.592679 +v 61.270115 31.280876 6.500761 +v 61.266422 31.306923 6.710764 +v 56.582695 33.338730 8.923434 +v 71.420601 33.898132 5.800478 +v 61.088646 34.057674 7.653634 +v 71.395645 34.057674 5.670740 +v 61.060886 33.999672 7.707832 +v 46.817268 34.199467 10.245275 +v 56.036705 34.199467 8.471608 +v 55.928535 34.321148 8.321699 +v 71.264687 34.497074 4.990050 +v 55.613091 34.548508 7.796787 +v 46.676956 34.548508 9.515950 +v 61.303711 34.490608 6.923106 +v 55.693523 34.497074 7.985682 +v 71.187935 34.574375 4.591091 +v 46.638119 34.574375 9.314066 +v 71.458389 32.320908 5.996901 +v 60.868248 32.168861 7.961548 +v 46.908573 32.320908 10.719875 +v 71.477783 32.721592 6.097717 +v 60.770199 32.518005 8.118308 +v 56.674541 32.721592 8.945614 +v 56.638954 32.518005 8.913091 +v 61.266220 31.311365 6.728227 +v 55.655907 31.306923 7.790134 +v 46.677250 31.306923 9.517477 +v 55.662575 31.311365 7.806274 +v 45.754868 31.906664 4.722975 +v 46.792843 27.567686 6.719300 +v 70.601196 28.748648 1.541240 +v 60.484760 31.906664 1.889190 +v 54.592804 31.906664 3.022704 +v 51.725163 32.743256 35.756310 +v 76.274979 32.743256 31.033335 +v 75.544533 25.600410 33.281940 +v 52.123806 26.241262 37.828434 +v 72.279839 27.965517 28.017206 +v 71.904739 28.001427 27.503540 +v 71.559708 28.020782 27.254135 +v 71.366455 28.028603 27.163761 +v 70.953598 28.039904 27.058821 +v 70.527489 28.044874 27.059690 +v 67.170486 28.113617 26.584072 +v 69.045937 27.965517 28.639355 +v 69.125305 27.990200 28.221403 +v 69.203674 28.001427 28.023180 +v 69.306305 28.011675 27.836245 +v 69.577461 28.028603 27.507933 +v 69.921913 28.039904 27.257298 +v 69.741806 28.035006 27.371834 +v 70.114883 28.043211 27.166206 +v 72.439278 27.912836 28.845966 +v 72.412743 27.900188 29.057392 +v 72.359901 27.888153 29.263918 +v 72.178909 27.866678 29.649078 +v 71.743401 27.843346 30.113489 +v 71.370232 27.835142 30.319136 +v 71.167282 27.833479 30.385315 +v 69.073219 27.939177 29.063824 +v 68.226372 27.764736 32.072548 +v 69.205376 27.912836 29.468115 +v 70.531616 27.838449 30.426502 +v 70.744606 27.835142 30.439495 +v 70.321869 27.843346 30.386969 +v 69.925507 27.857569 30.231188 +v 69.745140 27.866678 30.117294 +v 69.580475 27.876926 29.981783 +v 69.434158 27.888153 29.826784 +v 67.195198 30.209538 26.712547 +v 71.908768 30.209538 25.805735 +v 72.964653 29.860657 31.294212 +v 72.943474 28.064154 31.184092 +v 68.251091 29.860657 32.201023 +v 68.569992 30.209538 26.448061 +v 71.887581 28.413034 25.695614 +v 57.682102 27.939177 31.255283 +v 57.321167 27.990200 30.492323 +v 57.174850 28.001427 30.337324 +v 57.010181 28.011675 30.201813 +v 56.829819 28.020782 30.087919 +v 56.433453 28.035006 29.932138 +v 55.797596 28.044874 29.893476 +v 55.588039 28.044874 29.933792 +v 55.385090 28.043211 29.999971 +v 54.316307 27.952452 31.686220 +v 54.316055 27.965517 31.473139 +v 54.395416 27.990200 31.055189 +v 54.576412 28.011675 30.670029 +v 54.701645 28.020782 30.497345 +v 55.011921 28.035006 30.205618 +v 55.192024 28.039904 30.091084 +v 57.709126 27.925900 31.466671 +v 57.709385 27.912836 31.679752 +v 57.630016 27.888153 32.097702 +v 57.551651 27.876926 32.295929 +v 59.584839 27.764736 33.735035 +v 57.449020 27.866678 32.482864 +v 56.640343 27.835142 33.152920 +v 56.437393 27.833479 33.219101 +v 56.227837 27.833479 33.259415 +v 54.343330 27.939177 31.897608 +v 50.574806 28.113617 29.776804 +v 54.578556 27.900188 32.488392 +v 55.015247 27.866678 32.951080 +v 58.234764 29.860657 34.127998 +v 53.840099 30.209538 29.281845 +v 50.599525 30.209538 29.905277 +v 53.818913 28.413034 29.171724 +v 58.528950 28.113617 28.246559 +v 57.157688 28.413034 28.529400 +v 58.553665 30.209538 28.375034 +v 58.213581 28.064154 34.017876 +v 59.609554 29.860657 33.863510 +v 51.655415 29.860657 35.393753 +v 69.604698 28.064154 31.826416 +v 72.962326 28.057924 31.282099 +v 69.623550 28.057924 31.924423 +v 69.104996 30.633932 29.229004 +v 68.780930 30.440424 27.544506 +v 69.422005 30.228603 30.876797 +v 69.625877 29.860657 31.936537 +v 68.548805 28.413034 26.337940 +v 69.648270 30.153843 32.052898 +v 72.760780 30.228603 30.234472 +v 71.893440 30.515184 25.726080 +v 68.554665 30.515184 26.368404 +v 68.529945 28.419264 26.239931 +v 71.868721 28.419264 25.597607 +v 72.119705 30.440424 26.902182 +v 72.443771 30.633932 28.586679 +v 72.987045 30.153843 31.410572 +v 72.764313 30.528021 30.252825 +v 72.471054 30.607592 29.011148 +v 72.470795 30.620655 28.798069 +v 72.391685 30.582907 29.429100 +v 72.210686 30.561432 29.814259 +v 72.313316 30.571682 29.627323 +v 71.775185 30.538101 30.278669 +v 71.595078 30.533205 30.393206 +v 69.425537 30.528021 30.895151 +v 69.776917 30.561432 30.282476 +v 69.340240 30.594944 29.819784 +v 72.123238 30.739841 26.920534 +v 72.390434 30.647207 28.380356 +v 72.208527 30.672918 27.995901 +v 71.771851 30.706430 27.533209 +v 71.398232 30.723356 27.328941 +v 71.195122 30.729761 27.263535 +v 70.772285 30.737967 27.211027 +v 68.784454 30.739841 27.562859 +v 69.773590 30.729761 27.537014 +v 69.609238 30.723356 27.673115 +v 69.235451 30.696182 28.188360 +v 69.338081 30.706430 28.001427 +v 69.077972 30.647207 29.017616 +v 58.232433 28.057924 34.115883 +v 54.051037 30.440424 30.378290 +v 53.824776 30.515184 29.202190 +v 53.800060 28.419264 29.073715 +v 54.692116 30.228603 33.710583 +v 54.893661 28.057924 34.758209 +v 54.895992 29.860657 34.770321 +v 54.918377 30.153843 34.886681 +v 54.874805 28.064154 34.660202 +v 58.257153 30.153843 34.244358 +v 57.389812 30.440424 29.735966 +v 57.163551 30.515184 28.559864 +v 57.138832 28.419264 28.431391 +v 57.178875 30.209538 28.639521 +v 57.661793 30.582907 32.262886 +v 57.583431 30.571682 32.461109 +v 57.355564 30.552324 32.820728 +v 58.034420 30.528021 33.086609 +v 56.672123 30.529896 33.318100 +v 54.695644 30.528021 33.728935 +v 55.420643 30.544506 33.320526 +v 55.047028 30.561432 33.116261 +v 54.736042 30.582907 32.825748 +v 54.882362 30.571682 32.980751 +v 54.507275 30.607592 32.467079 +v 54.375107 30.633932 32.062790 +v 57.581722 30.660273 31.016172 +v 55.829376 30.739630 30.058657 +v 57.393345 30.739841 29.754320 +v 55.223801 30.734657 30.256264 +v 54.374340 30.672918 31.426899 +v 54.054569 30.739841 30.396645 +v 54.348083 30.647207 31.851400 +v 58.030888 30.228603 33.068256 +v 72.358658 27.952452 28.215174 +v 72.411995 27.939177 28.421499 +v 69.126556 27.925900 29.270149 +v 69.067329 27.440147 29.033234 +v 71.161400 27.334450 30.354727 +v 70.957726 27.833479 30.425631 +v 70.951836 27.334450 30.395042 +v 69.919617 27.358541 30.200598 +v 69.739250 27.367649 30.086706 +v 69.780449 30.860849 30.300829 +v 69.431534 28.020782 27.663559 +v 69.777115 31.029179 27.555368 +v 69.302582 27.401161 29.624014 +v 69.469467 30.882324 30.010319 +v 70.947708 27.540874 27.028231 +v 71.157455 27.535978 27.067764 +v 70.985374 30.734657 27.224001 +v 70.988907 31.034075 27.242355 +v 69.072472 27.978165 28.427931 +v 69.157089 30.684956 28.386585 +v 69.104248 30.672918 28.593111 +v 69.735924 27.535978 27.341244 +v 69.953697 30.734657 27.422480 +v 70.150192 31.037384 27.349741 +v 71.907753 27.849751 29.977388 +v 70.315987 27.344318 30.356379 +v 70.563393 30.533205 30.591682 +v 70.154068 30.843924 30.505096 +v 69.960815 30.851742 30.414722 +v 69.957283 30.552324 30.396368 +v 69.199493 27.413807 29.437527 +v 69.308464 27.900188 29.654604 +v 72.311615 30.660273 28.182388 +v 71.740074 28.011675 27.368027 +v 71.734192 27.512646 27.337439 +v 71.591484 30.715538 27.419315 +v 71.898857 27.502398 27.472950 +v 72.045174 27.491171 27.627949 +v 72.051056 27.990200 27.658539 +v 71.936516 30.696182 27.668720 +v 72.082832 30.684956 27.823719 +v 70.734627 27.544182 27.015257 +v 70.740509 28.043211 27.045845 +v 70.775818 31.037384 27.229380 +v 70.317932 28.044874 27.100006 +v 70.349709 30.739630 27.265188 +v 70.559265 30.739630 27.224873 +v 71.163345 28.035006 27.098354 +v 71.401764 31.022774 27.347296 +v 69.040314 27.453424 28.821846 +v 69.081505 30.946625 29.035971 +v 69.160614 30.984373 28.404938 +v 69.040054 27.466488 28.608767 +v 69.077713 30.660273 28.804537 +v 69.046196 27.952452 28.852436 +v 69.463310 30.715538 27.828741 +v 69.466843 31.014956 27.847094 +v 69.612770 31.022774 27.691467 +v 70.108994 27.544182 27.135616 +v 70.146660 30.737967 27.331387 +v 71.402016 30.529896 30.484316 +v 71.202591 30.827650 30.568850 +v 71.199059 30.528233 30.550497 +v 71.778709 30.837519 30.297024 +v 72.047791 27.358541 29.791174 +v 72.085457 30.552324 29.986944 +v 72.088989 30.851742 30.005297 +v 71.943062 30.843924 30.160923 +v 71.939529 30.544506 30.142570 +v 72.173027 27.367649 29.618488 +v 72.316849 30.871098 29.645678 +v 72.444519 30.594944 29.222574 +v 70.118759 27.849751 30.321562 +v 70.112869 27.350721 30.290972 +v 70.353645 30.538101 30.552149 +v 70.150536 30.544506 30.486742 +v 70.566925 30.832623 30.610037 +v 70.989502 30.528233 30.590813 +v 70.776382 30.529896 30.604677 +v 69.465935 30.582907 29.991964 +v 69.574585 27.377897 29.951195 +v 69.612251 30.571682 30.146965 +v 69.120674 27.426872 29.239559 +v 69.158333 30.620655 29.435329 +v 69.240685 30.907009 29.651649 +v 69.237152 30.607592 29.633297 +v 72.170860 27.479136 27.800131 +v 72.176750 27.978165 27.830719 +v 72.212059 30.972336 28.014254 +v 71.775383 31.005848 27.551561 +v 70.562798 31.039047 27.243225 +v 71.563293 27.838449 30.228024 +v 71.598602 30.832623 30.411558 +v 72.053680 27.857569 29.821762 +v 72.275658 27.377897 29.431553 +v 72.281540 27.876926 29.462143 +v 72.439018 27.925900 28.632887 +v 57.622879 27.453424 31.018370 +v 57.676220 27.440147 31.224695 +v 54.396667 27.925900 32.103931 +v 56.469170 30.528233 33.384281 +v 56.263145 30.827650 33.442951 +v 56.259613 30.528233 33.424599 +v 55.189728 27.358541 33.034382 +v 55.227390 30.552324 33.230152 +v 54.736954 31.014956 30.680880 +v 55.006035 27.535978 30.175030 +v 55.227333 31.034075 30.274618 +v 55.043697 30.729761 30.370800 +v 54.704266 27.888153 32.660568 +v 54.610332 30.594944 32.653572 +v 56.223705 28.039904 29.892605 +v 56.217823 27.540874 29.862017 +v 56.427567 27.535978 29.901550 +v 56.468761 31.029179 30.115673 +v 56.255486 30.734657 30.057787 +v 56.465229 30.729761 30.097321 +v 56.630680 27.529573 29.966957 +v 54.342564 27.978165 31.261719 +v 54.336678 27.479136 31.231129 +v 54.427197 30.684956 31.220369 +v 54.847572 28.028603 30.341719 +v 54.841686 27.529573 30.311129 +v 54.879349 30.723356 30.506899 +v 55.186138 27.540874 30.060493 +v 55.379204 27.544182 29.969383 +v 55.416866 30.737967 30.165154 +v 57.013512 27.843346 32.947273 +v 57.213169 30.843924 32.994709 +v 57.048820 30.837519 33.130810 +v 57.209641 30.544506 32.976357 +v 55.586098 27.344318 33.190163 +v 55.833504 30.533205 33.425468 +v 55.627289 30.837519 33.404289 +v 55.623760 30.538101 33.385933 +v 55.195614 27.857569 33.064972 +v 55.230923 30.851742 33.248508 +v 54.469612 27.413807 32.271309 +v 54.510807 30.907009 32.485432 +v 57.549946 27.965517 30.850992 +v 57.628765 27.952452 31.048958 +v 57.660542 30.647207 31.214140 +v 57.585255 30.959690 31.034527 +v 57.356476 30.984373 30.675858 +v 57.478638 30.672918 30.829685 +v 56.861599 30.715538 30.253101 +v 57.041962 30.706430 30.366993 +v 57.210159 30.995600 30.520859 +v 57.352943 30.684956 30.657505 +v 55.619820 30.739630 30.098972 +v 56.636566 28.028603 29.997545 +v 56.668343 30.723356 30.162727 +v 54.347836 30.660273 31.638319 +v 54.467896 27.502398 30.826376 +v 54.389534 27.491171 31.024599 +v 54.505558 30.696182 31.022146 +v 54.473782 28.001427 30.856964 +v 54.608192 30.706430 30.835211 +v 54.310173 27.466488 31.442549 +v 54.351364 30.959690 31.656673 +v 54.695759 27.521755 30.466755 +v 54.733421 30.715538 30.662525 +v 55.623348 31.039047 30.117327 +v 57.713882 30.633932 31.420465 +v 57.740902 30.620655 31.631853 +v 56.431507 27.334450 33.188511 +v 56.833408 27.838449 33.061810 +v 57.045292 30.538101 33.112453 +v 56.865185 30.533205 33.226990 +v 57.177860 27.849751 32.811172 +v 57.359097 30.851742 32.839081 +v 57.682850 27.900188 31.891176 +v 57.714626 30.594944 32.056358 +v 57.676964 27.401161 31.860588 +v 57.744694 30.907009 31.863287 +v 57.718159 30.894361 32.074711 +v 55.591980 27.843346 33.220753 +v 55.388866 27.849751 33.155346 +v 55.424175 30.843924 33.338882 +v 55.801727 27.838449 33.260288 +v 56.014816 27.835142 33.273262 +v 56.050125 30.829313 33.456795 +v 56.046593 30.529896 33.438442 +v 54.850582 27.876926 32.815567 +v 54.844696 27.377897 32.784981 +v 55.050560 30.860849 33.134613 +v 54.475498 27.912836 32.301899 +v 54.428448 30.620655 32.269115 +v 57.440971 27.479136 30.633915 +v 57.446857 27.978165 30.664505 +v 57.206627 30.696182 30.502504 +v 55.791714 27.545847 29.862886 +v 56.010616 28.043211 29.879631 +v 56.004734 27.544182 29.849041 +v 56.045925 31.037384 30.063166 +v 56.042397 30.737967 30.044811 +v 56.675652 30.829313 33.336456 +v 57.323788 27.857569 32.655548 +v 57.317902 27.358541 32.624958 +v 57.480797 30.561432 32.648045 +v 57.484329 30.860849 32.666397 +v 57.545769 27.377897 32.265339 +v 57.703499 27.413807 31.649162 +v 57.741161 30.607592 31.844933 +v 73.133041 25.948370 28.069304 +v 68.864822 26.065239 26.983849 +v 72.354019 27.389124 29.233330 +v 72.406853 27.401161 29.026802 +v 73.155472 25.886189 29.079416 +v 72.433388 27.413807 28.815378 +v 72.352768 27.453424 28.184586 +v 71.360573 27.529573 27.133171 +v 71.414162 26.080296 26.247744 +v 71.026947 26.090002 26.163887 +v 71.110840 26.088047 26.179663 +v 72.418747 25.812611 30.421471 +v 73.194214 25.904634 28.771019 +v 72.678169 26.012682 27.107624 +v 72.920380 25.986547 27.487391 +v 71.983307 26.058466 26.494383 +v 69.803406 26.094831 26.320511 +v 70.099487 26.097260 26.223911 +v 70.521606 27.545847 27.029102 +v 70.312042 27.545847 27.069418 +v 70.405281 26.097260 26.165081 +v 68.272964 25.948370 29.004301 +v 68.243927 25.981487 28.469589 +v 68.442665 26.029385 27.649981 +v 68.359734 26.017492 27.859955 +v 69.119423 27.491171 28.190815 +v 70.525734 27.339420 30.395912 +v 70.496414 25.794794 31.081974 +v 71.032852 25.788862 31.075527 +v 68.517754 25.899576 29.753220 +v 68.305092 25.937754 29.171307 +v 72.244850 25.805826 30.565607 +v 72.179062 25.803261 30.620136 +v 72.814415 25.837296 29.942667 +v 71.901863 27.350721 29.946800 +v 72.964188 25.852247 29.669930 +v 73.099586 25.873442 29.298115 +v 73.078400 25.868631 29.380653 +v 72.414482 26.033876 26.812588 +v 72.348557 26.037975 26.758394 +v 72.472954 26.029385 26.874619 +v 72.627869 26.017492 27.038837 +v 72.174210 26.048828 26.614885 +v 71.332840 26.082863 26.221514 +v 71.553825 27.521755 27.223545 +v 69.449699 26.088047 26.499239 +v 69.916031 27.540874 27.226709 +v 69.884560 26.095497 26.294022 +v 70.941620 26.091330 26.158655 +v 68.244370 25.962425 28.780510 +v 69.425652 27.521755 27.632971 +v 68.473946 26.033876 27.570681 +v 69.197784 27.502398 27.992590 +v 68.282661 25.999935 28.161194 +v 69.066589 27.479136 28.397341 +v 70.738724 27.336113 30.408907 +v 69.428268 27.389124 29.796194 +v 68.444992 25.909863 29.599417 +v 71.364349 27.336113 30.288546 +v 71.634727 25.791292 30.920099 +v 71.839088 25.794794 30.823664 +v 71.557411 27.339420 30.197435 +v 71.916382 25.796120 30.787148 +v 71.737518 27.344318 30.082899 +v 73.193764 25.923698 28.460100 +v 72.406113 27.440147 28.390909 +v 73.154411 25.943062 28.151785 +v 72.433128 27.426872 28.602299 +v 72.961609 25.981487 27.561985 +v 72.273949 27.466488 27.986618 +v 69.193283 26.080296 26.675001 +v 69.571579 27.529573 27.477345 +v 69.019287 26.073511 26.819159 +v 69.300415 27.512646 27.805655 +v 70.722069 25.791292 31.095680 +v 72.573311 25.820885 30.256760 +v 72.681854 25.827658 30.125401 +v 69.259071 26.082863 26.620474 +v 58.424519 25.943062 30.985571 +v 57.258545 25.798077 33.575157 +v 55.597404 25.798077 33.894730 +v 58.348507 25.868631 32.214439 +v 58.453705 25.899576 31.689375 +v 57.544060 27.466488 30.820402 +v 58.346668 25.962425 30.684662 +v 56.602951 26.082863 29.055300 +v 56.380947 26.088047 29.013447 +v 55.369595 26.097260 29.057695 +v 55.382980 27.350721 33.124756 +v 57.171974 27.350721 32.780586 +v 58.190487 25.986547 30.321175 +v 57.004299 27.512646 30.171223 +v 57.253414 26.058466 29.328167 +v 55.073513 26.094831 29.154295 +v 53.744095 26.033876 30.404459 +v 53.608658 26.012682 30.776279 +v 53.629807 26.017492 30.693748 +v 53.896976 25.886189 32.784428 +v 54.337444 27.440147 31.867020 +v 54.310421 27.453424 31.655630 +v 53.543034 25.948370 31.838093 +v 53.575165 25.937754 32.005100 +v 54.390781 27.426872 32.073345 +v 53.631645 25.923698 32.223526 +v 57.449173 25.803261 33.453922 +v 57.007629 27.344318 32.916683 +v 57.443134 27.367649 32.452274 +v 58.084530 25.837296 32.776451 +v 57.624130 27.389124 32.067116 +v 58.425587 25.886189 31.913200 +v 58.464214 25.909863 31.519552 +v 57.315281 27.491171 30.461735 +v 57.743065 26.029385 29.708405 +v 57.168964 27.502398 30.306734 +v 56.823933 27.521755 30.057331 +v 55.154671 26.095497 29.127806 +v 55.582157 27.545847 29.903202 +v 56.211830 26.091330 28.992420 +v 56.297058 26.090002 28.997673 +v 55.900986 26.095497 28.984228 +v 53.893787 26.048828 30.131733 +v 53.785137 26.037975 30.329685 +v 54.134899 26.065239 29.817640 +v 53.524647 25.986547 31.218805 +v 55.795841 27.339420 33.229698 +v 53.787865 25.899576 32.587002 +v 54.572670 27.401161 32.457802 +v 54.698380 27.389124 32.629978 +v 54.030071 25.873442 32.966770 +v 54.235268 25.856739 33.199780 +v 54.293797 25.852247 33.261799 +v 55.009365 27.367649 32.920490 +v 54.724979 25.827658 33.580006 +v 53.715111 25.909863 32.433201 +v 53.746639 25.904634 32.512405 +v 53.553837 25.943062 31.922609 +v 57.901814 25.824013 33.028290 +v 56.634460 27.336113 33.122330 +v 57.186497 25.796120 33.620930 +v 56.827522 27.339420 33.031219 +v 58.234299 25.852247 32.503712 +v 57.703239 27.426872 31.436083 +v 58.231724 25.981487 30.395771 +v 57.897980 26.017492 29.872623 +v 54.289398 26.073511 29.652943 +v 54.570530 27.512646 30.639441 +v 56.221951 27.334450 33.228828 +v 56.008930 27.336113 33.242672 +v 55.992176 25.791292 33.929466 +v 57.843426 25.820885 33.090546 +v 55.294083 25.805826 33.826653 +v 72.689682 32.936100 28.577045 +v 69.957222 31.034075 27.440832 +v 69.108528 30.933350 29.247358 +v 68.913231 32.922829 29.520058 +v 68.855606 32.944691 29.174501 +v 68.873070 32.936100 29.311298 +v 72.655136 32.944691 28.443537 +v 69.657249 32.846298 30.625416 +v 72.717247 32.884747 29.409529 +v 69.370560 33.026978 27.733027 +v 69.653427 33.039387 27.476149 +v 72.393967 30.946625 28.398708 +v 69.237755 32.876957 30.205971 +v 72.448051 30.894361 29.240927 +v 69.547142 33.035244 27.564184 +v 69.770012 33.042557 27.401987 +v 70.803802 32.810127 30.994938 +v 72.447304 30.933350 28.605034 +v 72.474327 30.920073 28.816422 +v 72.656609 32.870937 29.646420 +v 71.832893 32.816372 30.695118 +v 69.081245 30.959690 28.822889 +v 68.855453 32.953144 29.036612 +v 68.886566 32.929462 29.416945 +v 69.238983 30.995600 28.206715 +v 69.234924 33.018517 27.897144 +v 70.353241 31.039047 27.283541 +v 70.314301 33.050713 27.164234 +v 72.134529 33.000874 27.627140 +v 71.415482 33.035244 27.204746 +v 70.454575 32.816372 30.960283 +v 70.357178 30.837519 30.570503 +v 69.864136 32.835857 30.755957 +v 69.468384 32.858055 30.469965 +v 69.038193 32.899254 29.880606 +v 69.343773 30.894361 29.838139 +v 72.474586 30.907009 29.029501 +v 72.747452 32.905785 29.060493 +v 72.605949 32.863670 29.774776 +v 72.214218 30.860849 29.832613 +v 72.566765 32.858055 29.873888 +v 72.395218 30.882324 29.447453 +v 72.682976 32.876957 29.543167 +v 69.107780 30.972336 28.611465 +v 69.341614 31.005848 28.019779 +v 69.297539 33.023071 27.810801 +v 70.661209 33.049877 27.111113 +v 71.198654 31.029179 27.281889 +v 70.779915 30.829313 30.623030 +v 69.615784 30.871098 30.165318 +v 72.137970 32.826893 30.464764 +v 71.949486 32.819542 30.620956 +v 72.055763 32.823685 30.532921 +v 72.367989 32.840412 30.199961 +v 72.086365 30.984373 27.842073 +v 71.595016 31.014956 27.437670 +v 71.738770 33.023071 27.341148 +v 71.945656 33.012627 27.471689 +v 71.043518 33.045006 27.117043 +v 71.148338 33.042557 27.136820 +v 69.161865 30.920073 29.453684 +v 70.559395 32.813923 30.980062 +v 69.550751 32.852936 30.537661 +v 71.405540 30.829313 30.502670 +v 71.646355 32.812271 30.797882 +v 71.390076 32.809052 30.899731 +v 71.048218 32.808216 30.979116 +v 72.305374 32.835857 30.286304 +v 72.315147 30.959690 28.200741 +v 71.940048 30.995600 27.687075 +v 70.941696 32.809052 30.985991 +v 70.993034 30.827650 30.609165 +v 54.357590 33.005993 30.963581 +v 54.156677 32.929462 32.250729 +v 54.378639 30.933350 32.081142 +v 54.431976 30.920073 32.287468 +v 57.635265 32.981972 30.724920 +v 54.143177 32.936100 32.145084 +v 57.874226 32.953144 31.149218 +v 57.987358 32.884747 32.243313 +v 54.817253 33.035244 30.397968 +v 55.047230 31.029179 30.389153 +v 56.685593 33.035244 30.038530 +v 54.426495 32.884747 32.928364 +v 58.004532 32.892925 32.106560 +v 54.189991 32.981972 31.387732 +v 54.138645 32.966003 31.658104 +v 55.040123 33.042557 30.235771 +v 55.584408 33.050713 29.998020 +v 55.824802 33.050713 29.951773 +v 55.832905 31.039047 30.077011 +v 56.211807 32.809052 33.819775 +v 54.739574 30.882324 32.844105 +v 54.570660 32.870937 33.125854 +v 57.717411 30.933350 31.438818 +v 57.744434 30.920073 31.650206 +v 54.125698 32.944691 32.008289 +v 54.351616 30.946625 31.869755 +v 54.216408 32.987988 31.284470 +v 54.267094 32.995258 31.156109 +v 54.430725 30.984373 31.238724 +v 54.505032 33.018517 30.730928 +v 54.611721 31.005848 30.853563 +v 54.882881 31.022774 30.525253 +v 56.069214 33.048801 29.935953 +v 56.671875 31.022774 30.181080 +v 55.134247 32.835857 33.589741 +v 54.885891 30.871098 32.999104 +v 54.820824 32.852936 33.371452 +v 54.613865 30.894361 32.671925 +v 54.359818 32.892925 32.807743 +v 54.308266 32.899254 32.714397 +v 58.017681 32.899254 32.000771 +v 56.660187 32.809052 33.733517 +v 57.103004 32.816372 33.528904 +v 57.785500 32.852936 32.801102 +v 57.665325 30.882324 32.281239 +v 57.586960 30.871098 32.479462 +v 57.836872 32.858055 32.707672 +v 57.953087 32.876957 32.376953 +v 57.926720 32.870937 32.480206 +v 54.125408 32.959675 31.763912 +v 54.377872 30.972336 31.445253 +v 54.509090 30.995600 31.040499 +v 54.567650 33.023071 30.644585 +v 55.420399 31.037384 30.183506 +v 57.572468 32.987988 30.638821 +v 57.045490 31.005848 30.385347 +v 56.259014 31.034075 30.076139 +v 55.931320 33.049877 29.944899 +v 54.217865 32.914238 32.487358 +v 55.837036 30.832623 33.443821 +v 55.936039 32.812271 33.820282 +v 55.829506 32.813923 33.813847 +v 55.230915 32.831951 33.634872 +v 54.738510 32.858055 33.303745 +v 57.719166 32.846298 32.922104 +v 57.477795 32.995258 30.538424 +v 57.783360 32.966003 30.956921 +v 57.482166 30.972336 30.848040 +v 56.912216 33.026978 30.129803 +v 56.865128 31.014956 30.271454 +v 57.008881 33.023071 30.174932 +v 56.313625 33.045006 29.950829 +v 56.418449 33.042557 29.970606 +v 55.457539 32.823685 33.726143 +v 55.355911 32.826893 33.693352 +v 56.868717 30.832623 33.245342 +v 57.013000 32.813923 33.586163 +v 56.916462 32.812271 33.631668 +v 56.472702 30.827650 33.402634 +v 56.318329 32.808216 33.812901 +v 57.834824 32.959675 31.050283 +v 57.664074 30.946625 31.232492 +v 57.404636 33.000874 30.460924 +v 66.344604 39.697979 25.731829 +v 45.726643 38.559273 20.456078 +v 68.557976 38.559273 16.063711 +v 59.756031 34.946129 14.619476 +v 57.905457 27.120533 11.876236 +v 65.778191 34.374424 22.787634 +v 65.364769 34.357391 23.145042 +v 55.329880 34.385426 24.618225 +v 64.885788 34.768574 16.529219 +v 65.773750 35.392830 22.519989 +v 65.713318 35.397808 22.450407 +v 64.642441 35.766628 16.639479 +v 61.281570 34.189182 26.674751 +v 64.857506 34.777916 16.382204 +v 61.639938 26.055202 28.537531 +v 47.359364 27.719242 12.310676 +v 50.426838 26.801964 29.007669 +v 69.590553 27.567686 2.333402 +v 70.554688 33.050713 27.117987 +v 70.212830 33.049877 27.197374 +v 70.081459 33.048801 27.240231 +v 69.956558 33.046658 27.299223 +v 69.860016 33.045006 27.344728 +v 69.464935 33.032036 27.632341 +v 57.215771 33.012627 30.305473 +v 69.153854 33.012627 28.008785 +v 68.919930 32.981972 28.553938 +v 68.855339 32.959675 28.930120 +v 57.986450 32.929462 31.513945 +v 57.925243 32.944691 31.277321 +v 68.885658 32.974182 28.687576 +v 57.716671 32.974182 30.836304 +v 68.946297 32.987988 28.450686 +v 69.036140 33.000874 28.223217 +v 69.087517 33.005993 28.129789 +v 57.959789 32.936100 31.410831 +v 68.868484 32.966003 28.824327 +v 68.996964 32.995258 28.322329 +v 57.322266 33.005993 30.393229 +v 57.099064 33.018517 30.231880 +v 56.787216 33.032036 30.071323 +v 56.554234 33.039387 29.996216 +v 70.387024 33.147514 25.571024 +v 56.207088 33.046658 29.944391 +v 55.226669 33.046658 30.133007 +v 54.155811 32.974182 31.521353 +v 54.125526 32.953144 31.870403 +v 50.501671 33.147514 29.396635 +v 54.306274 33.000874 31.056999 +v 54.423965 33.012627 30.842569 +v 54.735046 33.032036 30.466125 +v 54.640671 33.026978 30.566811 +v 54.923534 33.039387 30.309935 +v 55.130127 33.045006 30.178513 +v 55.351574 33.048801 30.074015 +v 55.482941 33.049877 30.031158 +v 72.564713 32.959675 28.216497 +v 72.604118 32.953144 28.315432 +v 72.446564 32.974182 28.002518 +v 72.513252 32.966003 28.123137 +v 72.302361 32.987988 27.805037 +v 72.365158 32.981972 27.891134 +v 75.218338 33.092384 25.540936 +v 72.207687 32.995258 27.704639 +v 72.052155 33.005993 27.559443 +v 71.828957 33.018517 27.398094 +v 71.642105 33.026978 27.296019 +v 71.517105 33.032036 27.237537 +v 70.553871 33.092384 26.438301 +v 71.284119 33.039387 27.162430 +v 70.936981 33.046658 27.110605 +v 70.799103 33.048801 27.102167 +v 72.729843 32.922829 28.785805 +v 72.716339 32.929462 28.680161 +v 72.747299 32.914238 28.922602 +v 72.734421 32.892925 29.272776 +v 72.747574 32.899254 29.166985 +v 72.515388 32.852936 29.967316 +v 72.449051 32.846298 30.088320 +v 72.232353 32.831951 30.364079 +v 71.742889 32.813923 30.752377 +v 71.521446 32.810127 30.856874 +v 71.288612 32.808216 30.932869 +v 57.575481 32.835857 33.120090 +v 57.999950 32.922829 31.619591 +v 68.947769 32.914238 29.653568 +v 58.017410 32.914238 31.756388 +v 68.998795 32.905785 29.781673 +v 58.017563 32.905785 31.894278 +v 69.089661 32.892925 29.973969 +v 69.156349 32.884747 30.094587 +v 69.300552 32.870937 30.292068 +v 69.395226 32.863670 30.392466 +v 57.876057 32.863670 32.608562 +v 57.638096 32.840412 33.033745 +v 69.773956 32.840412 30.699009 +v 69.960800 32.831951 30.801086 +v 57.502460 32.831951 33.197865 +v 70.085800 32.826893 30.859566 +v 70.187431 32.823685 30.892359 +v 57.325878 32.823685 33.366707 +v 70.318787 32.819542 30.934675 +v 57.408081 32.826893 33.298550 +v 57.219593 32.819542 33.454739 +v 70.665932 32.812271 30.986498 +v 56.791557 32.810127 33.690659 +v 56.558720 32.808216 33.766655 +v 54.507816 32.876957 33.039764 +v 54.183338 32.922829 32.353844 +v 54.268864 32.905785 32.615463 +v 56.073917 32.810127 33.828720 +v 55.724682 32.816372 33.794067 +v 55.588898 32.819542 33.768459 +v 55.044064 32.840412 33.532795 +v 54.927361 32.846298 33.459202 +v 54.665352 32.863670 33.226246 +v 47.791321 32.554409 23.248281 +v 48.639805 32.274055 27.658665 +v 47.329750 34.144943 21.907705 +v 47.370945 37.638145 22.121828 +v 48.502254 37.264343 28.002338 +v 48.698654 37.264343 27.964556 +v 47.567341 37.638145 22.084045 +v 48.443409 32.274055 27.696447 +v 69.065254 32.274055 23.729149 +v 67.933945 32.647858 17.848639 +v 69.124100 37.264343 24.035040 +v 69.320503 37.264343 23.997257 +v 68.189186 37.638145 18.116745 +v 69.261650 32.274055 23.691366 +v 67.992790 37.638145 18.154530 +v 68.130341 32.647858 17.810854 +v 59.409355 27.286600 8.877703 +v 55.492302 31.280785 7.401309 +v 55.574558 31.280876 7.596490 +v 55.372860 27.449877 6.990583 +v 55.496300 37.917774 7.632237 +v 55.570572 37.917866 7.808144 +v 55.531143 34.574375 7.603197 +v 55.739853 37.864441 8.207960 +v 55.700066 34.490608 8.001154 +v 61.077744 27.375963 7.098868 +v 61.134232 31.534855 7.321551 +v 61.127060 31.557924 7.355211 +v 61.165653 34.199467 7.484885 +v 61.200024 37.745140 7.734471 +v 56.505417 33.498474 8.885402 +v 56.646233 33.135204 8.950924 +v 56.489738 32.168861 8.803900 +v 56.142513 31.656748 8.452569 +v 56.272274 31.798721 8.581312 +v 56.432846 27.344353 8.508171 +v 55.975113 27.375963 8.080529 +v 56.553570 37.581589 9.135686 +v 56.316475 37.683620 8.918522 +v 56.170826 34.057674 8.599740 +v 56.297031 33.898132 8.710000 +v 55.313728 27.469414 6.683230 +v 55.359997 31.392776 6.923721 +v 55.436939 37.917618 7.323677 +v 58.218220 37.150532 9.707748 +v 57.562119 27.298674 9.036112 +v 57.679352 37.239975 9.645487 +v 56.969593 27.318205 8.831476 +v 58.184422 27.286600 9.113360 +v 55.894501 31.434967 8.144784 +v 56.005283 31.534855 8.308274 +v 56.024429 31.557924 8.336872 +v 56.097393 37.745140 8.716132 +v 56.055264 34.172634 8.497147 +v 58.924877 37.087070 9.660819 +v 60.630196 37.452759 8.683191 +v 60.826279 33.338730 8.107039 +v 60.777477 33.135204 8.156141 +v 60.432880 27.318205 8.165197 +v 60.749218 32.721592 8.161714 +v 61.236313 31.358046 5.882530 +v 61.296688 37.917618 6.196359 +v 61.060333 31.656748 7.506463 +v 61.157894 34.172634 7.515486 +v 60.075809 37.239975 9.184449 +v 59.958572 27.298674 8.575074 +v 59.525467 37.132675 9.481241 +v 61.278057 31.304075 6.099518 +v 61.220230 27.411655 6.489185 +v 61.352345 37.917866 6.695827 +v 61.308647 34.548508 6.701057 +v 61.304043 34.497074 6.906312 +v 61.343494 37.864441 7.129913 +v 61.293682 37.839470 7.341565 +v 61.210480 34.321148 7.305542 +v 61.207512 34.312927 7.317633 +v 61.215351 37.760574 7.670162 +v 61.222050 31.358620 6.923690 +v 61.191738 27.404522 6.611038 +v 61.200790 31.394100 7.019108 +v 60.864689 27.350676 7.552396 +v 60.811356 27.344353 7.665818 +v 60.875416 32.133430 7.943223 +v 60.932079 37.581589 8.293333 +v 61.012363 33.898132 7.802849 +v 60.883930 33.498474 8.043050 +v 61.071674 37.683620 8.003700 +v 55.154667 33.337154 5.943245 +v 55.204365 33.896751 6.201581 +v 55.229305 34.056538 6.331215 +v 55.289486 34.320305 6.644032 +v 54.950108 37.147907 4.879951 +v 55.164299 37.682098 5.993318 +v 55.147388 32.719955 5.905413 +v 55.154755 32.516430 5.943708 +v 55.166855 32.319458 6.006606 +v 55.204548 31.957125 6.202527 +v 55.000202 27.567686 5.140342 +v 55.229507 31.797485 6.332258 +v 55.324051 31.434301 6.823695 +v 61.058716 33.534252 4.872584 +v 61.046623 33.337154 4.809731 +v 60.525677 35.376511 2.101882 +v 60.584129 35.864510 2.405715 +v 61.039345 32.719955 4.771899 +v 61.075512 32.131989 4.959892 +v 61.121464 31.797485 5.198744 +v 61.096504 31.957125 5.069013 +v 61.150021 31.655691 5.347185 +v 61.189579 27.469414 5.552814 +v 61.216007 31.434301 5.690181 +v 61.121262 34.056538 5.197701 +v 60.746132 36.778572 3.247786 +v 61.149776 34.198410 5.345915 +v 60.842064 37.147907 3.746436 +v 61.181442 34.320305 5.510518 +v 60.945972 37.450668 4.286564 +v 61.056255 37.682098 4.859804 +v 61.252117 34.496571 5.877890 +v 61.215725 34.420193 5.688726 +v 61.219742 31.392776 5.796404 +v 55.343891 31.392776 6.926820 +v 55.297623 27.469414 6.686328 +v 55.396927 34.524712 7.115701 +v 55.380825 34.524712 7.118799 +v 44.786377 31.056692 15.568623 +v 48.252270 30.884907 17.704353 +v 45.306290 30.884907 18.271109 +v 44.413383 27.719242 12.877433 +v 47.231552 28.717300 12.398706 +v 44.285572 28.717300 12.965464 +v 47.480862 26.801964 29.574427 +v 47.982811 26.201115 28.784519 +v 47.594925 32.554409 23.286064 +v 47.526150 34.144943 21.869921 +v 66.210762 37.917587 5.186333 +v 61.326370 37.917694 6.350639 +v 66.291702 37.917820 5.650479 +v 55.466621 37.917694 7.477957 +v 50.989876 37.917587 8.114577 +v 51.086922 37.917820 8.575625 +v 65.612869 36.563549 2.078520 +v 50.391983 36.563549 5.006763 +v 65.911079 37.566383 3.628589 +v 50.690193 37.566383 6.556833 +v 66.139931 37.878059 4.818144 +v 50.919044 37.878059 7.746388 +v 65.464867 35.620510 1.309204 +v 50.243980 35.620510 4.237448 +v 66.376831 37.761543 6.673365 +v 61.207687 37.752857 7.702316 +v 61.185539 37.738213 7.764773 +v 66.265823 37.683620 7.004432 +v 51.387402 37.761543 9.557081 +v 51.613319 37.683620 9.823330 +v 66.023651 37.760330 4.213748 +v 50.802765 37.760330 7.141993 +v 65.704056 36.963242 2.552516 +v 50.483173 36.963242 5.480761 +v 65.803986 37.299286 3.071905 +v 50.583092 37.299286 6.000150 +v 65.531876 36.106518 1.657485 +v 50.310982 36.106518 4.585728 +v 50.383705 39.574478 28.783466 +v 50.382244 39.198650 28.775875 +v 50.472374 38.791084 29.244370 +v 48.883270 40.194908 20.984259 +v 48.775272 39.377975 20.422897 +v 48.773239 39.002335 20.412317 +v 48.785156 38.504982 20.474270 +v 48.820061 38.038948 20.655716 +v 48.823151 35.931602 20.671766 +v 48.888027 35.566311 21.008991 +v 49.266525 35.948097 22.976391 +v 49.352234 35.734871 23.421919 +v 49.442326 35.580185 23.890221 +v 49.535370 35.486481 24.373871 +v 49.629894 35.455254 24.865189 +v 50.459534 38.169350 29.177628 +v 50.312088 37.749374 28.411201 +v 50.399311 37.490997 28.864588 +v 50.347046 35.428085 28.592913 +v 49.570679 34.457073 24.557388 +v 49.724419 35.486977 25.356527 +v 49.817448 35.581158 25.840084 +v 49.907501 35.736313 26.308191 +v 50.274094 35.108337 28.213696 +v 45.775330 33.641586 4.829321 +v 46.322777 33.435703 7.674942 +v 46.337143 33.627991 7.749600 +v 46.081108 34.636631 6.418759 +v 46.245750 30.014559 7.274553 +v 46.308212 33.030170 7.599214 +v 46.313080 33.235359 7.624534 +v 46.308224 32.823364 7.599279 +v 46.313141 32.618195 7.624831 +v 46.322872 32.417946 7.675428 +v 46.337273 32.225723 7.750277 +v 46.356117 32.044556 7.848237 +v 46.379093 31.877304 7.967663 +v 46.435966 31.594851 8.263283 +v 46.468956 31.484156 8.434780 +v 46.504318 31.396173 8.618591 +v 46.541481 31.332344 8.811754 +v 46.579857 31.293713 9.011235 +v 46.618851 31.280830 9.213915 +v 46.660690 28.046349 9.431399 +v 47.226501 28.289183 12.372464 +v 46.657837 31.293900 9.416565 +v 46.733364 31.396793 9.809153 +v 46.768700 31.484911 9.992837 +v 47.331383 31.992538 12.917617 +v 47.072456 37.301643 11.571739 +v 46.924316 32.619797 10.801710 +v 47.172447 36.966125 12.091484 +v 46.914619 32.419456 10.751301 +v 47.263699 36.566910 12.565804 +v 47.312149 31.595062 12.817638 +v 46.696209 31.332771 9.616024 +v 46.801678 31.595802 10.164242 +v 46.831772 31.727734 10.320654 +v 46.858498 31.878563 10.459578 +v 46.881451 32.045918 10.578901 +v 47.454453 35.246040 13.557343 +v 47.344788 36.110298 12.987311 +v 47.414421 35.603493 13.349266 +v 47.857475 33.019241 15.652243 +v 48.426811 31.700232 18.611607 +v 46.148113 35.122639 6.767040 +v 46.203804 35.417557 7.056528 +v 46.266029 35.673157 7.379961 +v 46.333817 35.885487 7.732326 +v 46.406101 36.051147 8.108049 +v 46.481731 36.167564 8.501171 +v 46.559532 36.232880 8.905572 +v 46.598518 36.245964 9.108223 +v 46.677254 36.233185 9.517482 +v 46.755058 36.168274 9.921923 +v 46.830723 36.052254 10.315209 +v 46.768440 34.371017 9.991465 +v 46.801430 34.260307 10.162962 +v 46.831547 34.128571 10.319494 +v 46.858307 33.977905 10.458584 +v 46.881279 33.810654 10.578010 +v 46.900124 33.629436 10.675966 +v 46.914524 33.437218 10.750816 +v 46.924259 33.236969 10.801413 +v 46.929176 33.031796 10.826964 +v 50.587166 29.161577 29.841042 +v 51.102753 27.939177 32.521042 +v 51.643059 28.812696 35.329514 +v 51.275322 26.521614 33.418053 +v 52.135628 27.243563 37.889874 +v 51.936302 30.494560 36.853813 +v 50.464256 29.974739 29.202152 +v 51.162346 31.476397 32.830795 +v 51.182911 37.878670 8.969482 +v 55.802246 37.851955 8.296991 +v 66.348701 37.878670 6.051837 +v 61.318588 37.851955 7.235739 +v 75.183838 31.650961 25.361618 +v 76.486122 30.494560 32.130836 +v 75.746658 32.917820 28.287136 +v 70.606079 34.356834 1.566617 +v 70.872589 33.435703 2.951968 +v 70.886963 33.627991 3.026626 +v 70.905762 33.809242 3.124368 +v 70.928719 33.976646 3.243695 +v 71.031548 36.167564 3.778196 +v 70.955444 34.127472 3.382618 +v 70.985535 34.259357 3.539026 +v 70.900772 35.935432 3.098455 +v 71.109344 36.232880 4.182598 +v 71.053848 34.458382 3.894118 +v 71.188080 36.246071 4.591826 +v 71.227066 36.233185 4.794507 +v 71.304871 36.168274 5.198948 +v 71.380539 36.052254 5.592235 +v 71.452850 35.886955 5.968088 +v 71.520676 35.674999 6.320664 +v 71.582954 35.419697 6.644365 +v 71.638687 35.125118 6.934073 +v 71.687012 34.795830 7.185299 +v 70.673080 34.842842 1.914898 +v 70.764267 35.242531 2.388895 +v 70.325150 33.641586 0.106346 +v 70.858025 33.030170 2.876239 +v 70.858032 32.823364 2.876305 +v 70.862953 32.618195 2.901856 +v 70.872688 32.417946 2.952453 +v 70.887085 32.225723 3.027302 +v 70.610558 31.931894 1.589911 +v 70.928909 31.877304 3.244689 +v 70.955673 31.726589 3.383775 +v 70.985779 31.594851 3.540308 +v 71.018776 31.484156 3.711806 +v 71.054138 31.396173 3.895617 +v 71.091293 31.332344 4.088780 +v 71.129669 31.293713 4.288260 +v 71.168671 31.280830 4.490941 +v 71.207657 31.293900 4.693590 +v 71.246025 31.332771 4.893049 +v 71.283180 31.396793 5.086177 +v 71.318520 31.484911 5.269862 +v 71.727051 34.438377 7.393374 +v 71.464340 33.437218 6.027841 +v 71.474075 33.236969 6.078438 +v 71.478989 33.031796 6.103990 +v 71.479004 32.824989 6.104055 +v 71.474136 32.619797 6.078735 +v 71.464432 32.419456 6.028327 +v 72.295868 35.040855 10.350090 +v 72.696762 34.592869 12.433889 +v 71.450073 32.227169 5.953669 +v 71.431274 32.045918 5.855927 +v 72.936409 33.972191 13.679592 +v 71.408310 31.878563 5.736604 +v 71.381584 31.727734 5.597680 +v 73.184525 33.452927 14.969294 +v 71.351494 31.595802 5.441268 +v 73.439575 33.038315 16.295025 +v 72.084915 30.803101 9.253565 +v 73.699982 32.730942 17.648582 +v 73.964134 32.532646 19.021666 +v 74.230423 32.444660 20.405840 +v 74.497192 32.467533 21.792480 +v 74.762794 32.601105 23.173067 +v 75.025604 32.844612 24.539139 +v 75.136978 29.161577 25.118065 +v 76.430016 27.252514 31.839178 +v 76.192871 28.812696 30.606541 +v 73.640350 27.744471 17.338646 +v 59.737595 35.153580 11.238645 +v 57.033558 37.429771 9.417791 +v 57.383957 37.323380 9.547683 +v 57.948784 37.195251 9.676618 +v 66.152237 37.301643 7.901110 +v 60.313831 37.323380 8.984024 +v 65.636391 37.150532 8.280617 +v 63.723949 36.029697 9.904218 +v 59.561989 37.141602 9.461723 +v 59.482620 37.129417 9.494074 +v 59.182327 37.106617 9.583863 +v 58.879730 37.090370 9.664869 +v 52.760487 36.957199 11.028932 +v 59.495354 36.781719 9.979478 +v 61.001877 37.632607 8.148517 +v 60.781136 37.517174 8.488262 +v 56.435020 37.632607 9.027103 +v 53.517529 37.495701 9.940955 +v 63.791565 35.515163 10.255713 +v 48.386688 27.028641 15.206619 +v 61.211090 27.459644 5.708040 +v 61.226418 27.430767 6.176226 +v 61.205986 27.408089 6.550112 +v 61.134743 27.390244 6.854953 +v 60.971214 27.363319 7.325632 +v 60.838020 27.347515 7.609107 +v 60.622116 27.331280 7.915508 +v 60.195724 27.308439 8.370136 +v 62.185196 27.013065 12.806107 +v 59.683964 27.292637 8.726389 +v 59.169296 27.284966 8.950566 +v 58.869247 27.282921 9.041628 +v 58.559311 27.284145 9.081285 +v 58.246895 27.286190 9.108051 +v 57.960625 27.218948 10.260080 +v 57.265854 27.308439 8.933794 +v 56.701218 27.331280 8.669824 +v 52.922104 27.154799 12.275953 +v 56.203979 27.360157 8.294350 +v 55.343292 27.459644 6.836906 +v 55.795853 27.393810 7.823881 +v 51.655807 27.319469 9.833126 +v 52.696091 26.140705 28.863274 +v 52.477608 26.129791 29.083374 +v 52.356964 26.119545 29.273724 +v 52.320770 26.115250 29.350765 +v 53.619232 26.015087 30.735014 +v 53.580688 26.006310 30.885633 +v 53.538681 25.993240 31.106895 +v 53.519344 25.984016 31.261089 +v 53.514099 25.978874 31.346001 +v 53.514297 25.969343 31.501465 +v 53.548435 25.945717 31.880352 +v 53.564499 25.940407 31.963856 +v 53.528740 25.955399 31.726196 +v 50.701912 26.263660 27.241035 +v 51.045235 27.518551 6.702814 +v 68.942055 26.069374 26.901505 +v 68.835625 26.063675 27.014977 +v 68.781357 26.060287 27.080654 +v 68.690002 26.053646 27.206573 +v 68.569382 26.043402 27.396919 +v 68.494499 26.035927 27.533287 +v 68.458305 26.031631 27.610331 +v 65.226677 26.045692 28.002647 +v 65.188141 26.036915 28.153267 +v 65.168770 26.027691 28.307465 +v 65.168991 26.018158 28.462925 +v 65.156036 26.200050 25.498055 +v 65.188667 26.008476 28.617083 +v 73.143723 25.945717 28.110544 +v 73.159790 25.940407 28.194046 +v 73.179466 25.930725 28.348206 +v 73.193932 25.916780 28.572933 +v 73.194153 25.907249 28.728394 +v 73.188904 25.902105 28.813305 +v 73.169533 25.892883 28.967503 +v 73.127533 25.879814 29.188766 +v 73.088989 25.871037 29.339384 +v 73.036934 25.862686 29.485641 +v 72.979828 25.854492 29.630280 +v 72.943634 25.850197 29.707321 +v 74.179474 25.718853 31.612305 +v 74.088120 25.712212 31.738224 +v 73.981644 25.706511 31.851706 +v 73.861801 25.701836 31.951038 +v 71.952408 25.797098 30.764259 +v 71.877731 25.795456 30.805405 +v 71.736908 25.793043 30.871881 +v 71.594147 25.790958 30.933344 +v 71.446106 25.789745 30.981644 +v 71.185745 25.788862 31.046112 +v 70.920059 25.789745 31.082848 +v 70.764664 25.790958 31.092922 +v 70.609238 25.793043 31.088827 +v 70.453796 25.795456 31.079348 +v 70.369240 25.797098 31.068832 +v 70.216293 25.800669 31.040020 +v 66.496857 25.701836 33.367931 +v 70.064636 25.804543 31.005981 +v 69.706635 25.816748 30.875748 +v 63.728554 25.842720 31.602112 +v 68.333328 25.930725 29.280519 +v 68.403282 25.916780 29.494576 +v 65.285072 25.989264 28.911983 +v 68.497139 25.902105 29.715921 +v 68.572334 25.892883 29.851927 +v 68.693436 25.879814 30.041809 +v 65.225098 25.961918 29.369652 +v 63.656845 25.862686 31.290215 +v 63.644615 25.854492 31.426220 +v 63.608421 25.850197 31.503262 +v 69.359375 25.832478 30.685974 +v 69.490936 25.825836 30.769009 +v 69.565697 25.822449 30.809864 +v 57.482063 25.804543 33.426659 +v 59.748825 26.008476 29.663620 +v 58.429901 25.940407 31.027832 +v 58.449577 25.930725 31.181988 +v 58.464043 25.916780 31.406719 +v 58.464268 25.907249 31.562180 +v 58.459015 25.902105 31.647091 +v 58.439644 25.892883 31.801289 +v 58.397640 25.879814 32.022552 +v 58.359100 25.871037 32.173172 +v 58.018246 25.832478 32.867821 +v 57.926888 25.825836 32.993736 +v 57.872620 25.822449 33.059418 +v 57.766144 25.816748 33.172901 +v 57.601906 25.809219 33.327324 +v 69.916443 25.809219 30.958214 +v 57.222519 25.797098 33.598045 +v 64.145676 25.600410 35.474892 +v 57.147896 25.795456 33.639179 +v 57.007069 25.793043 33.705658 +v 56.864262 25.790958 33.767128 +v 56.716221 25.789745 33.815430 +v 56.455864 25.788862 33.879898 +v 56.190170 25.789745 33.916634 +v 55.879349 25.793043 33.922611 +v 55.639351 25.797098 33.902618 +v 54.020454 25.703117 35.747246 +v 53.810631 25.710648 35.664783 +v 53.640411 25.718853 35.563675 +v 54.055202 25.871037 33.001167 +v 53.842422 25.892883 32.685715 +v 53.603405 25.930725 32.114311 +v 51.837807 25.900763 32.942802 +v 53.673378 25.916780 32.328362 +v 53.730873 25.907249 32.472801 +v 53.767250 25.902105 32.549706 +v 53.963524 25.879814 32.875599 +v 54.157799 25.862686 33.117672 +v 53.520309 25.726330 35.464821 +v 54.761059 25.825836 33.602791 +v 55.486404 25.800669 33.873802 +v 55.723911 25.795456 33.913132 +v 56.034775 25.790958 33.926708 +v 73.104797 25.955399 27.960091 +v 72.567543 26.584049 17.807671 +v 73.034851 25.969343 27.746035 +v 72.977371 25.978874 27.601589 +v 72.940994 25.984016 27.524689 +v 72.865799 25.993240 27.388683 +v 72.744690 26.006310 27.198799 +v 72.653015 26.015087 27.073231 +v 72.550415 26.023438 26.956728 +v 72.443718 26.031631 26.843603 +v 72.381516 26.035927 26.785492 +v 72.261383 26.043402 26.686640 +v 72.078758 26.053646 26.554634 +v 71.947205 26.060287 26.471600 +v 71.872437 26.063675 26.430744 +v 71.731453 26.069374 26.364870 +v 71.521645 26.076904 26.282406 +v 71.373505 26.081579 26.234629 +v 71.221840 26.085455 26.200588 +v 71.068893 26.089024 26.171776 +v 70.984283 26.090666 26.161270 +v 70.828842 26.093081 26.151791 +v 70.673470 26.095165 26.147686 +v 70.518082 26.096378 26.157763 +v 69.158974 26.211733 24.537340 +v 69.010933 26.210518 24.585640 +v 68.870102 26.208103 24.652115 +v 68.738770 26.204535 24.735622 +v 69.106285 26.076904 26.747080 +v 67.398262 26.597212 18.587399 +v 68.084305 27.217953 8.328716 +v 65.597847 27.286371 7.690868 +v 61.040871 27.518551 4.779821 +v 59.709900 26.018158 29.513165 +v 59.652428 26.027691 29.368719 +v 59.577229 26.036915 29.232714 +v 59.485558 26.045692 29.107145 +v 57.713829 26.031631 29.677387 +v 57.651630 26.035927 29.619274 +v 57.531494 26.043402 29.520424 +v 57.348866 26.053646 29.388420 +v 57.217308 26.060287 29.305386 +v 57.142548 26.063675 29.264530 +v 57.001564 26.069374 29.198654 +v 56.791756 26.076904 29.116190 +v 56.643612 26.081579 29.068413 +v 56.491951 26.085455 29.034374 +v 56.339005 26.089024 29.005560 +v 56.254444 26.090666 28.995047 +v 56.099003 26.093081 28.985567 +v 55.943581 26.095165 28.981472 +v 55.788185 26.096378 28.991547 +v 54.145035 26.208767 27.474152 +v 54.070412 26.207127 27.515287 +v 57.247070 26.200050 27.019609 +v 54.395302 26.211733 27.377625 +v 55.114090 26.095165 29.141050 +v 54.496288 26.081579 29.481522 +v 52.531845 26.133177 29.017700 +v 69.957443 35.061996 10.766566 +v 66.685387 34.773247 16.106777 +v 62.306770 34.862022 15.500840 +v 59.330925 34.960144 14.472578 +v 56.864216 34.890060 16.090534 +v 50.802345 34.965748 16.021910 +v 66.124504 34.259998 24.587757 +v 63.323170 34.273285 24.909897 +v 54.735939 34.581673 21.530956 +v 58.492126 34.145573 27.922846 +v 58.730881 34.273285 25.793377 +v 55.755035 34.371407 24.765114 +v 65.089661 42.940437 21.855305 +v 65.018768 42.916645 21.486801 +v 64.496414 37.986919 18.771645 +v 65.488335 42.255898 23.927607 +v 64.648743 36.878342 19.563429 +v 65.007645 35.985512 21.428986 +v 64.858185 42.706097 20.652138 +v 65.543083 42.016376 24.212179 +v 65.344055 42.684242 23.177662 +v 64.783478 36.370399 20.263779 +v 64.437698 39.194977 18.466452 +v 64.847763 36.210472 20.597946 +v 64.937859 36.055786 21.066250 +v 65.543564 36.880680 24.214684 +v 64.466370 40.495289 18.615473 +v 64.583458 41.648415 19.224106 +v 64.535385 41.291088 18.974182 +v 64.772522 42.492416 20.206810 +v 64.925735 42.822464 21.003244 +v 65.184174 42.909203 22.346622 +v 64.535728 37.600716 18.975994 +v 64.470230 38.336449 18.635548 +v 64.437653 39.696312 18.466190 +v 64.186829 39.258270 19.809036 +v 64.186790 39.634270 19.808865 +v 64.194595 38.982391 19.849438 +v 64.208847 38.713844 19.923496 +v 64.194504 39.910194 19.848999 +v 64.229324 38.456863 20.029991 +v 64.255753 38.215500 20.167309 +v 64.208710 40.178814 19.922775 +v 64.287674 37.993568 20.333267 +v 64.324585 37.794567 20.525131 +v 64.229149 40.435905 20.029053 +v 64.365936 37.621628 20.740044 +v 64.411057 37.477478 20.974575 +v 64.459213 37.364395 21.224934 +v 64.509689 37.284168 21.487295 +v 64.561661 37.238045 21.757450 +v 64.595924 37.226723 21.935532 +v 64.648575 37.238281 22.209225 +v 64.700546 37.284657 22.479345 +v 64.750992 37.365158 22.741570 +v 64.799149 37.478485 22.991886 +v 64.844238 37.622871 23.226246 +v 64.885536 37.796024 23.440922 +v 64.922432 37.995224 23.632694 +v 64.954315 38.217316 23.798422 +v 64.255516 40.677399 20.166105 +v 64.287399 40.899498 20.331833 +v 64.324295 41.098694 20.523603 +v 64.365593 41.271851 20.738281 +v 64.410675 41.416233 20.972641 +v 64.458832 41.529564 21.222958 +v 64.509285 41.610062 21.485182 +v 64.561249 41.656445 21.755302 +v 64.595520 41.667942 21.933407 +v 64.648163 41.656673 22.207077 +v 64.700134 41.610554 22.477234 +v 64.750610 41.530323 22.739594 +v 64.798782 41.417244 22.989952 +v 64.843903 41.273090 23.224483 +v 64.885239 41.100151 23.439394 +v 64.922157 40.901154 23.631260 +v 64.954086 40.679214 23.797218 +v 64.980499 40.437859 23.934534 +v 65.000992 40.180882 24.041031 +v 65.015236 39.912327 24.115089 +v 65.023010 39.636444 24.155491 +v 64.980682 38.458817 23.935474 +v 65.001129 38.715904 24.041752 +v 65.015320 38.984528 24.115528 +v 65.023041 39.260452 24.155663 +v 65.608368 37.246300 24.551502 +v 65.479256 36.603928 23.880411 +v 65.333633 36.188622 23.123470 +v 65.266083 36.072254 22.772364 +v 65.721588 40.558273 25.140060 +v 65.745186 40.072754 25.262705 +v 64.501190 40.961510 18.796457 +v 64.712570 42.290787 19.895197 +v 65.253967 42.838928 22.709358 +v 64.446640 38.821968 18.512901 +v 64.591431 37.195278 19.265549 +v 64.444290 40.007515 18.500713 +v 64.850441 40.316677 17.965229 +v 64.703491 36.638817 19.848001 +v 65.690636 37.933212 24.979151 +v 65.173058 35.978073 22.288807 +v 65.600388 41.699448 24.510059 +v 65.695412 40.907799 25.003962 +v 64.648254 42.014042 19.560925 +v 65.408348 42.524315 23.511829 +v 65.754913 39.261070 25.313259 +v 65.743088 38.763664 25.251778 +v 65.725449 38.399429 25.160135 +v 65.656448 37.603630 24.801426 +v 65.419312 36.402306 23.568798 +v 65.102158 35.954281 21.920303 +v 65.656097 41.294003 24.799614 +v 65.753387 39.762222 25.305311 +v 66.341087 39.574478 25.713531 +v 67.550522 43.439468 21.413597 +v 67.034348 42.356140 18.730499 +v 67.272919 43.158401 19.970592 +v 67.187241 42.944725 19.525265 +v 66.868332 41.145340 17.867582 +v 66.833511 40.679123 17.686600 +v 66.969543 41.990517 18.393681 +v 68.302063 39.825142 25.320065 +v 67.362976 43.313560 20.438702 +v 67.456001 43.407745 20.922256 +v 67.107300 42.675888 19.109715 +v 68.778412 40.316677 17.209553 +v 66.913918 41.584785 18.104534 +v 67.645050 43.408234 21.904913 +v 67.738091 43.314533 22.388565 +v 68.066895 42.358826 24.097664 +v 67.828186 43.159847 22.856865 +v 68.268051 40.682858 25.143246 +v 68.233139 41.148895 24.961798 +v 67.993881 42.678200 23.718168 +v 68.291641 40.197338 25.265890 +v 68.131775 41.993538 24.434887 +v 68.187477 41.588097 24.724442 +v 67.913895 42.946617 23.302391 +v 65.674019 35.426590 22.246143 +v 65.723358 35.394489 22.502598 +v 65.735397 35.346016 22.565155 +v 65.757790 34.837643 22.681578 +v 65.761017 35.517830 22.698362 +v 65.843590 35.101654 23.127579 +v 66.269852 35.256065 25.343250 +v 66.416916 38.169350 26.107695 +v 66.382095 37.703129 25.926710 +v 66.492859 36.014885 26.502462 +v 66.269112 35.984661 25.339407 +v 66.428741 38.666756 26.169178 +v 64.613541 37.162949 16.733797 +v 64.730621 39.002335 17.342384 +v 64.742538 38.504982 17.404335 +v 64.760979 36.642624 17.500193 +v 64.816681 36.237186 17.789747 +v 64.889694 35.917809 18.169243 +v 64.975403 35.704582 18.614769 +v 65.068451 35.610878 19.098419 +v 64.732651 39.377975 17.352962 +v 64.840652 40.194908 17.914326 +v 68.306084 39.698807 25.340954 +v 66.799286 40.196674 17.508701 +v 69.246307 39.717827 19.641657 +v 69.254829 39.893875 19.685951 +v 69.267456 40.062881 19.751556 +v 69.283974 40.222176 19.837425 +v 69.304146 40.369247 19.942272 +v 69.401505 42.101158 20.448341 +v 69.327637 40.501785 20.064381 +v 69.354073 40.617699 20.201805 +v 69.295464 41.912445 19.897152 +v 69.414116 40.792618 20.513897 +v 69.465240 42.155445 20.779654 +v 69.529755 42.167023 21.114988 +v 69.564011 42.155701 21.293070 +v 69.627754 42.101746 21.624390 +v 69.689720 42.005928 21.946501 +v 69.748917 41.869766 22.254183 +v 69.804428 41.695389 22.542747 +v 69.855377 41.485569 22.807545 +v 69.900948 41.243603 23.044445 +v 69.940445 40.973309 23.249748 +v 68.936630 38.506760 18.031940 +v 68.665970 39.376205 16.625072 +v 69.030823 40.362648 18.521603 +v 69.076416 40.802090 18.758553 +v 69.246361 39.175495 19.641916 +v 69.141212 41.167717 19.095371 +v 69.183998 41.898907 19.317736 +v 69.242020 39.537514 19.619389 +v 69.242035 39.355782 19.619465 +v 69.254913 38.999493 19.686373 +v 69.267563 38.830551 19.752121 +v 69.284119 38.671345 19.838196 +v 69.327827 38.391960 20.065372 +v 68.940742 36.495640 18.053347 +v 68.775116 33.038315 17.192390 +v 69.151268 35.496304 19.147636 +v 69.414360 38.101585 20.515175 +v 69.315269 35.313622 20.000111 +v 69.480743 38.011539 20.860268 +v 69.482346 35.230610 20.868580 +v 69.549271 38.011723 21.216454 +v 69.649551 35.248451 21.737675 +v 69.615646 38.102104 21.561440 +v 69.646698 38.179569 21.722876 +v 69.675690 38.277023 21.873531 +v 69.702126 38.392933 22.010956 +v 69.725616 38.525467 22.133064 +v 69.745789 38.672543 22.237911 +v 70.106247 38.399223 24.111582 +v 69.774925 39.000851 22.389387 +v 69.783447 39.176891 22.433680 +v 69.973213 40.678951 23.420073 +v 69.998764 40.365173 23.552853 +v 70.016678 40.036911 23.645977 +v 70.027100 39.664719 23.700153 +v 69.787735 39.357204 22.455948 +v 70.204453 35.195450 24.622036 +v 70.357727 38.791084 25.418762 +v 70.414948 35.515854 25.716198 +v 70.361145 32.844612 25.436504 +v 70.522758 33.043049 26.276573 +v 67.908577 34.949272 12.688181 +v 68.032288 34.592869 13.331255 +v 68.271942 33.972191 14.576957 +v 68.520065 33.452927 15.866659 +v 47.518276 29.974739 29.768909 +v 49.062492 36.014885 29.855774 +v 49.523602 33.480202 29.605947 +v 60.444347 33.147514 27.483829 +v 66.600204 37.162949 16.351595 +v 66.577850 35.267597 16.235415 +v 44.535973 29.886997 14.267043 +v 45.046333 30.970798 16.919865 +v 45.691010 35.537415 20.270847 +v 46.452789 38.671345 24.230560 +v 46.180412 40.000202 22.814762 +v 45.829292 39.377975 20.989655 +v 46.072453 38.822670 22.253582 +v 46.415031 39.175495 24.034283 +v 46.410694 39.537514 24.011753 +v 46.199501 40.362648 22.913969 +v 46.224991 40.676567 23.046459 +v 46.257706 40.971096 23.216526 +v 46.410709 39.355782 24.011831 +v 46.436234 38.830551 24.144489 +v 46.472984 38.524376 24.335537 +v 46.496498 38.391960 24.457741 +v 46.522964 38.276184 24.595325 +v 46.551964 38.178879 24.746048 +v 46.583031 38.101585 24.907541 +v 46.615692 38.045509 25.077320 +v 46.649422 38.011539 25.252636 +v 47.111088 35.573845 27.652359 +v 46.717945 38.011723 25.608820 +v 46.751671 38.045860 25.784130 +v 46.784313 38.102104 25.953808 +v 47.193069 35.685493 28.078493 +v 47.196854 39.573856 28.098166 +v 47.046555 42.946617 27.316921 +v 46.952118 39.176891 26.826046 +v 47.126541 42.678200 27.732697 +v 47.187263 39.306942 28.048302 +v 47.199551 42.358826 28.112190 +v 46.930977 38.831844 26.716148 +v 47.264427 41.993538 28.449417 +v 46.914459 38.672543 26.630280 +v 47.320133 41.588097 28.738972 +v 46.894287 38.525467 26.525433 +v 47.365799 41.148895 28.976326 +v 46.870792 38.392933 26.403322 +v 47.400703 40.682858 29.157772 +v 45.480831 31.700232 19.178362 +v 47.424301 40.197338 29.280418 +v 46.844353 38.277023 26.265900 +v 45.878170 27.331514 21.243702 +v 47.526398 38.791084 29.811127 +v 46.297134 41.241596 23.421474 +v 46.342674 41.483795 23.658192 +v 46.393585 41.693878 23.922800 +v 46.449059 41.868538 24.211163 +v 46.508240 42.005016 24.518784 +v 46.570171 42.101158 24.840708 +v 46.633911 42.155445 25.172020 +v 46.668175 42.166943 25.350122 +v 46.732685 42.155701 25.685436 +v 46.796425 42.101746 26.016754 +v 46.858391 42.005928 26.338867 +v 46.815136 40.715843 26.114021 +v 46.844131 40.618538 26.264744 +v 46.870602 40.502762 26.402330 +v 46.894112 40.370346 26.524532 +v 46.914307 40.223373 26.629509 +v 46.930866 40.064163 26.715582 +v 46.943516 39.895233 26.781330 +v 46.952072 39.719231 26.825788 +v 46.956390 39.538940 26.848240 +v 48.911728 39.698807 29.072104 +v 47.415627 40.193138 21.295424 +v 47.675732 37.295708 20.000786 +v 47.628216 33.266563 19.753813 +v 48.156170 43.439468 25.144745 +v 47.639988 42.356140 22.461651 +v 48.477951 43.067505 26.817360 +v 47.878563 43.158401 23.701742 +v 47.792885 42.944725 23.256416 +v 47.473976 41.145340 21.598732 +v 47.439159 40.679123 21.417747 +v 47.575188 41.990517 22.124830 +v 48.343739 43.314533 26.119713 +v 48.907711 39.825142 29.051216 +v 50.346680 40.682858 28.591015 +v 47.968620 43.313560 24.169849 +v 48.061646 43.407745 24.653408 +v 47.712944 42.675888 22.840866 +v 48.893063 40.316677 21.035164 +v 47.519562 41.584785 21.835682 +v 48.250694 43.408234 25.636063 +v 50.145531 42.358826 27.545433 +v 49.906822 43.159847 26.304638 +v 50.072521 42.678200 27.165939 +v 50.370277 40.197338 28.713661 +v 50.210403 41.993538 27.882660 +v 50.266113 41.588097 28.172215 +v 50.311775 41.148895 28.409569 +v 49.991524 42.940437 24.759933 +v 50.086044 42.909203 25.251251 +v 50.155830 42.838928 25.613989 +v 49.550606 36.878342 22.468058 +v 50.930565 39.260826 27.656063 +v 50.445427 36.880680 27.119312 +v 50.801670 41.356995 26.986065 +v 50.202412 36.119190 25.856136 +v 50.381119 36.603928 26.785040 +v 50.577362 37.762726 27.805096 +v 50.627316 38.399429 28.064766 +v 49.674377 42.492416 23.111441 +v 49.437592 37.600716 21.880623 +v 50.310211 42.524315 26.416458 +v 50.612438 40.737396 27.987434 +v 49.955616 39.910194 22.588322 +v 49.946747 39.537140 22.542234 +v 49.969810 40.178814 22.662100 +v 49.990257 40.435905 22.768379 +v 49.947929 39.258270 22.548363 +v 50.016624 40.677399 22.905432 +v 50.048508 40.899498 23.071156 +v 49.955704 38.982391 22.588764 +v 50.085400 41.098694 23.262928 +v 50.126701 41.271851 23.477606 +v 49.969948 38.713844 22.662821 +v 50.171787 41.416233 23.711967 +v 50.219940 41.529564 23.962282 +v 50.270393 41.610062 24.224506 +v 50.322357 41.656445 24.494629 +v 50.375011 41.667992 24.768320 +v 50.409271 41.656673 24.946400 +v 50.461243 41.610554 25.216558 +v 50.511719 41.530323 25.478918 +v 50.559883 41.417244 25.729279 +v 50.605003 41.273090 25.963810 +v 50.646351 41.100151 26.178719 +v 50.576584 40.370346 25.816086 +v 50.715187 40.679214 26.536543 +v 50.741608 40.437859 26.673859 +v 49.990437 38.456863 22.769318 +v 50.016853 38.215500 22.906635 +v 50.048782 37.993568 23.072594 +v 50.085693 37.794567 23.264458 +v 50.127037 37.621628 23.479366 +v 50.172157 37.477478 23.713902 +v 50.220322 37.364395 23.964260 +v 50.270798 37.284168 24.226620 +v 50.304527 37.250198 24.401936 +v 50.357033 37.226723 24.674858 +v 50.409683 37.238281 24.948549 +v 50.461651 37.284657 25.218670 +v 50.512100 37.365158 25.480896 +v 50.560257 37.478485 25.731211 +v 50.605343 37.622871 25.965572 +v 50.646645 37.796024 26.180248 +v 50.683540 37.995224 26.372021 +v 50.715420 38.217316 26.537746 +v 50.741787 38.458817 26.674799 +v 50.762230 38.715904 26.781078 +v 50.776428 38.984528 26.854855 +v 50.762093 40.180882 26.780357 +v 50.776344 39.912327 26.854416 +v 50.784111 39.636444 26.894815 +v 50.638874 39.357204 26.139870 +v 50.532898 37.391502 27.573999 +v 49.485321 41.648415 22.128735 +v 49.760056 42.706097 23.556768 +v 49.339561 39.194977 21.371080 +v 49.372093 38.336449 21.540176 +v 49.493294 37.195278 22.170177 +v 49.605350 36.638817 22.752630 +v 49.716534 36.279732 23.330555 +v 50.366314 42.510319 26.046408 +v 50.659489 41.160637 27.570328 +v 50.039612 35.954376 25.009912 +v 50.321167 36.402306 26.473427 +v 50.400372 37.067734 27.546759 +v 50.644947 38.763664 28.156406 +v 49.368233 40.495289 21.520103 +v 49.614429 42.290787 22.799826 +v 49.348503 38.821968 21.417532 +v 49.874199 36.009033 24.150084 +v 50.418999 42.145252 26.981943 +v 50.653732 39.887733 28.202087 +v 50.747757 39.657757 28.029158 +v 49.403049 40.961510 21.701086 +v 49.338776 39.633644 21.366978 +v 49.550121 42.014042 22.465553 +v 49.398277 37.986919 21.676275 +v 49.437241 41.291088 21.878811 +v 49.920631 42.916645 24.391430 +v 49.827599 42.822464 23.907873 +v 49.346153 40.007515 21.405342 +v 71.016159 34.956318 12.079202 +v 71.431984 32.480591 22.621675 +v 70.634117 32.688740 18.474390 +v 70.898766 32.508747 19.850039 +v 71.165276 32.439228 21.235346 +v 71.697258 32.632572 24.000555 +v 73.622032 33.026604 25.624420 +v 67.059769 40.894501 21.509285 +v 48.524654 40.894501 25.075130 +v 65.968369 39.386066 20.247313 +v 48.866055 39.325497 23.541418 +v 67.332733 39.538940 22.928169 +v 49.411007 39.569225 26.374060 +v 66.885178 40.564144 20.601799 +v 48.337540 40.501785 24.102524 +v 66.885391 38.329666 20.602873 +v 48.325188 38.454250 24.038315 +v 65.991806 38.858006 20.369154 +v 48.893658 38.803097 23.684891 +v 67.220482 40.618538 22.344675 +v 49.303757 40.600708 25.816574 +v 66.943146 40.759056 20.903112 +v 48.424019 40.792618 24.552040 +v 65.983292 39.922745 20.324879 +v 48.264740 39.893875 23.724094 +v 65.974068 39.747639 20.276953 +v 48.256218 39.717827 23.679800 +v 66.013733 40.247807 20.483109 +v 48.293877 40.222176 23.875568 +v 66.928314 38.178879 20.825979 +v 49.001919 38.193485 24.247637 +v 66.085625 38.258358 20.856817 +v 48.973309 38.294010 24.098902 +v 66.992035 38.045509 21.157251 +v 49.065121 38.053043 24.576147 +v 66.849335 38.524376 20.415468 +v 65.979828 39.028351 20.306885 +v 48.264816 38.999493 23.724516 +v 67.328415 39.719231 22.905720 +v 49.405975 39.749039 26.347891 +v 66.991776 40.848862 21.155872 +v 48.456665 40.848862 24.721718 +v 67.025497 40.883003 21.331181 +v 48.490391 40.883003 24.897026 +v 48.363979 40.617699 24.239948 +v 66.104713 40.700520 20.956072 +v 48.392960 40.715153 24.390606 +v 65.969078 39.567806 20.250975 +v 48.865322 39.507221 23.537600 +v 65.996582 40.090347 20.393955 +v 48.277359 40.062881 23.789700 +v 66.034492 40.392639 20.590998 +v 48.314049 40.369247 23.980415 +v 66.146385 38.090427 21.172623 +v 49.032681 38.112743 24.407526 +v 67.042831 38.000175 21.421257 +v 49.098713 38.015327 24.750772 +v 66.007729 38.696960 20.451933 +v 48.910847 38.645729 23.774256 +v 65.971977 39.205303 20.266043 +v 48.871090 39.145687 23.567587 +v 67.094025 40.883179 21.687366 +v 48.558914 40.883179 25.253212 +v 67.246948 40.502762 22.482262 +v 49.329762 40.481998 25.951767 +v 66.448647 38.502075 22.743797 +v 49.352921 38.548859 26.072130 +v 67.307213 40.064163 22.795513 +v 49.383404 40.091618 26.230585 +v 66.247437 38.000233 21.697876 +v 48.524918 38.000221 25.076492 +v 67.290657 40.223373 22.709440 +v 49.366215 40.248989 26.141220 +v 67.234619 38.330574 22.418133 +v 49.329952 38.413719 25.952730 +v 66.347664 38.113293 22.218864 +v 49.233967 38.090916 25.453814 +v 67.319870 39.895233 22.861263 +v 49.396725 39.924091 26.299814 +v 67.160416 40.793137 22.032459 +v 48.625305 40.793137 25.598307 +v 67.191483 40.715843 22.193954 +v 49.275143 40.701237 25.667837 +v 67.127754 40.849213 21.862682 +v 48.592644 40.849213 25.428528 +v 67.270462 40.370346 22.604462 +v 66.514061 39.326916 23.083822 +v 48.796566 39.266331 26.488522 +v 66.509064 39.147079 23.057844 +v 48.671680 38.223469 25.839373 +v 66.378418 38.194202 22.378723 +v 49.265331 38.164936 25.616827 +v 67.128021 38.045860 21.864059 +v 49.201099 38.038300 25.282965 +v 66.281654 38.015545 21.875753 +v 49.167236 38.007904 25.106951 +v 67.307327 38.831844 22.796080 +v 48.779526 38.914249 26.399937 +v 66.469406 38.646912 22.851690 +v 49.372509 38.698177 26.173935 +v 66.499847 38.971985 23.009918 +v 49.400349 39.029720 26.318655 +v 66.232704 34.574276 5.343794 +v 66.250427 34.574375 5.540986 +v 51.084633 34.574375 8.458632 +v 51.027924 34.574276 8.268940 +v 65.949280 33.133568 3.827172 +v 55.146141 33.030170 5.898943 +v 60.991566 31.791014 7.665103 +v 61.027931 31.720028 7.581254 +v 56.207394 31.727734 8.516941 +v 61.135521 34.127472 5.271808 +v 50.824596 34.127472 7.255457 +v 66.006470 31.957125 4.124418 +v 55.217026 31.877304 6.267393 +v 61.052761 32.417946 4.841643 +v 55.160805 32.417946 5.975157 +v 66.125687 34.420193 4.744131 +v 50.923000 34.458382 7.766957 +v 61.067032 33.627991 4.915816 +v 55.175076 33.627991 6.049330 +v 61.108791 33.976646 5.132885 +v 50.797867 33.976646 7.116533 +v 61.198849 31.484156 5.600996 +v 55.306892 31.484156 6.734510 +v 61.067162 32.225723 4.916492 +v 55.175205 32.225723 6.050006 +v 61.043030 32.618195 4.791047 +v 55.151070 32.618195 5.924561 +v 66.202370 33.629436 6.962537 +v 56.448875 33.629436 8.838947 +v 60.956619 31.878563 7.747334 +v 56.328194 31.878563 8.637766 +v 66.172409 34.510643 4.986992 +v 61.273354 34.536453 6.031681 +v 55.405556 34.536453 7.160548 +v 50.987167 34.548199 8.057079 +v 50.960152 34.522385 7.960084 +v 66.211365 34.561234 5.232846 +v 55.433598 34.561234 7.306308 +v 61.165611 34.259357 5.428216 +v 50.854687 34.259357 7.411866 +v 61.198586 34.370247 5.599622 +v 50.887661 34.370247 7.583271 +v 61.052670 33.435703 4.841157 +v 55.160713 33.435703 5.974671 +v 61.042969 33.235359 4.790749 +v 55.151016 33.235359 5.924263 +v 61.085835 33.809242 5.013558 +v 50.774910 33.809242 6.997207 +v 61.255272 31.332344 5.981068 +v 55.395523 31.332344 7.108386 +v 61.165855 31.594851 5.429498 +v 50.839096 31.655691 7.330834 +v 61.135742 31.726589 5.272964 +v 55.243786 31.726589 6.406479 +v 61.225929 31.413540 5.741743 +v 61.228027 31.375412 5.839467 +v 64.514832 31.369623 5.222026 +v 55.333969 31.413540 6.875257 +v 55.368279 31.375412 6.966784 +v 52.414326 31.369623 7.549962 +v 61.276142 31.305359 6.089562 +v 61.276070 31.292431 6.194254 +v 55.455307 31.292431 7.314072 +v 55.416393 31.305359 7.216879 +v 61.086006 32.044556 5.014452 +v 55.194054 32.044556 6.147966 +v 61.038113 32.823364 4.765495 +v 55.146156 32.823364 5.899009 +v 60.764568 33.031796 8.165266 +v 51.803696 32.928387 9.895829 +v 60.887939 33.517090 8.036115 +v 60.855103 33.418602 8.075045 +v 56.499119 33.517090 8.880450 +v 56.544056 33.418602 8.904418 +v 66.226974 33.810654 6.856220 +v 56.350979 33.810654 8.756198 +v 66.280647 34.128571 6.577813 +v 66.242142 34.057674 6.662187 +v 56.113045 34.115154 8.548443 +v 56.045982 34.186050 8.484377 +v 66.296783 34.371017 6.234537 +v 55.870380 34.371017 8.240402 +v 60.900528 32.045918 7.881865 +v 51.684113 32.133430 9.711477 +v 60.792236 32.419456 8.081479 +v 56.604824 32.419456 8.887068 +v 66.113503 32.721592 7.129715 +v 51.801254 32.721592 9.883153 +v 61.155338 31.484911 7.225088 +v 55.949890 31.484911 8.226529 +v 61.211418 31.376360 6.971399 +v 61.188618 31.414534 7.073867 +v 55.835308 31.396793 8.058090 +v 61.268269 31.293900 6.605762 +v 55.615234 31.293900 7.693312 +v 61.272095 31.280830 6.394876 +v 55.533432 31.280830 7.498899 +v 60.801880 33.236969 8.131590 +v 56.614464 33.236969 8.937180 +v 61.074768 34.028671 7.680733 +v 61.036625 33.948902 7.755341 +v 56.233929 33.977905 8.654870 +v 66.288780 34.260307 6.413921 +v 61.186584 34.256195 7.401259 +v 55.982620 34.260307 8.396654 +v 66.286667 34.522789 5.845553 +v 51.163979 34.522789 8.754906 +v 66.302574 34.458981 6.042809 +v 61.280937 34.455750 7.017253 +v 55.756145 34.455750 8.080130 +v 51.222404 34.458981 8.943981 +v 61.310783 34.561440 6.595968 +v 55.572117 34.561440 7.699992 +v 60.841263 32.244884 8.003099 +v 60.871834 32.151146 7.952385 +v 56.530216 32.244884 8.832472 +v 56.483009 32.151146 8.796721 +v 61.130646 31.546391 7.338381 +v 61.093697 31.607336 7.430837 +v 56.014854 31.546391 8.322573 +v 56.083473 31.607336 8.394720 +v 61.266319 31.309143 6.719496 +v 61.244133 31.334991 6.825958 +v 55.659241 31.309143 7.798204 +v 55.719345 31.334991 7.888835 +v 50.692825 29.737175 4.871002 +v 46.215881 29.235327 5.986284 +v 69.947617 29.737175 1.166701 +v 60.688461 29.737175 2.948009 +v 54.613262 33.641586 3.129050 +v 60.505219 33.641586 1.995536 +v 76.120895 26.923138 33.255138 +v 74.296257 27.851955 29.481903 +v 72.385330 27.945814 28.318336 +v 72.319244 27.958984 28.116190 +v 72.228294 27.971840 27.923962 +v 72.113907 27.984182 27.744629 +v 71.977898 27.995813 27.581039 +v 71.822403 28.006550 27.435783 +v 71.649887 28.016228 27.311081 +v 71.463081 28.024693 27.208948 +v 71.264900 28.031803 27.131058 +v 71.058472 28.037455 27.078587 +v 70.847054 28.041557 27.052334 +v 70.634003 28.044043 27.052769 +v 71.147552 28.113617 25.818951 +v 70.422714 28.044874 27.079849 +v 69.059708 27.945814 28.958130 +v 69.046066 27.958984 28.745895 +v 69.059204 27.971840 28.533642 +v 69.098892 27.984182 28.324667 +v 69.164490 27.995813 28.122292 +v 69.254990 28.006550 27.929712 +v 69.368919 28.016228 27.749901 +v 69.504501 28.024693 27.585747 +v 69.659637 28.031803 27.439884 +v 69.831863 28.037455 27.314566 +v 70.018402 28.041557 27.211752 +v 70.216408 28.044043 27.133106 +v 72.439148 27.919369 28.739426 +v 72.426010 27.906513 28.951679 +v 72.386322 27.894171 29.160656 +v 72.320724 27.882540 29.363029 +v 72.230225 27.871803 29.555611 +v 72.116295 27.862123 29.735420 +v 71.980713 27.853661 29.899574 +v 71.825577 27.846548 30.045439 +v 71.653351 27.840897 30.170757 +v 71.466766 27.836796 30.273579 +v 71.268753 27.834311 30.352226 +v 71.062500 27.833479 30.405472 +v 70.851166 27.834311 30.432564 +v 67.698425 27.939177 29.328310 +v 69.099884 27.932537 29.166986 +v 69.165970 27.919369 29.369133 +v 72.203445 27.764736 31.307426 +v 70.638107 27.836796 30.432999 +v 70.426743 27.840897 30.406734 +v 70.220314 27.846548 30.354265 +v 70.022133 27.853661 30.276375 +v 69.835327 27.862123 30.174240 +v 69.662811 27.871803 30.049538 +v 69.507317 27.882540 29.904284 +v 69.371307 27.894171 29.740694 +v 69.256920 27.906513 29.561359 +v 67.182846 29.161577 26.648310 +v 68.410538 30.035097 29.324543 +v 74.056992 30.035097 28.238258 +v 72.954063 28.962406 31.239151 +v 68.927895 28.962406 32.013718 +v 71.274086 28.064154 31.505253 +v 68.559402 29.311287 26.393002 +v 70.218193 28.413034 26.016777 +v 71.898178 29.311287 25.750675 +v 59.056892 27.939177 30.990797 +v 57.655434 27.945814 31.152121 +v 57.589355 27.958984 30.949974 +v 57.498402 27.971840 30.757748 +v 57.384010 27.984182 30.578415 +v 57.248009 27.995813 30.414824 +v 57.092514 28.006550 30.269569 +v 56.919998 28.016228 30.144867 +v 56.733192 28.024693 30.042732 +v 56.535011 28.031803 29.964842 +v 56.328579 28.037455 29.912373 +v 56.117161 28.041557 29.886118 +v 55.904106 28.044043 29.886555 +v 55.692818 28.044874 29.913635 +v 54.551880 28.113617 29.011681 +v 55.486565 28.044043 29.966881 +v 54.329819 27.945814 31.791914 +v 54.316181 27.958984 31.579679 +v 54.329308 27.971840 31.367428 +v 54.368988 27.984182 31.158455 +v 54.434601 27.995813 30.956078 +v 54.525097 28.006550 30.763496 +v 54.639030 28.016228 30.583687 +v 54.774609 28.024693 30.419533 +v 54.929749 28.031803 30.273668 +v 55.101974 28.037455 30.148350 +v 55.288559 28.041557 30.045528 +v 57.695614 27.932537 31.360977 +v 57.709255 27.919369 31.573212 +v 57.696117 27.906513 31.785465 +v 57.656433 27.894171 31.994438 +v 57.590836 27.882540 32.196815 +v 57.500336 27.871803 32.389397 +v 57.386406 27.862123 32.569206 +v 57.250824 27.853661 32.733360 +v 57.095688 27.846548 32.879223 +v 56.923462 27.840897 33.004539 +v 56.736877 27.836796 33.107365 +v 56.538868 27.834311 33.186012 +v 56.332615 27.833479 33.239258 +v 56.121326 27.834311 33.266338 +v 55.908272 27.836796 33.266777 +v 54.369999 27.932537 32.000771 +v 54.436081 27.919369 32.202915 +v 54.527027 27.906513 32.395145 +v 54.641411 27.894171 32.574478 +v 54.777424 27.882540 32.738068 +v 55.607769 27.764736 34.500156 +v 55.696854 27.840897 33.240520 +v 55.490425 27.846548 33.188049 +v 55.292240 27.853661 33.110161 +v 55.105431 27.862123 33.008026 +v 54.932915 27.871803 32.883324 +v 58.394215 30.035097 31.251516 +v 52.747757 30.035097 32.337799 +v 53.829506 29.311287 29.226784 +v 57.855675 29.311287 28.452217 +v 55.488300 28.413034 28.850563 +v 59.597198 28.812696 33.799271 +v 58.224174 28.962406 34.072937 +v 56.544193 28.064154 34.339039 +v 54.885399 28.962406 34.715263 +v 71.292938 28.057924 31.603260 +v 70.199333 28.419264 25.918770 +v 68.942963 30.537178 28.386755 +v 68.562332 30.362362 26.408234 +v 69.423767 30.378311 30.885975 +v 68.996002 30.219070 28.662430 +v 69.535141 30.191223 31.464848 +v 69.624710 28.959290 31.930481 +v 72.873917 30.191223 30.822521 +v 70.337189 30.477804 26.635292 +v 69.659355 29.816544 26.111471 +v 71.901108 30.362362 25.765907 +v 72.281738 30.537178 27.744431 +v 72.334778 30.219070 28.020103 +v 72.974686 29.105885 31.346336 +v 72.762543 30.378311 30.243649 +v 72.457283 30.627293 28.692375 +v 72.470924 30.614124 28.904610 +v 72.457787 30.601269 29.116861 +v 72.418106 30.588924 29.325836 +v 72.352501 30.577293 29.528212 +v 72.262001 30.566557 29.720791 +v 72.148071 30.556877 29.900600 +v 72.012497 30.548416 30.064758 +v 71.857361 30.541304 30.210621 +v 71.685135 30.535652 30.335938 +v 71.498550 30.531551 30.438761 +v 71.300537 30.529064 30.517406 +v 71.094284 30.528233 30.570656 +v 70.207520 30.528126 30.742981 +v 70.669891 30.531551 30.598179 +v 70.458519 30.535652 30.571915 +v 70.252090 30.541304 30.519445 +v 70.053909 30.548416 30.441555 +v 69.867096 30.556877 30.339422 +v 69.694580 30.566557 30.214722 +v 69.539093 30.577293 30.069466 +v 69.403091 30.588924 29.905874 +v 69.288696 30.601269 29.726540 +v 69.197739 30.614124 29.534313 +v 69.131668 30.627293 29.332167 +v 72.417099 30.640570 28.483517 +v 72.351028 30.653740 28.281372 +v 72.260071 30.666595 28.089146 +v 72.145676 30.678936 27.909809 +v 72.009674 30.690569 27.746220 +v 71.854187 30.701305 27.600964 +v 71.681671 30.710983 27.476261 +v 71.494858 30.719448 27.374128 +v 71.296677 30.726559 27.296238 +v 71.090248 30.732208 27.243767 +v 70.878830 30.736313 27.217514 +v 70.665771 30.738798 27.217949 +v 70.454483 30.739630 27.245029 +v 70.453842 30.739841 27.241695 +v 70.248184 30.738798 27.298286 +v 70.050179 30.736313 27.376934 +v 69.863647 30.732208 27.479748 +v 69.691414 30.726559 27.605064 +v 69.536270 30.719448 27.750927 +v 69.400696 30.710983 27.915085 +v 69.286766 30.701305 28.094894 +v 69.196274 30.690569 28.287472 +v 69.130669 30.678936 28.489849 +v 69.090981 30.666595 28.698824 +v 69.077843 30.653740 28.911076 +v 69.091484 30.640570 29.123310 +v 71.007957 30.540230 27.122408 +v 56.563049 28.057924 34.437046 +v 55.469444 28.419264 28.752552 +v 54.213074 30.537178 31.220539 +v 53.832436 30.362362 29.242018 +v 54.693878 30.378311 33.719757 +v 54.266106 30.219070 31.496214 +v 54.805244 30.191223 34.298630 +v 54.902679 29.357473 34.805073 +v 58.144020 30.191223 33.656307 +v 55.607292 30.477804 29.469078 +v 54.929462 29.816544 28.945257 +v 57.158852 29.314400 28.535456 +v 57.284344 30.324982 29.187744 +v 57.551849 30.537178 30.578217 +v 57.814846 30.099600 31.945259 +v 58.244793 29.105885 34.180122 +v 58.032654 30.378311 33.077431 +v 57.727394 30.627293 31.526159 +v 57.741032 30.614124 31.738392 +v 57.727894 30.601269 31.950645 +v 57.688210 30.588924 32.159622 +v 57.622612 30.577293 32.362000 +v 57.532112 30.566557 32.554577 +v 57.418182 30.556877 32.734386 +v 57.282600 30.548416 32.898544 +v 57.127464 30.541304 33.044403 +v 56.955238 30.535652 33.169724 +v 56.768654 30.531551 33.272545 +v 56.570648 30.529064 33.351189 +v 56.364391 30.528233 33.404442 +v 55.477631 30.528126 33.576767 +v 55.940048 30.531551 33.431953 +v 55.728630 30.535652 33.405701 +v 55.522202 30.541304 33.353230 +v 55.324017 30.548416 33.275337 +v 55.137207 30.556877 33.173206 +v 54.964695 30.566557 33.048508 +v 54.809204 30.577293 32.903252 +v 54.673187 30.588924 32.739662 +v 54.558804 30.601269 32.560326 +v 54.467861 30.614124 32.368095 +v 54.401779 30.627293 32.165955 +v 57.687210 30.640570 31.317303 +v 57.621132 30.653740 31.115156 +v 57.530182 30.666595 30.922928 +v 57.415791 30.678936 30.743595 +v 57.279785 30.690569 30.580006 +v 57.124294 30.701305 30.434750 +v 56.951782 30.710983 30.310047 +v 56.764969 30.719448 30.207914 +v 56.566788 30.726559 30.130024 +v 56.360359 30.732208 30.077553 +v 56.148941 30.736313 30.051300 +v 55.935886 30.738798 30.051735 +v 55.724598 30.739630 30.078815 +v 55.723957 30.739841 30.075481 +v 55.518341 30.738798 30.132063 +v 55.320335 30.736313 30.210709 +v 55.133751 30.732208 30.313532 +v 54.961525 30.726559 30.438850 +v 54.806385 30.719448 30.584713 +v 54.670807 30.710983 30.748867 +v 54.556877 30.701305 30.928679 +v 54.466377 30.690569 31.121258 +v 54.400768 30.678936 31.323635 +v 54.361088 30.666595 31.532608 +v 54.347961 30.653740 31.744860 +v 54.361595 30.640570 31.957096 +v 56.278065 30.540230 29.956192 +v 72.420639 30.939987 28.501871 +v 72.379440 27.446785 28.287746 +v 69.135193 30.926712 29.350521 +v 69.094002 27.433510 29.136396 +v 71.056618 27.334450 30.374886 +v 71.097809 30.827650 30.589008 +v 69.742195 27.617165 30.102001 +v 69.870636 30.856297 30.357777 +v 69.363037 27.517200 27.719313 +v 69.339844 30.856140 28.010603 +v 69.825974 27.538425 27.283978 +v 69.867172 31.031628 27.498100 +v 69.365425 27.395142 29.710104 +v 69.406616 30.888344 29.924229 +v 71.052582 27.538425 27.047997 +v 71.093781 31.031628 27.262123 +v 71.457199 27.525665 27.178358 +v 71.498390 31.018864 27.392483 +v 69.093002 27.485153 28.294079 +v 69.134201 30.978355 28.508202 +v 69.653748 27.532776 27.409294 +v 69.694946 31.025976 27.623417 +v 70.012512 27.542528 27.181164 +v 70.053711 31.035728 27.395287 +v 71.819687 27.347519 30.014851 +v 71.860886 30.840721 30.228973 +v 70.420860 27.341869 30.376144 +v 70.462051 30.835072 30.590271 +v 70.016243 27.354631 30.245785 +v 70.057442 30.847832 30.459909 +v 69.251038 27.407484 29.530769 +v 69.292229 30.900684 29.744894 +v 72.313354 27.459957 28.085602 +v 72.354553 30.953157 28.299725 +v 72.108017 27.485153 27.714039 +v 72.149216 30.978355 27.928164 +v 71.737137 27.762161 27.352734 +v 71.685196 31.010403 27.494616 +v 71.972015 27.496784 27.550449 +v 72.013206 30.989986 27.764574 +v 70.841171 27.542528 27.021744 +v 70.882362 31.035728 27.235867 +v 70.416824 27.545847 27.049259 +v 70.458023 31.039047 27.263382 +v 71.259018 27.532776 27.100468 +v 71.300209 31.025976 27.314592 +v 69.040184 27.459957 28.715305 +v 69.081375 30.953157 28.929430 +v 69.158600 27.496784 28.091702 +v 69.199799 30.989986 28.305826 +v 69.249100 27.507523 27.899122 +v 69.053322 27.472813 28.503054 +v 69.094513 30.966013 28.717178 +v 69.095016 30.939987 29.141665 +v 69.053818 27.446785 28.927540 +v 69.498611 27.525665 27.555157 +v 69.539810 31.018864 27.769279 +v 70.210518 27.545013 27.102516 +v 70.251717 31.038216 27.316641 +v 71.262878 27.335281 30.321636 +v 71.304062 30.828482 30.535759 +v 71.647461 27.341869 30.140167 +v 71.688660 30.835072 30.354290 +v 71.974823 27.354631 29.868988 +v 72.016022 30.847832 30.083111 +v 72.224342 27.372772 29.525021 +v 72.212448 30.711140 29.823437 +v 72.380432 27.395142 29.130066 +v 72.421631 30.888344 29.344189 +v 72.420120 27.407484 28.921089 +v 72.461319 30.900684 29.135214 +v 72.460815 30.926712 28.710728 +v 72.422562 27.683025 28.511898 +v 70.214432 27.347519 30.323675 +v 70.255623 30.840721 30.537800 +v 70.632233 27.337767 30.402409 +v 70.778152 30.679604 30.613853 +v 70.845276 27.335281 30.401974 +v 70.886475 30.828482 30.616096 +v 69.501427 27.383511 29.873695 +v 69.542625 30.876711 30.087818 +v 69.698120 30.865974 30.233074 +v 69.160080 27.420340 29.338543 +v 69.201279 30.913540 29.552666 +v 72.222404 27.472813 27.893375 +v 72.263603 30.966013 28.107498 +v 71.857712 31.000725 27.619318 +v 70.628113 27.545013 27.022179 +v 70.669312 31.038216 27.236301 +v 71.460876 27.337767 30.242990 +v 71.502075 30.830967 30.457115 +v 72.110413 27.363094 29.704830 +v 72.314835 27.383511 29.332441 +v 72.356033 30.876711 29.546566 +v 72.433258 27.420340 28.708839 +v 72.474457 30.913540 28.922962 +v 57.690742 30.939987 31.335655 +v 57.649551 27.446785 31.121532 +v 54.405308 30.926712 32.184303 +v 54.364113 27.433510 31.970182 +v 56.326729 27.334450 33.208672 +v 56.367924 30.827650 33.422791 +v 55.012306 27.617165 32.935783 +v 55.140739 30.856297 33.191559 +v 54.633144 27.517200 30.553097 +v 54.609955 30.856140 30.844387 +v 55.096085 27.538425 30.117762 +v 55.137283 31.031628 30.331886 +v 54.635525 27.395142 32.543892 +v 54.676720 30.888344 32.758015 +v 56.322693 27.538425 29.881783 +v 56.363888 31.031628 30.095905 +v 56.727306 27.525665 30.012144 +v 56.768501 31.018864 30.226267 +v 54.363106 27.485153 31.127865 +v 54.404297 30.978355 31.341988 +v 54.923859 27.532776 30.243080 +v 54.965057 31.025976 30.457203 +v 55.282669 27.542528 30.014938 +v 55.323868 31.035728 30.229061 +v 57.089802 27.347519 32.848633 +v 57.130997 30.840721 33.062759 +v 55.690971 27.341869 33.209930 +v 55.732162 30.835072 33.424057 +v 55.286354 27.354631 33.079567 +v 55.327549 30.847832 33.293694 +v 54.521141 27.407484 32.364555 +v 54.562336 30.900684 32.578678 +v 57.583469 27.459957 30.919386 +v 57.624664 30.953157 31.133511 +v 57.378128 27.485153 30.547825 +v 57.419319 30.978355 30.761948 +v 57.007240 27.762161 30.186518 +v 56.955307 31.010403 30.328400 +v 57.242123 27.496784 30.384235 +v 57.283318 30.989986 30.598358 +v 56.111279 27.542528 29.855530 +v 56.152470 31.035728 30.069653 +v 55.686935 27.545847 29.883045 +v 55.728127 31.039047 30.097168 +v 56.529121 27.532776 29.934254 +v 56.570320 31.025976 30.148376 +v 54.310295 27.459957 31.549089 +v 54.351490 30.953157 31.763214 +v 54.428715 27.496784 30.925488 +v 54.469910 30.989986 31.139610 +v 54.519211 27.507523 30.732908 +v 54.323425 27.472813 31.336838 +v 54.364616 30.966013 31.550964 +v 54.365128 30.939987 31.975449 +v 54.323933 27.446785 31.761326 +v 54.768723 27.525665 30.388943 +v 54.809917 31.018864 30.603065 +v 55.480682 27.545013 29.936293 +v 55.521873 31.038216 30.150417 +v 57.730923 30.926712 31.544512 +v 57.689728 27.433510 31.330389 +v 56.532982 27.335281 33.155418 +v 56.574177 30.828482 33.369545 +v 56.917576 27.341869 32.973953 +v 56.958771 30.835072 33.188076 +v 57.244938 27.354631 32.702774 +v 57.286133 30.847832 32.916893 +v 57.494453 27.372772 32.358807 +v 57.482563 30.711140 32.657219 +v 57.650547 27.395142 31.963852 +v 57.691742 30.888344 32.177975 +v 57.690231 27.407484 31.754875 +v 57.731426 30.900684 31.968998 +v 55.484539 27.347519 33.157459 +v 55.525734 30.840721 33.371586 +v 55.902386 27.337767 33.236183 +v 56.048359 30.679604 33.447617 +v 56.115440 27.335281 33.235748 +v 56.156635 30.828482 33.449875 +v 54.771538 27.383511 32.707481 +v 54.812733 30.876711 32.921604 +v 54.968224 30.865974 33.066856 +v 54.430199 27.420340 32.172325 +v 54.471390 30.913540 32.386452 +v 57.492516 27.472813 30.727158 +v 57.533710 30.966013 30.941284 +v 57.127823 31.000725 30.453102 +v 55.898224 27.545013 29.855965 +v 55.939415 31.038216 30.070087 +v 56.730991 27.337767 33.076775 +v 56.772186 30.830967 33.290901 +v 57.380516 27.363094 32.538616 +v 57.584949 27.383511 32.166229 +v 57.626144 30.876711 32.380348 +v 57.703369 27.420340 31.542622 +v 57.744564 30.913540 31.756746 +v 71.968338 26.315218 30.481468 +v 70.098694 26.579535 26.529116 +v 69.414413 26.607899 30.284462 +v 72.160309 26.581665 30.184135 +v 71.733040 27.032171 27.003265 +v 69.956200 26.819508 26.728064 +v 70.342125 26.580122 26.472696 +v 68.642342 26.707924 28.801178 +v 68.660568 26.732841 28.391180 +v 68.739578 26.754332 28.025385 +v 69.299118 26.615072 30.189610 +v 68.675522 26.691605 29.061028 +v 72.108589 26.319937 30.377516 +v 72.493721 26.602472 29.780579 +v 72.015846 27.016306 27.207657 +v 69.592812 26.812012 26.920242 +v 69.678917 26.574068 26.699045 +v 68.562599 26.497252 28.167009 +v 68.509338 26.474747 28.544397 +v 69.119263 26.628878 29.998981 +v 69.728638 26.850067 30.359697 +v 68.707001 26.409433 29.571854 +v 72.668144 26.934923 28.409842 +v 72.417603 26.334480 30.080812 +v 72.495529 26.339773 29.979467 +v 69.341316 26.564245 26.924273 +v 54.651581 26.609947 33.091145 +v 58.167965 26.443861 31.037786 +v 57.984772 26.707924 30.851517 +v 57.238449 26.315218 33.315254 +v 54.565544 26.801542 29.982037 +v 58.079407 26.371498 32.235325 +v 58.013012 26.474747 30.563715 +v 55.368805 26.579535 29.362902 +v 54.993431 26.591278 33.329979 +v 55.230995 26.581665 33.441048 +v 54.771683 26.602472 33.190002 +v 57.761147 26.739536 30.378838 +v 57.003143 27.032171 29.837051 +v 55.107254 26.576782 29.458143 +v 53.974915 26.521887 30.571533 +v 53.910694 26.512682 30.734039 +v 57.818161 26.607899 32.515388 +v 57.606628 26.754332 30.167179 +v 57.285950 27.016306 30.041443 +v 54.862923 26.812012 29.754026 +v 54.949017 26.574068 29.532829 +v 55.612232 26.580122 29.306482 +v 54.232159 26.780737 30.385586 +v 54.302399 26.549704 30.054762 +v 54.033253 26.528168 30.457861 +v 54.457920 26.362295 33.082184 +v 58.015217 26.362295 32.397823 +v 57.954395 26.482391 30.450287 +v 54.224499 26.544409 30.156107 +v 55.350819 26.319937 33.601429 +v 72.581818 31.931406 28.642597 +v 69.247734 32.009239 28.014282 +v 69.908287 33.045830 27.321976 +v 72.610809 31.917156 28.869511 +v 68.997543 31.931406 29.332151 +v 72.329315 31.973259 28.008385 +v 68.968552 31.945658 29.105236 +v 72.524551 31.945658 28.421124 +v 69.718849 31.853573 30.463123 +v 72.582649 31.889553 29.325228 +v 69.417747 33.029507 27.682684 +v 69.600281 33.037315 27.520166 +v 71.349800 33.037315 27.183588 +v 69.250061 31.889553 29.966362 +v 72.740997 32.896088 29.219879 +v 72.040512 31.835407 30.312843 +v 68.996719 31.973259 28.649521 +v 69.773560 32.035866 27.478678 +v 69.506042 33.033638 27.598263 +v 70.333771 32.044880 27.223888 +v 70.872749 32.809589 30.990463 +v 69.385010 31.876631 30.151194 +v 72.525909 31.876631 29.546936 +v 71.864098 31.828529 30.458990 +v 68.855392 32.956409 28.983366 +v 69.053452 31.986179 28.427811 +v 69.137558 31.998238 28.214966 +v 69.382187 32.019012 27.828947 +v 72.171112 32.998066 27.665890 +v 70.787460 32.043091 27.165773 +v 71.459435 32.027405 27.292416 +v 70.386681 32.817955 30.947479 +v 70.253113 32.821613 30.913517 +v 69.819046 32.838135 30.727482 +v 69.509567 32.855495 30.503813 +v 69.063927 32.896088 29.927288 +v 72.747513 32.902519 29.113739 +v 71.455765 32.809589 30.878304 +v 71.787888 32.815147 30.723747 +v 72.331635 31.853573 29.960466 +v 72.441803 31.864576 29.759783 +v 72.541077 32.855495 29.920601 +v 68.861908 32.962837 28.877224 +v 69.061829 33.003433 28.176502 +v 69.266235 33.020794 27.853973 +v 70.019012 33.047729 27.269726 +v 70.147141 33.049339 27.218803 +v 72.194366 31.986179 27.823555 +v 71.860519 32.009239 27.511625 +v 70.990250 33.045830 27.113823 +v 71.173492 32.035866 27.209354 +v 70.607948 33.050293 27.114550 +v 69.054817 31.917156 29.553627 +v 70.722923 31.820793 30.804764 +v 70.612663 32.813095 30.983280 +v 70.119934 31.835407 30.682331 +v 69.912468 32.833904 30.778522 +v 69.431808 32.860863 30.431215 +v 71.783859 33.020794 27.369621 +v 71.690437 33.025024 27.318584 +v 69.018494 32.902519 29.831139 +v 70.506989 32.815147 30.970173 +v 71.694626 32.813095 30.775129 +v 71.583900 32.811199 30.827377 +v 71.339340 32.808632 30.916300 +v 71.168411 32.808216 30.955994 +v 72.259796 31.848354 30.059460 +v 72.268860 32.833904 30.325191 +v 72.538986 32.962837 28.169817 +v 72.584412 32.956409 28.265965 +v 72.093338 33.003433 27.593292 +v 70.994957 32.808632 30.982553 +v 57.851929 31.931406 31.476381 +v 54.517845 32.009239 30.848066 +v 55.178398 33.045830 30.155760 +v 57.880920 31.917156 31.703297 +v 54.267658 31.931406 32.165936 +v 57.599419 31.973259 30.842171 +v 54.238655 31.945658 31.939022 +v 57.794659 31.945658 31.254906 +v 54.988960 31.853573 33.296906 +v 57.852760 31.889553 32.159012 +v 54.808964 32.027405 30.495689 +v 54.985382 32.034283 30.349545 +v 56.511497 32.034283 30.055944 +v 54.520180 31.889553 32.800144 +v 58.011108 32.896088 32.053665 +v 57.310623 31.835407 33.146629 +v 54.266842 31.973259 31.483303 +v 55.085125 33.043781 30.207142 +v 55.704605 33.050713 29.974895 +v 56.142860 32.809589 33.824249 +v 54.655117 31.876631 32.984978 +v 57.796021 31.876631 32.380722 +v 57.161301 32.817955 33.491821 +v 54.125465 32.956409 31.817158 +v 54.323566 31.986179 31.261597 +v 54.407684 31.998238 31.048748 +v 54.652302 32.019012 30.662731 +v 55.533676 33.050293 30.014589 +v 57.441216 32.998066 30.499674 +v 56.057571 32.043091 29.999559 +v 56.729546 32.027405 30.126202 +v 55.608093 31.828529 33.586372 +v 55.089157 32.838135 33.561268 +v 54.779667 32.855495 33.337601 +v 54.334042 32.896088 32.761070 +v 58.017624 32.902519 31.947525 +v 56.725872 32.809589 33.712090 +v 57.058002 32.815147 33.557533 +v 57.601746 31.853573 32.794250 +v 57.711914 31.864576 32.593567 +v 57.811188 32.855495 32.754387 +v 54.132027 32.962837 31.711008 +v 54.331932 33.003433 31.010290 +v 54.536339 33.020794 30.687756 +v 55.385986 32.043091 30.128761 +v 57.464470 31.986179 30.657339 +v 57.130630 32.009239 30.345409 +v 56.260357 33.045830 29.947609 +v 55.878059 33.050293 29.948336 +v 54.324921 31.917156 32.387413 +v 55.993080 31.820793 33.638538 +v 55.882774 32.813095 33.817062 +v 55.390045 31.835407 33.516117 +v 55.182579 32.833904 33.612305 +v 54.701931 32.860863 33.264996 +v 57.272736 32.821613 33.410721 +v 57.053970 33.020794 30.203407 +v 56.960548 33.025024 30.152367 +v 56.366035 33.043781 29.960716 +v 54.288567 32.902519 32.664932 +v 55.777092 32.815147 33.803955 +v 56.964729 32.813095 33.608917 +v 56.854012 32.811199 33.661163 +v 56.609451 32.808632 33.750084 +v 56.438522 32.808216 33.789780 +v 57.529907 31.848354 32.893242 +v 57.538971 32.833904 33.158978 +v 57.809090 32.962837 31.003601 +v 57.854523 32.956409 31.099751 +v 57.363449 33.003433 30.427076 +v 56.265068 32.808632 33.816338 +v 67.710693 39.093384 25.775009 +v 58.390137 29.744638 12.561989 +v 53.301613 30.837086 17.513102 +v 66.998322 30.350315 22.819202 +v 53.510498 29.193283 22.495529 +v 65.717789 30.823067 15.353117 +v 65.329773 35.080704 19.524605 +v 65.208099 35.579727 19.579735 +v 65.675636 35.415257 22.173010 +v 61.460754 30.122192 27.606140 +v 47.649055 27.454153 12.493173 +v 47.290409 27.790154 12.328449 +v 70.123520 33.096260 26.457876 +v 70.434082 33.071548 26.801268 +v 69.936821 33.076488 26.816385 +v 69.685043 33.065853 27.038284 +v 69.770439 33.080070 26.789906 +v 63.151642 33.009308 29.217632 +v 63.290779 32.984978 29.587803 +v 63.429760 32.932781 30.412621 +v 63.407700 32.940395 30.292667 +v 63.390350 32.948917 30.156967 +v 63.334511 32.970093 29.822248 +v 63.318298 32.978077 29.695122 +v 63.284714 32.991623 29.480576 +v 65.170296 32.997131 29.027990 +v 61.567287 33.059551 28.702791 +v 63.587120 33.089775 27.821175 +v 63.470627 33.093452 27.783619 +v 60.887775 33.080994 28.483789 +v 54.167400 33.082066 29.759163 +v 52.926620 33.098160 29.735325 +v 52.659462 33.091377 29.897301 +v 52.462818 33.080070 30.119602 +v 52.384384 33.071388 30.276371 +v 52.328743 33.060848 30.458994 +v 52.313683 33.046104 30.702461 +v 52.329174 33.038490 30.823681 +v 52.969357 33.039158 30.689613 +v 52.571171 33.087246 29.981724 +v 53.488445 33.076488 29.980782 +v 73.954010 33.014244 27.058990 +v 73.492531 32.996738 27.433302 +v 73.832451 33.033283 26.771727 +v 73.760345 33.040184 26.672985 +v 73.582001 33.052505 26.506313 +v 73.367722 33.062210 26.389236 +v 71.162155 33.053223 26.960196 +v 70.851105 33.067471 26.787560 +v 70.676483 33.070595 26.770233 +v 74.502411 32.833042 29.909569 +v 73.923241 32.854427 29.672142 +v 74.496109 32.814003 30.221432 +v 74.465790 32.807098 30.339878 +v 73.815895 32.821659 30.227333 +v 74.362015 32.794777 30.560827 +v 73.649445 32.806507 30.506533 +v 74.206474 32.785072 30.749050 +v 74.112228 32.781399 30.827145 +v 63.571854 32.884747 31.168949 +v 63.482590 32.914238 30.704979 +v 63.449841 32.926147 30.517002 +v 65.286980 32.919964 30.264404 +v 61.677921 32.908604 31.144112 +v 65.416847 32.890198 30.725039 +v 63.595421 32.876957 31.291462 +v 63.613636 32.870937 31.386137 +v 61.732666 32.866093 31.827078 +v 61.727100 32.859928 31.928732 +v 63.721375 32.849617 31.713259 +v 63.746559 32.843353 31.810556 +v 61.692570 32.837376 32.303265 +v 63.794128 32.829422 32.028717 +v 65.893768 32.825825 31.683489 +v 61.684250 32.823376 32.533310 +v 66.160912 32.812107 31.855865 +v 66.533264 32.776691 32.361996 +v 53.116489 32.810104 34.398037 +v 52.954250 32.833042 34.055077 +v 53.403961 32.854427 33.619709 +v 53.503826 32.840313 33.830807 +v 53.653728 32.825954 34.036137 +v 54.578373 32.788551 34.468437 +v 53.657028 32.781399 34.762383 +v 53.540535 32.785072 34.724831 +v 53.326263 32.794777 34.607758 +v 48.117363 32.414230 25.472364 +v 47.936600 37.451244 25.062084 +v 47.462337 33.349678 22.596886 +v 48.472832 34.769199 27.849392 +v 48.132996 37.451244 25.024300 +v 47.448547 35.891544 21.995876 +v 47.658737 33.349678 22.559101 +v 48.669228 34.769199 27.811611 +v 68.597794 32.460957 20.770002 +v 68.529022 34.956100 20.941839 +v 68.656647 37.451244 21.075893 +v 68.725418 34.956100 20.904057 +v 69.192871 34.769199 23.863203 +v 68.061569 35.143002 17.982693 +v 55.493916 28.714106 7.319708 +v 55.513721 36.246075 7.617717 +v 55.613857 36.785557 7.873100 +v 55.716690 36.180756 8.096821 +v 61.174526 35.372417 7.578281 +v 56.715851 36.162281 9.156989 +v 56.882771 35.167583 9.204084 +v 56.822067 30.019897 8.888545 +v 56.431080 30.478729 8.663978 +v 56.156376 36.533798 8.710601 +v 55.448944 35.688725 7.386063 +v 58.242477 32.209637 9.415129 +v 57.847332 33.890442 9.466166 +v 55.887363 36.691738 8.367769 +v 60.789288 36.162281 8.373327 +v 60.650536 35.064186 8.472656 +v 60.650768 30.852600 8.148406 +v 61.226772 28.741980 5.832928 +v 61.276680 36.221165 6.092371 +v 61.304726 36.245945 6.238131 +v 61.139194 35.320530 7.654732 +v 59.853283 33.890442 9.080255 +v 61.334484 36.246075 6.497900 +v 61.323769 36.180756 7.018112 +v 61.336254 36.785557 6.772207 +v 61.212914 36.040863 7.487852 +v 61.270916 36.696884 7.311611 +v 61.101208 36.471527 7.835536 +v 60.939262 35.652382 8.111008 +v 55.129189 35.673710 5.810829 +v 55.272560 36.103405 6.556059 +v 55.155918 35.824539 5.949753 +v 55.188896 35.935432 6.121159 +v 55.388840 36.221134 7.160454 +v 55.282745 35.532955 6.608987 +v 54.923420 34.600830 4.741237 +v 55.004421 35.057861 5.162272 +v 55.052929 35.978577 5.414425 +v 54.940838 30.423944 4.831768 +v 55.182667 30.252462 6.088789 +v 55.207291 28.823801 6.216789 +v 60.840225 34.880630 3.736892 +v 60.853653 32.655666 3.806683 +v 60.900909 31.887091 4.052315 +v 60.842358 35.727879 3.747977 +v 60.912659 36.041630 4.113379 +v 60.989826 36.306293 4.514506 +v 61.135990 36.051147 5.274265 +v 61.164520 36.103405 5.422544 +v 61.280792 36.221134 6.026940 +v 61.204659 29.431095 5.674609 +v 55.328812 29.431095 6.805025 +v 61.294086 36.786648 6.124968 +v 55.408882 36.221165 7.221238 +v 46.259369 31.056692 15.285244 +v 44.349480 28.218271 12.921448 +v 44.634125 27.525064 13.077703 +v 49.204826 26.501539 28.896095 +v 45.588505 27.542517 12.810178 +# 3660 vertices, 0 vertices normals + +f 90 1792 1 1782 +f 2 1784 83 1782 +f 1783 3 1784 2 +f 3 3624 1716 1784 +f 1785 1690 3655 5 +f 1786 73 1882 4 +f 1787 1667 1785 5 +f 4 1903 1668 1787 +f 1786 5 3635 6 +f 1759 1804 18 1788 +f 97 1808 23 1788 +f 69 1805 20 1789 +f 24 1809 25 1789 +f 7 1918 99 1790 +f 1763 3649 1764 1790 +f 1739 3634 22 1791 +f 8 1879 17 1791 +f 90 1919 89 1792 +f 9 3651 1 1792 +f 6 3635 16 1793 +f 70 1881 73 1793 +f 1750 1808 10 1794 +f 11 1932 1749 1794 +f 47 1809 12 1795 +f 13 2847 45 1795 +f 94 1923 93 1796 +f 1796 1720 3628 1723 +f 1797 1676 1798 94 +f 1798 14 1799 94 +f 15 1904 82 1800 +f 76 1801 1699 1800 +f 1801 1685 3611 1699 +f 89 1915 7 1802 +f 1764 3650 9 1802 +f 16 3632 1739 1803 +f 17 1880 70 1803 +f 19 1931 18 1804 +f 1759 3647 1761 1804 +f 1738 3638 20 1805 +f 69 1877 21 1805 +f 99 1931 19 1806 +f 1761 3648 1763 1806 +f 22 3639 1738 1807 +f 21 1878 8 1807 +f 1810 27 2494 26 +f 1811 612 1812 40 +f 510 2615 29 1813 +f 1814 28 1813 29 +f 1815 31 1814 29 +f 1815 616 2590 590 +f 31 1815 590 1816 +f 1816 30 2591 606 +f 31 1816 606 1817 +f 33 1818 32 1817 +f 1818 607 2592 593 +f 32 1818 593 1819 +f 1819 34 2593 36 +f 1820 35 1821 32 +f 1821 531 1822 32 +f 1822 608 1823 32 +f 1823 527 1829 32 +f 1824 526 1830 43 +f 586 2600 37 1825 +f 1825 40 1826 38 +f 614 2587 39 1827 +f 1827 40 2424 43 +f 1828 603 2599 614 +f 43 1833 585 1828 +f 1830 42 1831 43 +f 1831 41 1832 43 +f 1832 602 1833 43 +f 1833 601 2530 585 +f 1834 859 1840 44 +f 1835 753 1836 45 +f 1836 784 1837 45 +f 46 1875 47 1837 +f 1838 791 1846 859 +f 1839 781 1840 859 +f 1841 48 1839 859 +f 1842 805 1841 859 +f 1843 49 1842 859 +f 1844 760 1843 859 +f 1845 802 1844 859 +f 1846 801 1845 859 +f 1838 54 1847 50 +f 1848 51 1847 54 +f 1849 52 1848 54 +f 1850 795 1849 54 +f 1851 799 1850 54 +f 1852 53 1851 54 +f 1853 56 1854 55 +f 1854 1776 1855 55 +f 1856 57 1865 1776 +f 1857 828 1856 1776 +f 60 2805 61 1858 +f 1859 75 1894 60 +f 1860 823 1862 59 +f 1861 62 1859 60 +f 1862 850 1864 59 +f 1863 63 1861 60 +f 1865 857 1855 1776 +f 1866 58 1857 1776 +f 1867 64 1866 1776 +f 1868 768 1867 1776 +f 1869 65 1868 1776 +f 66 1869 1776 1864 +f 1870 140 1872 60 +f 1871 67 1863 60 +f 1872 132 1871 60 +f 59 1873 255 1858 +f 1874 493 2497 32 +f 1873 1773 1874 32 +f 1875 25 1809 47 +f 1875 46 2738 68 +f 1876 69 1789 25 +f 1876 68 2728 777 +f 1877 777 2757 780 +f 1878 780 2759 751 +f 1879 751 2734 750 +f 1880 750 2753 72 +f 1881 72 2752 71 +f 73 1881 71 1882 +f 1882 847 1883 4 +f 1884 82 1883 843 +f 1885 76 1884 74 +f 1886 817 1887 75 +f 1887 838 1888 75 +f 1888 813 1889 75 +f 1889 77 1890 75 +f 1890 78 1891 75 +f 1891 763 1892 75 +f 1892 810 1893 75 +f 1893 807 1894 75 +f 1885 79 1886 75 +f 1895 938 1901 1547 +f 1896 933 1895 1547 +f 1897 80 1899 81 +f 1898 867 1899 80 +f 1900 864 1902 945 +f 938 2966 945 1902 +f 864 3539 1578 1902 +f 82 1904 1670 1903 +f 93 1922 83 1905 +f 1905 1716 3626 1719 +f 1906 1720 1796 93 +f 121 1974 120 1907 +f 1907 1593 1909 84 +f 1908 123 1977 84 +f 1909 865 1908 84 +f 98 2722 742 1910 +f 1911 98 1931 99 +f 1912 85 1911 99 +f 1913 752 1912 99 +f 1914 86 1913 99 +f 1915 749 1920 7 +f 1916 756 1914 99 +f 1917 87 1916 99 +f 778 2733 88 1918 +f 1919 91 1915 89 +f 1920 778 1918 7 +f 1919 90 1921 773 +f 1921 83 1922 846 +f 92 2719 846 1922 +f 1923 841 2823 92 +f 1924 95 2825 841 +f 1924 94 1799 138 +f 1925 815 2798 95 +f 1925 138 1983 129 +f 1926 812 2820 815 +f 1926 129 1985 134 +f 1927 836 2794 812 +f 1927 134 1992 133 +f 1928 834 2795 836 +f 1928 133 1986 139 +f 1929 139 1997 125 +f 1929 96 2792 834 +f 11 1794 10 1910 +f 1930 98 1910 10 +f 97 1788 18 1930 +f 1931 98 1930 18 +f 1932 11 1910 742 +f 1933 804 1934 101 +f 1934 761 1935 101 +f 1935 748 1936 101 +f 1936 759 1937 101 +f 1937 800 1938 101 +f 100 1939 861 1938 +f 1939 789 1940 861 +f 1940 790 1941 861 +f 1941 102 1942 861 +f 1942 103 1943 861 +f 1943 794 1944 861 +f 1944 105 1945 861 +f 1945 104 1946 861 +f 1946 830 1947 861 +f 1947 106 1948 861 +f 1948 108 1949 861 +f 1949 107 1968 861 +f 1950 826 1967 115 +f 96 1929 125 1951 +f 126 1952 109 1951 +f 1953 808 1952 126 +f 1954 110 1953 126 +f 1955 111 1954 126 +f 1956 851 1955 126 +f 1957 821 1956 126 +f 1958 112 1959 848 +f 1959 629 1960 848 +f 126 2196 628 1958 +f 1961 818 1960 629 +f 1962 113 1963 766 +f 1964 746 1963 113 +f 1965 630 1966 114 +f 1969 116 1970 117 +f 1970 633 1971 117 +f 1971 118 1972 117 +f 1972 631 1975 117 +f 1973 119 1974 121 +f 1968 115 1969 117 +f 1976 122 1978 117 +f 1978 861 1968 117 +f 124 1977 123 1976 +f 1870 255 1979 125 +f 1979 126 1951 125 +f 1859 62 1980 127 +f 1981 128 1980 62 +f 1982 1693 1981 62 +f 1983 1702 1994 130 +f 1984 1711 1985 129 +f 1871 132 1986 133 +f 132 1872 139 1986 +f 1985 131 1987 134 +f 1987 1713 1988 134 +f 1988 135 1989 134 +f 1989 1701 1992 134 +f 1990 137 3613 136 +f 1861 63 1991 1691 +f 1991 136 1982 1691 +f 63 1992 1701 1991 +f 1992 63 1863 133 +f 1863 67 1871 133 +f 1799 1733 1993 138 +f 1993 1730 1994 138 +f 1994 1702 1983 138 +f 1801 76 1885 75 +f 1995 1685 1801 75 +f 75 1859 127 1996 +f 1872 140 1997 139 +f 140 1870 125 1997 +f 142 2034 1779 1998 +f 141 3512 149 1998 +f 1999 1756 2169 1548 +f 2000 143 1999 1548 +f 2001 1715 2000 1548 +f 2002 1725 2168 231 +f 2003 1672 2002 231 +f 2004 1727 2003 231 +f 2005 1728 2004 231 +f 2006 1705 2005 231 +f 2007 146 2008 1712 +f 2009 1663 2008 146 +f 2010 144 2009 146 +f 2011 145 2010 146 +f 2012 147 2011 146 +f 1692 3613 1695 2013 +f 2013 146 3503 1535 +f 2014 1692 2013 1535 +f 2015 1694 2014 1535 +f 2016 149 2017 1682 +f 2018 1666 2035 1769 +f 1535 3504 142 2016 +f 2019 1683 2017 149 +f 2020 1666 3602 148 +f 2020 149 3512 860 +f 156 2034 252 2021 +f 2022 156 2195 150 +f 151 3351 1345 2022 +f 2023 156 2022 1345 +f 1346 3353 1319 2023 +f 2024 156 2023 1319 +f 152 3344 1321 2024 +f 2025 1320 2026 156 +f 2026 153 2027 156 +f 2027 1348 2028 156 +f 2028 154 2029 156 +f 2029 155 2030 156 +f 2030 157 2033 156 +f 2031 1360 2032 156 +f 2032 1326 2126 156 +f 2033 1325 2031 156 +f 860 2843 1744 2035 +f 2036 1226 2037 166 +f 2037 158 2038 166 +f 2038 160 2039 166 +f 2039 159 2047 166 +f 2040 161 2041 162 +f 2041 1276 2042 162 +f 2042 1248 2043 162 +f 1249 3312 163 2043 +f 2044 162 2043 163 +f 1278 3311 164 2044 +f 2045 162 2044 164 +f 1247 3321 165 2045 +f 2046 162 2045 165 +f 2046 1274 3310 1246 +f 2048 162 2046 1246 +f 2049 1225 2137 866 +f 2050 1291 2049 866 +f 2051 167 2050 866 +f 2052 1289 2051 866 +f 2053 168 2052 866 +f 2054 1237 2053 866 +f 2055 169 2054 866 +f 2056 1229 2055 866 +f 2057 1261 2056 866 +f 2058 1262 2057 866 +f 2059 170 2058 866 +f 2060 1260 2059 866 +f 1258 3316 171 2061 +f 2061 866 2062 1301 +f 172 3327 1301 2062 +f 2062 866 2063 1300 +f 1236 3306 1300 2063 +f 2063 866 2064 1256 +f 1257 3315 1256 2064 +f 2064 866 2065 173 +f 2066 1287 2065 866 +f 2067 1285 2066 866 +f 2068 1284 2067 866 +f 2069 174 2068 866 +f 2070 175 2069 866 +f 2071 1253 2070 866 +f 2072 176 2071 866 +f 2073 1299 2072 866 +f 2074 1252 2073 866 +f 2075 177 2074 866 +f 2076 178 2075 866 +f 2077 1329 2078 179 +f 2079 200 2107 1374 +f 2080 180 2090 188 +f 2048 181 3314 1255 +f 2081 182 2082 162 +f 2082 1282 2083 162 +f 2084 1254 2085 1545 +f 2085 183 2086 1545 +f 2086 184 2087 1545 +f 2088 1306 2087 197 +f 2089 185 2088 186 +f 2090 187 3305 188 +f 2091 198 2104 1332 +f 2092 190 2105 199 +f 2093 191 2079 1374 +f 2094 203 2109 202 +f 2083 192 2084 1545 +f 2095 234 3511 1545 +f 2089 187 2090 1365 +f 2096 1303 2095 1545 +f 2097 193 2096 1545 +f 2098 194 2097 1545 +f 2099 1335 2098 1545 +f 2100 195 2099 1545 +f 2101 1307 2100 1545 +f 2102 1334 2101 1545 +f 2103 196 2102 1545 +f 180 3345 1365 2090 +f 2080 189 2091 1332 +f 2106 1361 2105 190 +f 2108 201 2107 200 +f 2077 866 2110 1304 +f 2111 213 2112 1363 +f 2113 204 2112 213 +f 2114 205 2113 213 +f 2115 206 2114 213 +f 2116 207 2115 213 +f 2117 208 2116 213 +f 2118 1373 2136 213 +f 2119 219 2135 213 +f 1375 3358 210 2120 +f 211 3338 217 2121 +f 2122 213 2132 214 +f 2123 212 2131 213 +f 2124 1322 2130 213 +f 2125 1328 2127 213 +f 2127 1358 2128 213 +f 2128 1359 2129 213 +f 2129 1350 2124 213 +f 2130 1353 2123 213 +f 2131 1354 2132 213 +f 2132 1355 3329 214 +f 2122 215 3339 1357 +f 2133 216 2121 213 +f 213 2121 217 2120 +f 2134 1305 2119 213 +f 2135 218 2118 213 +f 2136 209 2117 213 +f 2137 220 2138 866 +f 2139 221 2140 1548 +f 2140 1293 2141 1548 +f 2141 1239 2142 1548 +f 2142 222 2143 1548 +f 2143 1238 2144 1548 +f 2144 1266 2145 1548 +f 2145 1265 2146 1548 +f 2146 1263 2167 1548 +f 2147 1264 2148 230 +f 2148 1267 2149 230 +f 2149 1240 2150 230 +f 2150 223 2166 230 +f 2151 224 2152 166 +f 2152 225 2153 166 +f 2153 1233 2154 166 +f 2154 1268 2155 166 +f 2155 1235 2156 166 +f 2156 1234 2157 166 +f 2157 1273 2158 166 +f 2158 226 2159 166 +f 2159 227 2160 166 +f 2160 1245 2161 166 +f 1242 3304 1272 2161 +f 2162 166 2161 1272 +f 1241 3308 228 2162 +f 2163 166 2162 228 +f 229 3318 1270 2163 +f 2164 166 2163 1270 +f 1302 3328 1295 2164 +f 2165 1297 2036 166 +f 2167 230 3507 231 +f 1548 2167 231 2168 +f 2169 232 2846 1548 +f 143 3619 233 1999 +f 2095 1303 3330 235 +f 234 2095 235 2170 +f 2170 1309 3331 236 +f 234 2170 236 2171 +f 2171 1367 3356 1315 +f 234 2171 1315 2172 +f 2172 237 3340 238 +f 234 2172 238 2173 +f 2173 1368 3346 1337 +f 2174 240 2175 234 +f 2175 239 2176 234 +f 2176 241 2177 234 +f 2177 1317 2178 234 +f 2178 242 2179 234 +f 2179 243 2180 234 +f 2180 245 2181 234 +f 2181 244 2182 234 +f 2182 1310 2191 234 +f 2183 1311 2184 252 +f 2184 1343 2185 252 +f 2185 1342 2186 252 +f 2186 246 2187 252 +f 2187 1344 2188 252 +f 2188 247 2192 252 +f 248 3349 249 2189 +f 250 3348 253 2190 +f 251 3333 1369 2021 +f 2192 1312 3336 1340 +f 2193 1318 2189 252 +f 252 2189 249 2190 +f 2194 251 2021 252 +f 1347 3352 150 2195 +f 2195 156 2021 1369 +f 254 2616 628 2196 +f 2197 439 2419 254 +f 2198 1544 2197 254 +f 2196 126 1979 257 +f 2199 1534 2198 254 +f 2200 257 1979 255 +f 2201 255 1873 32 +f 2201 258 3504 256 +f 1544 3507 1539 2197 +f 2202 1536 3505 1537 +f 2203 1543 2205 453 +f 2204 258 2201 32 +f 2206 259 2207 43 +f 2207 1538 1829 43 +f 260 2227 272 2208 +f 2209 363 2319 372 +f 2210 262 2236 283 +f 2211 312 2269 264 +f 2212 338 2291 400 +f 263 2294 279 2212 +f 2213 271 2353 386 +f 2214 347 2312 365 +f 2215 349 2211 264 +f 2216 379 2326 378 +f 2217 289 2294 265 +f 2210 266 2349 267 +f 269 2230 268 2218 +f 398 2290 282 2218 +f 2219 275 2352 270 +f 2220 288 2219 270 +f 2221 356 2253 325 +f 353 2297 342 2215 +f 2222 273 2287 369 +f 344 2299 345 2223 +f 331 2223 330 2224 +f 332 2214 365 2225 +f 261 2209 372 2226 +f 332 2261 306 2214 +f 2216 334 2267 311 +f 2227 333 2323 272 +f 2228 267 2349 336 +f 2229 335 2290 399 +f 2228 337 2237 262 +f 2222 375 2293 277 +f 269 2232 339 2230 +f 274 2347 268 2230 +f 275 2219 324 2217 +f 2231 276 2232 269 +f 2233 685 2231 282 +f 2232 276 2663 671 +f 2234 284 2233 335 +f 2235 277 2292 339 +f 2236 285 2234 283 +f 2235 671 2641 650 +f 2237 646 2236 262 +f 2238 273 2222 277 +f 2239 684 2237 337 +f 2238 650 2639 278 +f 2240 286 2239 338 +f 2241 280 2287 273 +f 2241 278 2665 281 +f 2242 645 2240 279 +f 2243 654 2242 289 +f 2244 287 2243 324 +f 2245 290 2244 288 +f 2245 340 2246 681 +f 2246 292 2247 291 +f 727 2685 291 2247 +f 2247 292 2306 293 +f 2248 724 2710 727 +f 2248 293 2296 294 +f 2249 725 2708 724 +f 2249 294 2284 341 +f 2250 295 2691 725 +f 2250 341 2283 296 +f 2251 718 2706 295 +f 2251 296 2305 357 +f 2252 700 2689 718 +f 2252 357 2282 325 +f 2253 297 2689 700 +f 2254 298 2681 297 +f 2254 356 2281 299 +f 2255 299 2304 319 +f 2255 300 2714 298 +f 2256 331 2224 280 +f 2256 281 2643 677 +f 2257 344 2223 331 +f 2257 677 2667 302 +f 2258 301 2299 344 +f 2258 302 2629 643 +f 2259 303 2288 301 +f 2259 643 2629 304 +f 2260 332 2225 303 +f 2260 304 2661 305 +f 2261 305 2637 667 +f 2262 261 2226 306 +f 2262 667 2656 307 +f 2263 308 2209 261 +f 2263 307 2658 637 +f 308 2263 637 2264 +f 2264 636 2265 260 +f 2266 350 2265 309 +f 2267 334 2266 310 +f 2268 311 2267 705 +f 2269 312 2268 317 +f 2270 264 2269 318 +f 2271 353 2270 313 +f 2272 354 2271 711 +f 2273 343 2272 314 +f 2274 360 2273 315 +f 2275 323 2274 666 +f 2276 361 2275 316 +f 2277 732 2712 300 +f 2277 319 2295 321 +f 2278 735 2716 732 +f 2278 321 2303 320 +f 2279 716 2704 735 +f 2279 320 2302 322 +f 2280 713 2702 716 +f 2280 322 2301 351 +f 2276 713 2280 351 +f 2219 288 2244 324 +f 2281 356 2221 394 +f 358 2340 326 2282 +f 327 2339 390 2283 +f 2284 294 2296 389 +f 327 2283 341 2284 +f 323 2286 329 2285 +f 362 2330 329 2286 +f 2287 346 2314 369 +f 2288 366 2320 373 +f 2225 366 2288 303 +f 334 2216 378 2289 +f 2290 398 2348 399 +f 266 2210 283 2229 +f 2291 337 2228 336 +f 405 2355 274 2292 +f 2293 405 2292 277 +f 2294 289 2242 279 +f 2294 263 2351 265 +f 271 2213 340 2220 +f 2213 292 2246 340 +f 393 2342 392 2295 +f 326 2340 394 2221 +f 359 2334 389 2296 +f 354 2307 382 2297 +f 2285 328 2298 360 +f 2298 328 2328 384 +f 2299 373 2311 345 +f 2224 346 2287 280 +f 2226 347 2214 306 +f 2208 363 2209 308 +f 2211 349 2329 348 +f 312 2211 348 2300 +f 2289 333 2227 350 +f 2227 260 2265 350 +f 335 2233 282 2290 +f 397 2309 351 2301 +f 322 2302 352 2301 +f 355 2341 352 2302 +f 2303 355 2302 320 +f 393 2295 319 2304 +f 2305 296 2283 390 +f 359 2296 293 2306 +f 2306 292 2213 386 +f 2307 384 2332 382 +f 2307 354 2272 343 +f 2300 379 2216 311 +f 2303 321 2295 392 +f 395 2304 299 2281 +f 358 2282 357 2305 +f 2298 343 2273 360 +f 361 2276 351 2308 +f 396 2317 362 2308 +f 2309 396 2308 351 +f 2286 323 2275 361 +f 417 2369 411 2310 +f 363 2208 272 2310 +f 373 2320 374 2311 +f 364 2316 345 2311 +f 433 2366 406 2312 +f 406 2384 432 2313 +f 366 2225 365 2313 +f 346 2322 367 2314 +f 429 2315 369 2314 +f 368 2321 375 2315 +f 376 2322 330 2316 +f 370 2330 362 2317 +f 396 2356 371 2317 +f 412 2363 433 2318 +f 347 2226 372 2318 +f 411 2368 412 2319 +f 432 2383 374 2320 +f 2321 404 2293 375 +f 418 2370 417 2323 +f 377 2371 418 2324 +f 333 2289 378 2324 +f 383 2375 423 2325 +f 349 2215 342 2325 +f 420 2372 377 2326 +f 380 2328 328 2327 +f 329 2330 381 2327 +f 385 2332 384 2328 +f 423 2374 421 2329 +f 383 2325 342 2331 +f 382 2332 426 2331 +f 421 2373 420 2333 +f 379 2300 348 2333 +f 359 2306 386 2334 +f 2334 1541 2335 389 +f 2335 387 2338 389 +f 2336 388 2337 327 +f 2337 1536 2339 327 +f 327 2284 389 2338 +f 2339 1536 2202 358 +f 2202 453 2340 358 +f 453 2344 394 2340 +f 391 2345 352 2341 +f 2341 355 2303 392 +f 391 2341 392 2342 +f 2342 393 2304 395 +f 2343 391 2342 395 +f 453 2343 395 2344 +f 2309 397 2345 1531 +f 2345 397 2301 352 +f 391 3502 1531 2345 +f 2346 401 2349 266 +f 2347 455 2348 398 +f 2348 455 2346 266 +f 2349 401 2350 336 +f 400 2291 336 2350 +f 2350 401 2351 263 +f 2351 401 2352 275 +f 2352 401 2353 271 +f 2354 455 2347 274 +f 2355 402 2354 274 +f 403 2415 371 2356 +f 396 2309 1531 2356 +f 2357 402 2355 404 +f 2355 405 2293 404 +f 2358 651 2385 404 +f 2359 430 2384 406 +f 2360 674 2359 406 +f 2361 407 2360 406 +f 2362 678 2361 406 +f 2363 413 2367 433 +f 2364 408 2362 406 +f 2365 642 2364 406 +f 410 2637 409 2366 +f 2367 410 2366 433 +f 2368 668 2363 412 +f 2368 411 2369 414 +f 2369 417 2370 416 +f 415 2677 416 2370 +f 2371 419 2699 415 +f 2372 706 2695 419 +f 2373 649 2697 706 +f 2374 422 2635 649 +f 2375 424 2679 422 +f 2376 427 2701 424 +f 2376 383 2331 426 +f 2377 426 2332 385 +f 2377 425 2687 427 +f 2378 435 2390 439 +f 1533 2357 428 2379 +f 2379 404 2382 663 +f 2321 368 2380 430 +f 429 2314 367 2380 +f 2381 430 2380 367 +f 376 2316 364 2381 +f 2383 430 2381 364 +f 2385 434 2386 404 +f 2386 431 2382 404 +f 2387 664 2388 1533 +f 2388 647 2389 1533 +f 2389 661 2378 1533 +f 2390 436 2391 439 +f 2392 438 2422 439 +f 440 2393 437 2391 +f 655 2394 441 2393 +f 2394 659 2395 441 +f 2396 696 2397 632 +f 2395 658 2396 632 +f 2398 443 2399 442 +f 2400 702 2401 634 +f 2401 719 2414 634 +f 2402 720 2403 444 +f 2403 691 2404 444 +f 2404 445 2405 444 +f 2405 731 2406 444 +f 2397 728 2398 442 +f 2407 736 2408 371 +f 2408 712 2413 371 +f 2409 446 2683 425 +f 2409 385 2328 380 +f 2410 448 2693 446 +f 2410 380 2327 381 +f 2406 447 2407 371 +f 2411 449 2654 448 +f 2411 381 2330 370 +f 449 2411 370 2412 +f 371 2413 640 2412 +f 2415 444 2406 371 +f 2399 701 2400 634 +f 444 2416 450 2414 +f 2416 1573 2417 450 +f 2418 635 2417 1573 +f 2420 451 2419 439 +f 2421 627 2420 439 +f 2422 452 2421 439 +f 2423 1778 1901 1578 +f 43 2424 467 2425 +f 2424 475 2454 467 +f 2343 453 2416 444 +f 2205 43 2425 1578 +f 2426 1573 2416 453 +f 1542 2346 455 2427 +f 2427 1533 2428 454 +f 439 2197 1539 2428 +f 2429 1777 3657 456 +f 2430 1772 2429 456 +f 2431 492 2445 457 +f 2432 464 2442 1532 +f 2433 459 2438 462 +f 2434 1532 2435 463 +f 1532 2442 461 2435 +f 2436 687 2441 463 +f 2437 652 2433 463 +f 2438 501 2504 462 +f 2438 459 2640 676 +f 2439 676 2666 653 +f 2440 653 2644 679 +f 2440 511 2513 512 +f 2439 512 2503 501 +f 2441 672 2437 463 +f 686 2436 463 2435 +f 2443 458 2432 1532 +f 2444 465 2443 1532 +f 2446 657 2445 492 +f 2447 466 2446 492 +f 2448 680 2447 492 +f 2449 683 2448 492 +f 697 2449 492 2450 +f 467 2451 729 2450 +f 2452 726 2451 467 +f 2453 703 2452 467 +f 2454 721 2705 468 +f 477 2703 717 2455 +f 2456 491 2493 477 +f 2457 469 2459 505 +f 2458 470 2456 477 +f 2460 514 2458 477 +f 2461 734 2463 475 +f 2462 472 2460 477 +f 2463 692 2465 475 +f 2464 521 2462 477 +f 2465 474 2467 475 +f 2466 473 2464 477 +f 2467 722 2471 475 +f 2468 523 2466 477 +f 2469 1774 2472 1778 +f 505 2507 504 2455 +f 2470 476 2468 477 +f 2471 721 2454 475 +f 2423 467 2450 492 +f 2459 471 2461 475 +f 2473 478 2494 505 +f 2474 479 2505 511 +f 2474 679 2668 644 +f 2475 507 2499 479 +f 2475 644 2630 669 +f 2476 508 2511 507 +f 2476 669 2660 670 +f 2477 497 2502 508 +f 2477 670 2662 480 +f 2478 498 2501 497 +f 2478 480 2638 481 +f 2479 483 2509 498 +f 2479 481 2657 482 +f 2480 494 2510 483 +f 2480 482 2659 484 +f 494 2480 484 2481 +f 2481 485 2482 513 +f 2483 503 2482 486 +f 2484 515 2483 707 +f 2485 708 2486 491 +f 2486 487 2487 491 +f 2487 689 2488 491 +f 2488 488 2489 491 +f 2489 489 2490 491 +f 2490 695 2491 491 +f 2491 490 2492 491 +f 2492 641 2493 491 +f 2484 710 2485 491 +f 1813 28 1814 460 +f 2495 510 1813 460 +f 1532 2434 31 2496 +f 2431 1532 2496 32 +f 525 2522 495 2498 +f 494 2481 513 2498 +f 507 2511 621 2499 +f 605 2505 479 2499 +f 2500 491 2456 496 +f 2456 470 2517 496 +f 620 2589 617 2501 +f 617 2533 622 2502 +f 512 2513 499 2503 +f 500 2504 501 2503 +f 509 2512 462 2504 +f 502 2513 511 2505 +f 528 2524 597 2506 +f 503 2483 515 2506 +f 518 2518 504 2507 +f 505 2494 27 2507 +f 2468 476 2508 524 +f 506 2614 620 2509 +f 495 2613 506 2510 +f 622 2602 621 2511 +f 2512 510 2495 462 +f 2495 463 2433 462 +f 597 2523 525 2514 +f 513 2482 503 2514 +f 2515 609 2458 514 +f 2460 472 2462 519 +f 2516 528 2506 515 +f 2500 516 2516 515 +f 2470 504 2518 517 +f 2462 521 2519 519 +f 2464 473 2520 520 +f 2466 523 2521 522 +f 2522 550 2552 625 +f 2523 597 2524 552 +f 527 1823 569 1824 +f 34 1819 593 2525 +f 2526 583 1810 612 +f 1811 37 2600 604 +f 2527 577 2599 603 +f 1823 608 2604 569 +f 2528 600 2515 519 +f 2529 530 2574 529 +f 2530 584 2527 585 +f 42 1830 570 1831 +f 41 1831 570 2529 +f 2531 580 2580 587 +f 2532 586 1825 38 +f 33 2611 560 2534 +f 1821 35 2594 595 +f 2516 516 2595 599 +f 550 2522 525 2523 +f 532 2556 598 2535 +f 2536 524 2508 578 +f 575 2561 556 2536 +f 2537 533 2538 615 +f 2538 534 2609 615 +f 2539 542 2537 624 +f 2538 533 2664 673 +f 2540 543 2539 535 +f 2541 619 2590 534 +f 2542 675 2540 536 +f 2541 673 2626 688 +f 2543 537 2542 589 +f 2544 540 2603 619 +f 2545 538 2543 618 +f 2544 688 2676 665 +f 2546 546 2545 539 +f 2547 558 2591 540 +f 2547 665 2653 559 +f 2548 541 2546 545 +f 2549 544 2548 623 +f 2550 547 2549 548 +f 2551 549 2550 626 +f 2551 625 2552 638 +f 2552 550 2553 639 +f 551 2678 639 2553 +f 2553 550 2523 552 +f 2554 553 2700 551 +f 2554 552 2524 599 +f 2555 554 2696 553 +f 2555 599 2595 598 +f 2556 709 2698 554 +f 2557 555 2636 709 +f 2557 532 2605 610 +f 2558 690 2680 555 +f 2558 610 2605 600 +f 2559 690 2558 600 +f 2528 557 2560 698 +f 2560 699 2688 698 +f 2560 557 2596 556 +f 2561 694 2684 699 +f 2562 560 2611 558 +f 2562 559 2634 648 +f 2563 592 2534 560 +f 2563 648 2674 662 +f 2564 591 2592 592 +f 2564 662 2632 563 +f 2565 561 2525 591 +f 2565 563 2632 562 +f 2566 594 2593 561 +f 2566 562 2648 564 +f 2567 596 2594 594 +f 2567 564 2646 656 +f 2568 595 2594 596 +f 2568 656 2670 565 +f 2569 566 2604 595 +f 2569 565 2650 660 +f 566 2569 660 2570 +f 2570 682 2571 569 +f 2572 568 2571 567 +f 2573 570 2572 730 +f 2574 530 2573 571 +f 2575 529 2574 572 +f 2576 573 2575 723 +f 2577 584 2576 574 +f 2578 577 2577 693 +f 2579 613 2578 579 +f 2580 580 2579 733 +f 2581 587 2580 581 +f 2582 588 2581 582 +f 2583 704 2694 694 +f 2583 575 2536 578 +f 2584 576 2655 704 +f 2584 578 2606 611 +f 2585 715 2628 576 +f 2585 611 2607 583 +f 2586 715 2585 583 +f 2526 604 2582 714 +f 601 2598 573 2530 +f 41 2529 529 1832 +f 39 2531 587 1826 +f 2587 613 2531 39 +f 587 2581 588 2532 +f 2588 618 2543 589 +f 2589 620 2614 548 +f 2593 594 1820 36 +f 34 2525 561 2593 +f 1822 531 1821 595 +f 2517 609 2605 532 +f 2535 516 2500 496 +f 518 2606 578 2518 +f 2508 517 2518 578 +f 2521 524 2536 556 +f 2520 522 2596 557 +f 2519 520 2520 557 +f 526 2597 570 1830 +f 2597 568 2572 570 +f 2598 529 2575 573 +f 2598 601 1833 602 +f 586 2532 588 2600 +f 2588 502 2505 605 +f 509 2504 500 2601 +f 539 2610 621 2602 +f 2603 540 2591 30 +f 2534 592 2592 607 +f 35 1820 594 2594 +f 2604 566 2570 569 +f 608 1822 595 2604 +f 2605 609 2515 600 +f 2606 518 2507 27 +f 27 1810 583 2607 +f 526 1824 569 2597 +f 614 2599 577 2587 +f 535 2601 500 2608 +f 616 1815 29 2609 +f 623 2533 617 2589 +f 2603 590 2590 619 +f 606 2591 558 2611 +f 2608 499 2612 536 +f 2612 499 2513 502 +f 626 2614 506 2613 +f 618 2588 605 2610 +f 2602 622 2533 545 +f 2533 623 2548 545 +f 2612 589 2542 536 +f 615 2609 29 2615 +f 2512 509 2601 624 +f 2615 510 2512 624 +f 2601 535 2539 624 +f 2613 495 2522 625 +f 2614 626 2550 548 +f 1962 629 2420 627 +f 2421 452 1965 113 +f 1967 630 2422 438 +f 2392 437 1969 115 +f 112 1958 628 2616 +f 2419 451 1959 112 +f 1972 118 2620 442 +f 442 2399 634 2617 +f 437 2393 441 2618 +f 1970 116 2618 441 +f 441 2395 632 2619 +f 1971 633 2619 632 +f 632 2397 442 2620 +f 1974 119 2621 450 +f 2417 635 2622 120 +f 1973 631 2617 634 +f 634 2414 450 2621 +f 1593 1907 120 2622 +f 2418 1599 3548 1593 +f 414 2369 416 2623 +f 636 2264 637 2623 +f 638 2552 639 2624 +f 485 2481 484 2624 +f 434 2385 276 2625 +f 2386 434 2625 685 +f 2441 687 2626 673 +f 687 2436 688 2626 +f 316 2654 449 2627 +f 640 2413 713 2627 +f 641 2492 576 2628 +f 2493 641 2628 715 +f 2364 642 2629 302 +f 546 2660 669 2630 +f 644 2668 538 2630 +f 2390 435 2651 286 +f 2631 436 2390 286 +f 2632 465 2444 562 +f 2444 457 2648 562 +f 664 2387 285 2633 +f 2388 664 2633 646 +f 2442 464 2634 559 +f 464 2432 648 2634 +f 318 2269 317 2635 +f 2486 708 2698 709 +f 2367 413 2656 667 +f 2637 410 2367 667 +f 544 2549 547 2638 +f 674 2360 278 2639 +f 2359 674 2639 650 +f 543 2666 676 2640 +f 459 2642 542 2640 +f 430 2359 650 2641 +f 2358 430 2641 671 +f 652 2437 533 2642 +f 678 2362 677 2643 +f 2361 678 2643 281 +f 537 2668 679 2644 +f 653 2666 675 2644 +f 655 2393 440 2645 +f 654 2243 287 2645 +f 2446 466 2670 656 +f 2631 645 2647 440 +f 645 2242 654 2647 +f 2445 657 2646 564 +f 658 2395 659 2649 +f 290 2245 681 2649 +f 2448 683 2672 660 +f 661 2389 684 2651 +f 2443 465 2632 662 +f 663 2382 284 2652 +f 2387 663 2652 285 +f 665 2676 686 2653 +f 461 2442 559 2653 +f 666 2693 448 2654 +f 490 2491 704 2655 +f 2492 490 2655 576 +f 413 2363 668 2656 +f 547 2550 549 2657 +f 668 2368 414 2658 +f 549 2551 638 2659 +f 2365 409 2661 304 +f 2629 642 2365 304 +f 546 2546 541 2660 +f 2637 305 2661 409 +f 541 2548 544 2662 +f 651 2358 671 2663 +f 2385 651 2663 276 +f 2437 672 2664 533 +f 672 2441 673 2664 +f 407 2361 281 2665 +f 2360 407 2665 278 +f 408 2364 302 2667 +f 2362 408 2667 677 +f 2394 655 2645 287 +f 287 2244 290 2669 +f 2447 680 2650 565 +f 2396 658 2649 681 +f 2671 696 2396 681 +f 682 2570 660 2672 +f 2449 697 2686 682 +f 647 2388 646 2673 +f 2389 647 2673 684 +f 2432 458 2674 648 +f 458 2443 662 2674 +f 431 2386 685 2675 +f 2382 431 2675 284 +f 2436 686 2676 688 +f 309 2265 636 2677 +f 486 2482 485 2678 +f 313 2270 318 2679 +f 2487 487 2636 555 +f 2404 691 2681 298 +f 691 2403 297 2681 +f 692 2463 579 2682 +f 2465 692 2682 693 +f 314 2687 425 2683 +f 446 2693 315 2683 +f 489 2489 699 2684 +f 2490 489 2684 694 +f 2671 291 2685 728 +f 567 2571 682 2686 +f 697 2450 729 2686 +f 711 2701 427 2687 +f 488 2488 698 2688 +f 2489 488 2688 699 +f 2403 720 2689 297 +f 474 2465 693 2690 +f 2467 474 2690 574 +f 2400 701 2708 725 +f 572 2574 571 2692 +f 2453 468 2707 572 +f 695 2490 694 2694 +f 2491 695 2694 704 +f 705 2267 310 2695 +f 710 2484 707 2696 +f 317 2268 705 2697 +f 2485 710 2696 554 +f 310 2266 309 2699 +f 707 2483 486 2700 +f 313 2679 424 2701 +f 2559 698 2488 689 +f 2413 712 2702 713 +f 712 2408 716 2702 +f 2586 714 2703 477 +f 2408 736 2704 716 +f 736 2407 735 2704 +f 2703 714 2582 582 +f 2457 717 2703 582 +f 2402 719 2706 718 +f 2689 720 2402 718 +f 2705 721 2471 574 +f 2471 722 2467 574 +f 2401 702 2691 295 +f 723 2575 572 2707 +f 2705 723 2707 468 +f 701 2399 443 2708 +f 571 2573 730 2709 +f 2452 703 2692 571 +f 2398 728 2685 727 +f 730 2572 567 2711 +f 2451 726 2709 730 +f 732 2716 447 2712 +f 731 2405 300 2712 +f 2713 581 2580 733 +f 2461 471 2713 733 +f 2405 445 2714 300 +f 445 2404 298 2714 +f 734 2461 733 2715 +f 2463 734 2715 579 +f 2407 447 2716 735 +f 469 2457 582 2717 +f 2713 471 2459 469 +f 2718 773 1921 846 +f 2719 738 3624 737 +f 737 3621 772 2718 +f 2720 847 2721 739 +f 71 2755 775 2721 +f 2722 741 3644 740 +f 1933 742 2722 740 +f 1839 48 2723 782 +f 2724 745 1963 746 +f 1964 114 2836 1709 +f 2725 744 2724 746 +f 2726 1680 1867 768 +f 2727 1758 1916 87 +f 747 2757 777 2728 +f 68 2738 1736 2728 +f 2729 1754 3645 1753 +f 1939 100 2729 1753 +f 1846 791 2730 1743 +f 2731 758 1936 748 +f 1843 760 2732 1741 +f 1920 749 2749 1765 +f 2733 778 1920 1765 +f 779 3636 771 2734 +f 2735 1747 1912 752 +f 1836 753 2736 754 +f 2737 757 1914 756 +f 46 2765 1735 2738 +f 1942 102 2768 787 +f 2739 1757 1943 103 +f 2740 788 1847 51 +f 1848 52 2775 1746 +f 2741 1752 1937 759 +f 1844 802 2742 1742 +f 2743 762 1935 761 +f 1842 49 2744 1740 +f 811 2792 96 2745 +f 109 1952 809 2745 +f 2746 764 1892 763 +f 1891 78 2793 765 +f 2747 767 1961 766 +f 2748 1681 1868 65 +f 2749 749 1915 91 +f 769 2749 91 2750 +f 774 3620 770 2750 +f 769 3650 1765 2749 +f 2751 776 2752 1771 +f 2752 72 2753 1771 +f 771 3632 1771 2753 +f 91 1919 773 2754 +f 772 3621 774 2754 +f 2752 776 2755 71 +f 1917 88 2758 1762 +f 2756 1760 2727 87 +f 747 3633 1737 2757 +f 2733 1766 2758 88 +f 1737 3634 779 2759 +f 2760 1748 1911 85 +f 1835 44 2761 755 +f 2762 741 2722 98 +f 1840 781 2763 1734 +f 2764 783 1913 86 +f 784 1836 754 2765 +f 1944 794 2774 1707 +f 2766 785 2778 105 +f 2767 796 2777 795 +f 1850 799 2781 786 +f 1941 790 2770 1755 +f 2769 50 1847 788 +f 1940 789 1939 1753 +f 2771 1745 2730 791 +f 2769 792 2771 791 +f 2772 793 2774 794 +f 2773 1707 2774 793 +f 2776 1689 3653 1768 +f 1849 795 2777 1768 +f 2778 1714 1945 105 +f 2779 797 1946 104 +f 1851 53 2780 798 +f 2782 1754 2729 800 +f 2729 100 1938 800 +f 1845 801 2783 803 +f 2784 1751 1934 804 +f 1841 805 2785 743 +f 1954 111 2786 1704 +f 2787 60 1894 1678 +f 1894 807 2816 1678 +f 1952 808 2788 809 +f 2789 1732 2788 808 +f 2790 1677 2791 810 +f 2791 833 1893 810 +f 1731 2818 834 2792 +f 1890 77 2819 1687 +f 1675 2821 812 2794 +f 2794 836 2795 1710 +f 2795 835 3622 1710 +f 2796 1686 1889 813 +f 2797 1700 2796 813 +f 1888 838 2822 839 +f 814 2826 95 2798 +f 815 2820 1721 2798 +f 2799 840 1887 817 +f 1886 79 2827 816 +f 2800 1729 1960 818 +f 2801 820 3610 819 +f 1869 66 2801 819 +f 1957 848 2831 822 +f 2802 852 1956 821 +f 2803 824 1862 823 +f 1955 851 2804 825 +f 2804 1706 3617 825 +f 2787 806 3608 853 +f 1950 107 2809 827 +f 2806 1673 1966 826 +f 1857 58 2807 1696 +f 1949 108 2840 1724 +f 2808 1726 2809 107 +f 1856 828 2810 829 +f 1947 830 2813 831 +f 2811 832 1948 106 +f 1855 857 2812 1665 +f 1852 55 2814 1664 +f 1953 110 2815 1703 +f 2817 835 2795 834 +f 2818 837 2817 834 +f 2820 812 2821 1722 +f 1717 2829 92 2823 +f 841 2825 1718 2823 +f 842 3605 845 2824 +f 74 1884 843 2824 +f 2825 95 2826 844 +f 845 3605 1671 2828 +f 79 1885 74 2828 +f 2829 738 2719 92 +f 2830 842 2824 843 +f 2720 1669 2830 843 +f 2832 849 2831 848 +f 2833 1679 2834 850 +f 2834 820 2801 850 +f 2801 66 1864 850 +f 2804 851 1956 852 +f 2805 853 3609 854 +f 1860 61 2805 854 +f 2835 1674 2836 114 +f 1866 64 2837 1697 +f 2838 1698 2837 64 +f 2839 855 2840 108 +f 1865 57 2841 856 +f 2842 858 2841 57 +f 2843 860 2844 859 +f 863 3640 1744 2843 +f 860 1853 54 2844 +f 101 1938 861 2845 +f 1548 2846 862 2845 +f 2847 863 1834 45 +f 1932 101 2848 1749 +f 1900 81 1908 865 +f 123 1908 81 2849 +f 1899 867 1898 213 +f 2111 866 2849 81 +f 866 2138 122 2849 +f 2850 124 1976 117 +f 2851 1022 2850 117 +f 2852 1021 2851 117 +f 2853 868 2852 117 +f 2854 1108 2853 117 +f 2855 1059 2854 117 +f 2856 869 2855 117 +f 2857 1054 2856 117 +f 2858 870 2857 117 +f 2859 871 2858 117 +f 2860 1068 2859 117 +f 2861 872 2860 117 +f 2862 1063 2861 117 +f 2863 874 2864 873 +f 2865 1075 2866 874 +f 2866 875 2867 874 +f 2867 1039 2868 874 +f 2868 876 2869 874 +f 2869 877 2870 874 +f 2870 878 2871 874 +f 2871 1031 2872 874 +f 2872 879 2873 874 +f 2873 881 2874 874 +f 2874 880 2875 874 +f 2875 882 2876 874 +f 2876 1065 2864 874 +f 2850 1022 3183 1117 +f 2877 883 2878 124 +f 2878 884 2879 124 +f 2879 885 2880 124 +f 2880 1116 2881 124 +f 2881 886 2882 124 +f 2882 1114 2883 124 +f 2883 1045 2884 124 +f 2884 887 2885 124 +f 2885 1112 2886 124 +f 2886 888 2887 124 +f 2887 889 2893 124 +f 2888 1026 2889 891 +f 2889 894 2894 891 +f 2890 891 2891 890 +f 2892 1023 2891 891 +f 2894 893 2895 891 +f 2895 895 2896 891 +f 2896 1093 2897 891 +f 2897 896 2898 891 +f 2898 897 2899 891 +f 2899 898 2900 891 +f 2900 899 2901 891 +f 2901 1052 2902 891 +f 2902 892 2892 891 +f 2903 900 2904 904 +f 2890 874 2903 904 +f 905 2979 952 2904 +f 121 1907 84 2905 +f 902 2987 901 2905 +f 2906 902 2905 84 +f 2893 891 2908 903 +f 1977 124 2893 903 +f 904 2904 952 2907 +f 2907 946 2908 891 +f 2909 905 2904 900 +f 2910 953 2903 874 +f 2911 906 1975 121 +f 2863 117 1975 906 +f 2912 940 2913 907 +f 2914 1161 2913 940 +f 2915 1160 2914 940 +f 2916 1210 2915 940 +f 2917 908 2916 940 +f 2918 909 2917 940 +f 2919 910 2918 940 +f 2920 911 2919 940 +f 2921 1171 2920 940 +f 2922 912 2921 940 +f 2923 1132 2922 940 +f 2924 1213 2923 940 +f 2925 913 2924 940 +f 2926 933 2927 914 +f 2928 916 2929 933 +f 2929 917 2930 933 +f 2930 1139 2931 933 +f 2931 918 2932 933 +f 2932 1177 2933 933 +f 2933 919 2934 933 +f 2934 920 2935 933 +f 2935 1142 2936 933 +f 2936 921 2937 933 +f 2937 922 2938 933 +f 2938 915 2927 933 +f 2939 923 2940 927 +f 2940 924 2941 927 +f 2941 1192 2942 927 +f 2942 925 2943 927 +f 2943 926 2944 927 +f 2944 928 2945 927 +f 2945 1218 2946 927 +f 2946 1190 2947 927 +f 2947 1148 2948 927 +f 2948 1187 2949 927 +f 2949 929 2950 927 +f 2950 930 2951 927 +f 2951 931 2959 927 +f 2952 1201 2953 80 +f 2953 1200 2960 80 +f 1896 80 2954 932 +f 2955 1120 2954 80 +f 2956 1207 2955 80 +f 2957 934 2956 80 +f 2958 1130 2957 80 +f 2960 1197 2961 80 +f 2961 1198 2962 80 +f 2962 1156 2963 80 +f 2963 935 2964 80 +f 2964 1204 2958 80 +f 942 2970 944 2965 +f 936 3056 1000 2965 +f 937 3047 993 2966 +f 2967 937 2966 938 +f 2926 940 2969 939 +f 1895 933 2926 939 +f 942 2965 1000 2968 +f 2968 941 2969 940 +f 2912 927 2970 942 +f 2971 936 2965 944 +f 2972 943 2970 927 +f 2973 995 1897 945 +f 2959 80 1897 995 +f 2908 946 2974 947 +f 2975 958 2910 906 +f 2976 950 3028 981 +f 905 2909 958 2977 +f 2977 957 2983 950 +f 2976 949 2978 951 +f 2979 905 2977 950 +f 2979 951 2980 952 +f 2980 954 2981 952 +f 948 2974 946 2981 +f 2909 953 2910 958 +f 2982 962 2988 954 +f 2980 951 2978 955 +f 956 2985 960 2983 +f 956 2983 957 2984 +f 2975 959 2985 956 +f 2906 903 2908 947 +f 2911 901 2985 959 +f 2985 901 2987 960 +f 961 3015 974 2986 +f 2987 955 2986 960 +f 2982 955 2987 902 +f 2988 962 2982 902 +f 2989 963 2990 961 +f 2988 947 2974 954 +f 2974 948 2981 954 +f 2991 965 2990 963 +f 2992 964 2991 963 +f 2993 1092 2992 963 +f 2994 966 2993 963 +f 2995 968 2994 963 +f 2996 967 2995 963 +f 2997 1086 2996 963 +f 2998 1089 2997 963 +f 2999 969 2998 963 +f 3000 970 2999 963 +f 3001 1081 3000 963 +f 3002 1083 3001 963 +f 1098 3002 963 3003 +f 3003 971 3004 1099 +f 3005 1047 3004 971 +f 3006 1095 3005 971 +f 3007 1096 3006 971 +f 3008 1050 3007 971 +f 3009 972 3008 971 +f 3010 1102 3009 971 +f 3011 1100 3010 971 +f 3012 973 3011 971 +f 3013 1106 3012 971 +f 3014 1104 3013 971 +f 3015 975 3016 974 +f 3016 1053 3017 974 +f 3017 976 3018 974 +f 3018 1061 3019 974 +f 3019 1060 3020 974 +f 3020 977 3021 974 +f 3021 1056 3022 974 +f 3022 978 3023 974 +f 3023 979 3024 974 +f 3024 1037 3025 974 +f 3025 980 3026 974 +f 3026 1067 3027 974 +f 3027 1066 3028 974 +f 3029 1080 3030 981 +f 3030 1043 3031 981 +f 3031 982 3032 981 +f 3032 983 3033 981 +f 3033 1076 3034 981 +f 3034 985 3035 981 +f 3035 984 3036 981 +f 3036 1040 3037 981 +f 3037 1041 3038 981 +f 3038 1074 3039 981 +f 3039 986 3040 981 +f 3040 949 2976 981 +f 2989 955 2978 971 +f 950 2983 960 3041 +f 2972 995 3042 987 +f 3043 990 2969 941 +f 3044 988 3097 1018 +f 937 2967 990 3045 +f 3045 989 3051 988 +f 3044 1012 3046 991 +f 3047 937 3045 988 +f 3047 991 3048 993 +f 3048 994 3049 993 +f 2973 993 3049 992 +f 2967 939 2969 990 +f 3050 996 3057 994 +f 3048 991 3046 1020 +f 998 3054 997 3051 +f 998 3051 989 3052 +f 3043 999 3053 998 +f 2971 943 2972 987 +f 999 3043 941 3053 +f 1000 3054 998 3053 +f 1184 3084 1015 3055 +f 3054 1000 3056 1020 +f 3055 997 3054 1020 +f 3050 1020 3056 936 +f 3057 996 3050 936 +f 3058 1004 3059 1184 +f 3057 987 3042 994 +f 3042 992 3049 994 +f 3060 1185 3059 1004 +f 3061 1224 3060 1004 +f 3062 1193 3061 1004 +f 3063 1001 3062 1004 +f 3064 1002 3063 1004 +f 3065 1220 3064 1004 +f 3066 1003 3065 1004 +f 3067 1151 3066 1004 +f 3068 1188 3067 1004 +f 3069 1189 3068 1004 +f 3070 1005 3069 1004 +f 3071 1121 3070 1004 +f 1123 3071 1004 3072 +f 3072 1006 3073 1203 +f 3074 1153 3073 1006 +f 3075 1155 3074 1006 +f 3076 1007 3075 1006 +f 3077 1125 3076 1006 +f 3078 1008 3077 1006 +f 3079 1010 3078 1006 +f 3080 1009 3079 1006 +f 3081 1131 3080 1006 +f 3082 1011 3081 1006 +f 3083 1208 3082 1006 +f 3084 1162 3085 1015 +f 3085 1013 3086 1015 +f 3086 1165 3087 1015 +f 3087 1169 3088 1015 +f 3088 1211 3089 1015 +f 3089 1167 3090 1015 +f 3090 1166 3091 1015 +f 3091 1172 3092 1015 +f 3092 1137 3093 1015 +f 3093 1136 3094 1015 +f 3094 1216 3095 1015 +f 3095 1014 3096 1015 +f 3096 1170 3097 1015 +f 3098 1147 3099 1018 +f 3099 1016 3100 1018 +f 3100 1129 3101 1018 +f 3101 1144 3102 1018 +f 3102 1182 3103 1018 +f 3103 1178 3104 1018 +f 3104 1176 3105 1018 +f 3105 1141 3106 1018 +f 3106 1017 3107 1018 +f 3107 1173 3108 1018 +f 3108 1019 3109 1018 +f 3109 1012 3044 1018 +f 988 3051 997 3110 +f 3058 1020 3046 1006 +f 3015 961 3111 1387 +f 3112 1290 2851 1021 +f 3113 1436 3014 949 +f 2891 1023 3114 1024 +f 2888 889 3170 1025 +f 3115 1027 2889 1026 +f 3116 1082 3001 1083 +f 3002 1098 3189 1447 +f 2898 896 3139 1028 +f 3117 897 2898 1028 +f 3118 1049 3007 1050 +f 3008 972 3009 1030 +f 2871 878 2870 1298 +f 3119 1275 2872 1031 +f 3120 985 3034 1077 +f 3034 1076 3167 1077 +f 2874 881 3131 1042 +f 3121 1271 2875 880 +f 3122 1032 3032 982 +f 3031 1043 3134 1377 +f 2901 899 3123 1033 +f 3124 1034 3011 973 +f 2860 872 3151 1035 +f 3125 1036 2859 1068 +f 3126 1038 3025 1037 +f 3024 979 3156 1423 +f 2858 871 3155 1232 +f 3127 1269 2857 870 +f 3128 1069 3023 978 +f 3022 1056 3148 1431 +f 2868 1039 3129 1250 +f 3130 1419 3037 1040 +f 2873 879 3166 1296 +f 3132 1078 3033 983 +f 3133 1079 2876 882 +f 3030 1080 3169 1044 +f 2884 1045 3174 1259 +f 3135 1288 2885 887 +f 3136 1088 2997 1089 +f 2998 969 3173 1084 +f 2895 893 3186 1251 +f 3137 1046 2896 895 +f 3138 1097 3004 1047 +f 3005 1095 3185 1407 +f 2897 1093 3184 1094 +f 3140 1048 3006 1096 +f 2902 1052 3141 1051 +f 3142 1411 3012 1106 +f 3143 1231 2852 868 +f 3016 975 3144 1444 +f 3145 1107 2854 1059 +f 3018 976 3146 1430 +f 3147 1054 2857 1269 +f 3021 977 3020 1110 +f 3149 1058 2855 869 +f 3019 1061 3150 1445 +f 2861 1063 3198 1062 +f 3152 1064 3026 980 +f 2864 1065 3168 1244 +f 3153 1243 2862 873 +f 3154 1402 3029 1066 +f 3027 1067 3199 1111 +f 2866 1075 3157 1073 +f 3158 1071 3039 1074 +f 2869 876 3159 1277 +f 3160 1072 3036 984 +f 2870 877 3161 1298 +f 3120 1420 3385 1400 +f 3035 985 3120 1400 +f 2867 875 3162 1279 +f 3163 1397 3038 1041 +f 3164 1378 3040 986 +f 2865 890 3165 1070 +f 2887 888 3200 1283 +f 3171 1439 3000 1081 +f 3172 1286 2886 1112 +f 2999 970 3201 1113 +f 2883 1114 3202 1085 +f 3175 1087 2996 1086 +f 3176 1090 2881 1116 +f 2995 967 3177 1091 +f 3177 1415 3399 1091 +f 3178 1227 2879 884 +f 2993 966 3179 1389 +f 3180 1228 2878 883 +f 2992 1092 3181 1412 +f 2990 965 3182 1393 +f 1022 2851 1290 3183 +f 1292 3205 1117 3183 +f 2894 894 3188 1280 +f 3187 1099 3004 1097 +f 3187 1424 3189 1098 +f 2900 898 3190 1281 +f 3191 1425 3010 1100 +f 2899 897 3117 1101 +f 3117 1029 3313 1101 +f 3192 1030 3009 1102 +f 2892 892 3193 1103 +f 3194 1105 3013 1104 +f 3195 1294 2853 1108 +f 3017 1053 3196 1109 +f 3147 1055 3317 1057 +f 2856 1054 3147 1057 +f 3020 1060 3197 1110 +f 2882 886 2881 1090 +f 3177 967 2996 1087 +f 3203 1115 2880 885 +f 2994 968 3204 1417 +f 3205 1230 2877 1117 +f 2991 964 3206 1394 +f 3084 1184 3207 1529 +f 3208 1119 2913 1161 +f 3209 1451 3083 1012 +f 2954 1120 3210 1323 +f 2951 930 3268 1186 +f 3211 1371 2952 931 +f 3212 1526 3070 1121 +f 3071 1123 3285 1122 +f 2963 1156 3235 1124 +f 3213 935 2963 1124 +f 3214 1157 3076 1125 +f 3077 1008 3078 1206 +f 2934 919 2933 1370 +f 3215 1181 2935 920 +f 3216 1178 3103 1126 +f 3103 1182 3263 1126 +f 2937 921 3227 1127 +f 3217 1145 2938 922 +f 3218 1457 3101 1129 +f 3100 1016 3230 1128 +f 2957 1130 3219 1351 +f 3220 1468 3080 1131 +f 2922 1132 3247 1133 +f 3221 1134 2921 912 +f 3222 1504 3094 1136 +f 3093 1137 3252 1135 +f 2920 1171 3251 1138 +f 3223 1339 2919 911 +f 3224 1481 3092 1172 +f 3091 1166 3244 1517 +f 2931 1139 3225 1175 +f 3226 1498 3106 1141 +f 2936 1142 3262 1143 +f 3228 1479 3102 1144 +f 3229 1146 2927 915 +f 3099 1147 3265 1501 +f 2947 1190 3272 1314 +f 3231 1330 2948 1148 +f 3232 1149 3066 1151 +f 3067 1188 3271 1150 +f 2960 1200 3282 1349 +f 3233 1152 2961 1197 +f 3234 1507 3073 1153 +f 3074 1155 3281 1154 +f 2962 1198 3280 1313 +f 3236 1199 3075 1007 +f 2956 934 3237 1158 +f 3238 1485 3081 1011 +f 3239 1118 2914 1160 +f 3085 1162 3240 1163 +f 3241 1209 2916 908 +f 3087 1165 3242 1164 +f 3243 910 2919 1339 +f 3090 1167 3089 1503 +f 3245 1336 2917 909 +f 3088 1169 3246 1168 +f 2923 1213 3294 1214 +f 3248 1215 3095 1216 +f 2925 914 3264 1341 +f 3249 1212 2924 913 +f 3250 1183 3098 1170 +f 3096 1014 3295 1466 +f 2929 916 3253 1179 +f 3254 1473 3108 1173 +f 2932 918 3255 1174 +f 3256 1476 3105 1176 +f 2933 1177 3257 1370 +f 3216 1478 3456 1499 +f 3104 1178 3216 1499 +f 2930 917 3258 1140 +f 3259 1180 3107 1017 +f 3260 1450 3109 1019 +f 2928 932 3261 1324 +f 3059 1185 3266 1470 +f 3267 1366 2939 907 +f 2950 929 3296 1362 +f 3269 1217 3069 1005 +f 3270 1364 2949 1187 +f 3068 1189 3297 1523 +f 2946 1218 3298 1219 +f 3273 1191 3065 1003 +f 3274 1331 2944 926 +f 3064 1220 3275 1493 +f 3275 1221 3470 1493 +f 3276 1333 2942 1192 +f 3062 1001 3277 1196 +f 3278 1194 2941 924 +f 3061 1193 3279 1195 +f 2953 1201 3284 1372 +f 3283 1203 3073 1507 +f 3283 1202 3285 1123 +f 2958 1204 3286 1352 +f 3287 1483 3079 1009 +f 2964 935 3213 1205 +f 3213 1356 3329 1205 +f 3288 1206 3078 1010 +f 2955 1207 3289 1327 +f 3290 1159 3082 1208 +f 3291 1308 2915 1210 +f 3086 1013 3292 1515 +f 3243 1316 3347 1338 +f 2918 910 3243 1338 +f 3089 1211 3293 1503 +f 2945 928 2944 1331 +f 3275 1220 3065 1191 +f 3299 1222 2943 925 +f 3063 1002 3300 1492 +f 3301 1223 2940 923 +f 3060 1224 3302 1471 +f 2049 1291 2050 1290 +f 3112 1231 2137 1225 +f 1257 2064 173 3303 +f 2036 1297 2165 1296 +f 2076 179 3184 1046 +f 2058 170 3203 1227 +f 2055 1229 2056 1228 +f 3180 1230 2053 1237 +f 2054 169 2055 1228 +f 2140 221 3143 1294 +f 2139 220 2137 1231 +f 3155 1036 2155 1268 +f 2154 1233 2153 1232 +f 3125 1035 2157 1234 +f 2156 1235 2155 1036 +f 1244 3168 1272 3304 +f 3139 1094 2079 191 +f 2093 190 3323 1028 +f 1029 2091 189 3305 +f 2082 182 2081 1103 +f 3306 1259 3174 1300 +f 3205 1292 2052 168 +f 2143 222 2142 1107 +f 2142 1239 2141 1107 +f 1055 3147 1269 3307 +f 2150 1240 2149 1055 +f 2152 224 3127 1232 +f 2153 225 2152 1232 +f 1079 3133 228 3308 +f 3153 1244 3304 1242 +f 1242 2161 1245 3309 +f 2160 227 3198 1243 +f 1070 3165 1246 3310 +f 2041 161 2040 1298 +f 3161 1277 2042 1276 +f 3311 1279 3162 1247 +f 3162 1073 3321 1247 +f 3312 1249 2043 1248 +f 3312 1250 3129 163 +f 3129 1279 3320 163 +f 3186 1280 2073 1252 +f 2074 177 3137 1251 +f 2071 176 3188 1027 +f 3141 1033 2084 192 +f 3190 1101 3313 186 +f 3313 187 2089 186 +f 3314 1024 3114 1255 +f 3114 1103 2081 1255 +f 1259 3306 1256 3315 +f 3135 1259 3315 1257 +f 1258 2061 1301 3316 +f 1236 2063 1256 3306 +f 2069 175 3115 1025 +f 3176 1115 2059 1260 +f 2060 171 3316 1090 +f 3178 1228 2056 1261 +f 2057 1262 2058 1227 +f 2144 1238 3145 1058 +f 2147 1263 3317 1055 +f 1263 2146 1057 3317 +f 2145 1266 2144 1058 +f 2148 1264 2147 1055 +f 2149 1267 2148 1055 +f 2151 223 3307 1269 +f 3121 1042 3318 229 +f 1271 3121 229 3319 +f 3133 1271 3319 228 +f 3308 1241 2162 1272 +f 3168 1079 3308 1272 +f 3151 1062 2158 1273 +f 2159 226 2158 1062 +f 3310 1274 2046 165 +f 2039 160 2038 1298 +f 2037 1226 3166 1275 +f 3159 1250 3312 1248 +f 1279 3311 1278 3320 +f 3157 1070 3310 165 +f 2109 200 2079 1094 +f 2072 1299 2073 1280 +f 2086 183 2085 1033 +f 2085 1254 2084 1033 +f 3123 1281 3322 184 +f 3322 197 2087 184 +f 1281 3190 186 3322 +f 1029 3117 1028 3323 +f 2092 198 2091 1029 +f 3193 1051 3324 1282 +f 1051 3141 192 3324 +f 3165 1024 3314 1246 +f 3314 181 2048 1246 +f 2067 1284 2068 1283 +f 2068 174 3170 1283 +f 2066 1285 3200 1286 +f 2065 1287 3172 1288 +f 2070 1253 2071 1027 +f 2051 1289 2052 1292 +f 2050 167 3325 1290 +f 167 2051 1292 3325 +f 2141 1293 3195 1107 +f 2146 1265 3149 1057 +f 2165 1295 3328 1296 +f 2040 159 2039 1298 +f 2038 158 3119 1298 +f 2075 178 2076 1046 +f 1085 3202 172 3326 +f 3174 1085 3326 1300 +f 1090 3316 1301 3327 +f 3202 1090 3327 172 +f 1042 3131 1302 3318 +f 3131 1296 3328 1302 +f 2078 202 2109 1094 +f 1355 3354 1205 3329 +f 1356 3339 214 3329 +f 3208 1118 3331 235 +f 1309 2170 235 3331 +f 1329 2077 1304 3332 +f 1143 3262 1347 3333 +f 2134 210 3280 1152 +f 1306 2088 185 3334 +f 2101 1334 2102 1194 +f 3278 1223 2099 195 +f 2100 1307 2101 1194 +f 1308 3291 1367 3335 +f 3239 1308 3335 236 +f 3331 1118 3239 236 +f 3251 1134 2183 1310 +f 2182 244 2181 1138 +f 3221 1133 2185 1343 +f 2184 1311 2183 1134 +f 1341 3264 1340 3336 +f 3235 1313 3338 211 +f 3337 1124 3235 211 +f 3339 215 2122 214 +f 2127 1328 2125 1327 +f 2107 201 2108 1314 +f 3301 1366 2098 1335 +f 3340 1209 3241 238 +f 237 2172 1315 3340 +f 1316 3243 1339 3341 +f 2178 1317 2177 1316 +f 2180 243 3223 1138 +f 2181 245 2180 1138 +f 1146 3229 248 3342 +f 2030 155 3253 1324 +f 152 2024 1319 3343 +f 3255 1175 3344 152 +f 2025 1321 3344 1175 +f 3225 1140 2026 1320 +f 2027 153 2026 1140 +f 2117 209 3284 1371 +f 2130 1322 2124 1351 +f 3261 1323 2031 1325 +f 2125 1326 3210 1327 +f 2094 1329 3231 1314 +f 2104 199 2105 1331 +f 2108 203 2094 1314 +f 2115 207 3211 1186 +f 180 2080 1332 3345 +f 3276 1194 2102 196 +f 2103 1306 3334 1333 +f 3346 1336 3245 1337 +f 2175 240 3347 1316 +f 240 2174 1338 3347 +f 2174 1337 3245 1338 +f 2176 239 2175 1316 +f 2177 241 2176 1316 +f 2179 242 3341 1339 +f 3348 250 2190 249 +f 3217 1127 3348 249 +f 1145 3217 249 3349 +f 3229 1145 3349 248 +f 2193 1340 3264 1146 +f 3247 1214 2186 1342 +f 2188 1344 3294 1212 +f 3249 1341 3336 1312 +f 1312 2192 247 3350 +f 2187 246 2186 1214 +f 2033 157 2030 1324 +f 1370 3353 1346 3351 +f 1181 3215 150 3352 +f 3262 1181 3352 1347 +f 3257 1174 3343 1319 +f 2028 1348 2027 1140 +f 3258 1179 2029 154 +f 3233 1349 2135 219 +f 2119 1305 2134 1152 +f 3282 1372 2118 218 +f 2124 1350 2129 1351 +f 3219 1352 2123 1353 +f 3286 1205 3354 1354 +f 2131 212 2123 1352 +f 1356 2133 1357 3339 +f 3337 216 2133 1356 +f 3289 1158 2128 1358 +f 3237 1351 2129 1359 +f 2032 1360 2031 1323 +f 2105 1361 3298 1331 +f 2113 205 2114 1362 +f 2114 206 3268 1362 +f 2112 204 3296 1364 +f 2110 1363 3270 1330 +f 2116 208 2117 1371 +f 3274 1222 3355 1365 +f 3345 1331 3274 1365 +f 3299 1333 3334 185 +f 185 2089 1365 3355 +f 2097 194 2098 1366 +f 2096 193 3267 1119 +f 1209 3340 1315 3356 +f 3291 1209 3356 1367 +f 3346 1368 2173 238 +f 3241 1336 3346 238 +f 1370 3351 151 3357 +f 3215 1370 3357 150 +f 1313 3358 1375 3338 +f 2136 1373 2118 1372 +f 2106 1374 3272 1219 +f 1127 3227 253 3348 +f 2194 253 3227 1143 +f 3111 1393 3359 1376 +f 3359 1604 3543 1376 +f 3360 1556 3519 1565 +f 3361 1553 3122 1377 +f 3134 1044 3404 1552 +f 3362 1605 3552 1603 +f 3363 1399 3563 1379 +f 3113 1378 3363 1379 +f 3364 1589 3545 1592 +f 3365 1380 3522 1381 +f 3366 1382 3544 1588 +f 3367 1383 3572 1626 +f 3368 1384 3554 1606 +f 1385 3167 1078 3369 +f 1390 3132 1032 3370 +f 1600 3409 1423 3371 +f 3366 1387 3111 1376 +f 3372 1621 3561 1388 +f 3362 1394 3206 1413 +f 3181 1389 3373 1607 +f 3374 1426 3559 1610 +f 3375 1419 3130 1557 +f 3375 1561 3524 1567 +f 3376 1032 3122 1553 +f 3377 1554 3369 1078 +f 3376 1391 3516 1386 +f 3378 1402 3154 1549 +f 3154 1111 3410 1549 +f 1446 3189 1424 3379 +f 3124 1411 3372 1388 +f 3380 1034 3124 1388 +f 3359 1393 3182 1603 +f 3182 1394 3362 1603 +f 3381 1417 3204 1414 +f 3382 1084 3397 1396 +f 3365 1071 3158 1398 +f 3383 1398 3158 1397 +f 3384 1563 3520 1557 +f 3130 1072 3384 1557 +f 3363 1378 3164 1381 +f 3164 1071 3365 1381 +f 3384 1072 3160 1568 +f 3385 1564 3527 1568 +f 3160 1400 3385 1568 +f 3360 1420 3403 1401 +f 3386 1077 3167 1385 +f 3169 1402 3378 1550 +f 3378 1403 3515 1550 +f 3387 1404 3197 1445 +f 3150 1430 3406 1594 +f 3388 1064 3152 1601 +f 3156 1069 3389 1405 +f 3389 1598 3549 1405 +f 1406 3420 1407 3390 +f 1632 3390 1407 3391 +f 3118 1030 3392 1408 +f 3380 1622 3568 1623 +f 3192 1425 3416 1409 +f 3393 1438 3367 1030 +f 3372 1411 3394 1620 +f 3142 1105 3419 1410 +f 3206 1412 3395 1413 +f 1612 3422 1439 3396 +f 3173 1113 3421 1611 +f 3398 1609 3557 1608 +f 3204 1091 3399 1414 +f 3399 1416 3556 1414 +f 3400 1416 3399 1415 +f 3179 1417 3381 1418 +f 3381 1395 3555 1418 +f 3368 1389 3179 1418 +f 3401 1558 3163 1419 +f 3402 1564 3385 1420 +f 3386 1421 3403 1420 +f 1551 3404 1044 3405 +f 3406 1430 3146 1592 +f 3146 1109 3364 1592 +f 3407 1433 3547 1596 +f 3388 1602 3551 1422 +f 3152 1038 3408 1601 +f 3199 1064 3388 1422 +f 3411 1616 3562 1618 +f 3411 1436 3113 1379 +f 3412 1635 3577 1392 +f 3412 1424 3187 1097 +f 3413 1635 3412 1097 +f 3414 1048 3140 1627 +f 3140 1049 3415 1627 +f 3191 1034 3380 1623 +f 3374 1088 3136 1428 +f 3136 1084 3382 1428 +f 3382 1427 3560 1428 +f 3398 1415 3425 1429 +f 3406 1591 3546 1594 +f 3364 1109 3427 1590 +f 3148 1110 3417 1432 +f 3407 1110 3429 1595 +f 3389 1069 3128 1597 +f 3128 1431 3418 1597 +f 3126 1423 3409 1434 +f 3409 1435 3550 1434 +f 3194 1436 3411 1618 +f 3138 1407 3420 1437 +f 3185 1048 3414 1630 +f 3414 1629 3575 1630 +f 3201 1439 3422 1440 +f 3171 1082 3423 1441 +f 3424 1613 3423 1082 +f 3116 1447 3430 1442 +f 1443 3558 1429 3425 +f 1415 3177 1087 3425 +f 3426 1443 3425 1087 +f 3175 1088 3374 1610 +f 3196 1444 3428 1587 +f 3144 1387 3366 1588 +f 3207 1470 3431 1566 +f 3431 1559 3521 1566 +f 3432 1580 3535 1448 +f 3433 1584 3218 1128 +f 3230 1501 3475 1575 +f 3434 1617 3562 1615 +f 3435 1449 3580 1639 +f 3209 1450 3435 1639 +f 3436 1562 3525 1452 +f 3437 1472 3538 1453 +f 3438 1560 3523 1454 +f 3439 1645 3587 1644 +f 3440 1455 3561 1460 +f 3441 1581 3541 1582 +f 3442 1583 3534 1456 +f 3443 1135 3252 1458 +f 3438 1529 3207 1566 +f 3444 1459 3579 1638 +f 3434 1471 3302 1619 +f 3279 1196 3445 1488 +f 3446 1633 3574 1628 +f 3447 1498 3226 1461 +f 3447 1576 3537 1462 +f 3448 1463 3442 1457 +f 3441 1479 3228 1456 +f 3228 1457 3442 1456 +f 3449 1464 3458 1183 +f 3250 1466 3479 1465 +f 1467 3285 1202 3450 +f 3220 1485 3444 1638 +f 3451 1468 3220 1638 +f 3431 1470 3266 1615 +f 3266 1471 3434 1615 +f 3452 1492 3300 1624 +f 1634 3486 1150 3453 +f 3437 1473 3254 1577 +f 3454 1577 3254 1180 +f 3455 1474 3540 1461 +f 3226 1476 3455 1461 +f 3435 1450 3260 1453 +f 3260 1473 3437 1453 +f 3455 1476 3256 1475 +f 3456 1579 3536 1475 +f 3256 1499 3456 1475 +f 3432 1478 3474 1477 +f 3457 1126 3263 1582 +f 3263 1479 3441 1582 +f 3265 1183 3458 1586 +f 3459 1530 3293 1168 +f 3246 1164 3476 1513 +f 3460 1215 3248 1574 +f 3252 1481 3461 1458 +f 3461 1571 3529 1458 +f 3462 1643 3585 1642 +f 3462 1154 3281 1521 +f 3214 1206 3463 1482 +f 3451 1469 3583 1646 +f 3288 1483 3485 1511 +f 3464 1484 3439 1206 +f 3444 1485 3465 1486 +f 3238 1159 3490 1487 +f 3302 1195 3466 1619 +f 1636 3493 1217 3467 +f 3468 1490 3453 1150 +f 3271 1523 3492 1524 +f 3469 1512 3571 1491 +f 3300 1493 3470 1624 +f 3470 1494 3570 1624 +f 3471 1494 3470 1221 +f 3277 1492 3452 1495 +f 3452 1496 3568 1495 +f 3440 1196 3277 1495 +f 3472 1497 3259 1498 +f 3473 1579 3456 1478 +f 3457 1500 3474 1478 +f 3475 1585 3533 1575 +f 3475 1501 3265 1586 +f 3476 1164 3242 1452 +f 3242 1515 3436 1452 +f 3477 1555 3528 1570 +f 3460 1480 3532 1505 +f 3248 1504 3478 1574 +f 3443 1572 3530 1520 +f 3295 1215 3460 1505 +f 3480 1506 3581 1640 +f 3480 1451 3209 1639 +f 3481 1508 3584 1641 +f 3481 1202 3283 1507 +f 3482 1508 3481 1507 +f 3483 1199 3236 1510 +f 3236 1157 3484 1510 +f 3287 1468 3451 1646 +f 3446 1149 3232 1631 +f 3232 1150 3486 1631 +f 3469 1221 3496 1625 +f 3476 1502 3526 1513 +f 3436 1515 3498 1514 +f 3244 1503 3487 1518 +f 3477 1503 3500 1569 +f 3461 1481 3224 1516 +f 3224 1517 3488 1516 +f 3222 1135 3489 1519 +f 3290 1451 3480 1640 +f 3234 1154 3491 1509 +f 3281 1199 3483 1521 +f 3483 1522 3586 1521 +f 3297 1217 3493 1525 +f 3269 1526 3494 1489 +f 3495 1637 3494 1526 +f 3212 1122 3501 1527 +f 1614 3573 1625 3496 +f 1221 3275 1191 3496 +f 3497 1614 3496 1191 +f 3273 1149 3446 1628 +f 3292 1163 3499 1528 +f 3240 1529 3438 1454 +f 2473 475 1812 26 +f 403 2356 1531 3502 +f 2415 403 3502 391 +f 1814 31 2434 460 +f 2354 402 2357 1533 +f 2199 257 3503 146 +f 2007 231 2198 1534 +f 2200 256 3504 1535 +f 2337 388 2336 1540 +f 1536 2337 1540 3505 +f 3505 166 2047 1537 +f 2047 162 2203 1537 +f 2204 1538 3506 142 +f 1538 2207 252 3506 +f 3507 230 2166 1539 +f 3508 1540 3509 454 +f 3509 1542 2427 454 +f 2166 166 3508 1539 +f 3509 1540 3510 386 +f 1540 2335 1541 3510 +f 2353 401 2346 1542 +f 2206 1543 3511 234 +f 2191 252 2207 259 +f 3507 1544 2198 231 +f 2336 387 2335 1540 +f 3511 1543 2203 162 +f 3512 1546 3513 860 +f 3513 56 1853 860 +f 1898 1547 2126 213 +f 1978 122 2138 1548 +f 3514 1553 3361 1599 +f 3410 1422 3551 1599 +f 1403 3378 1549 3515 +f 3515 1599 3405 1550 +f 3404 1551 3405 1599 +f 3361 1552 3404 1599 +f 3514 1573 3516 1391 +f 3370 1386 3516 1573 +f 3377 1390 3370 1573 +f 3369 1554 3377 1573 +f 1421 3386 1385 3517 +f 3403 1421 3517 1573 +f 1556 3360 1401 3518 +f 3518 1573 3528 1555 +f 3520 1452 3525 1557 +f 3383 1558 3499 1454 +f 1559 3563 1399 3521 +f 3522 1566 3521 1381 +f 1560 3522 1380 3523 +f 3401 1567 3498 1528 +f 3524 1514 3498 1567 +f 1562 3524 1561 3525 +f 3526 1502 3520 1563 +f 3519 1555 3477 1569 +f 3523 1398 3383 1454 +f 3526 1568 3527 1513 +f 3459 1513 3527 1564 +f 3500 1530 3459 1564 +f 3402 1565 3519 1569 +f 3487 1570 3528 1573 +f 3488 1518 3487 1573 +f 1571 3461 1516 3529 +f 3529 1573 3530 1458 +f 1572 3443 1458 3530 +f 3530 1573 3489 1520 +f 3478 1519 3489 1573 +f 1480 3460 1574 3531 +f 2426 1578 3532 1480 +f 1578 3479 1505 3532 +f 3533 1578 3433 1575 +f 3474 1500 3541 1578 +f 3535 1578 3473 1448 +f 3537 1578 3472 1462 +f 3538 1578 3539 1453 +f 3539 1449 3435 1453 +f 3538 1472 3437 1577 +f 3454 1497 3472 1578 +f 3537 1576 3447 1461 +f 1578 3537 1461 3540 +f 1578 3540 1474 3536 +f 3535 1580 3432 1477 +f 1578 3541 1581 3534 +f 1578 3534 1583 3542 +f 3448 1584 3433 1578 +f 3533 1585 3475 1586 +f 3458 1464 3449 1578 +f 3449 1465 3479 1578 +f 1909 1593 3543 1604 +f 3543 1593 3544 1382 +f 3428 1588 3544 1593 +f 3427 1587 3428 1593 +f 1589 3364 1590 3545 +f 3545 1593 3546 1592 +f 1591 3406 1592 3546 +f 3546 1593 3387 1594 +f 3429 1404 3387 1593 +f 1433 3407 1595 3547 +f 3547 1593 3417 1596 +f 3418 1432 3417 1593 +f 1598 3389 1597 3548 +f 1599 3371 1405 3549 +f 3550 1435 3409 1600 +f 1599 3408 1434 3550 +f 3551 1602 3388 1601 +f 865 1909 1604 3552 +f 865 3552 1605 3553 +f 3395 1607 3373 865 +f 3373 1606 3554 865 +f 3554 1384 3368 1418 +f 865 3554 1418 3555 +f 3555 1395 3381 1414 +f 865 3555 1414 3556 +f 3400 1608 3557 865 +f 3557 1609 3398 1429 +f 865 3557 1429 3558 +f 3426 1610 3559 865 +f 3559 1426 3374 1428 +f 865 3559 1428 3560 +f 3560 1427 3382 1396 +f 3397 1611 3421 865 +f 3421 1440 3422 865 +f 3422 1612 3396 865 +f 3396 1441 3423 865 +f 3423 1613 3424 865 +f 3424 1442 3430 865 +f 3430 1446 3379 865 +f 3379 1392 3578 865 +f 3415 1408 3573 1614 +f 3561 1455 3567 1388 +f 3562 1617 3565 1618 +f 3563 1559 3431 1615 +f 1615 3562 1616 3564 +f 3419 1618 3565 1619 +f 3466 1488 3394 1410 +f 3445 1460 3566 1620 +f 1460 3561 1621 3566 +f 1495 3568 1622 3567 +f 1496 3569 1623 3568 +f 3416 1623 3569 1624 +f 1409 3416 1624 3570 +f 3393 1409 3570 1494 +f 3471 1491 3571 1438 +f 3571 1383 3367 1438 +f 3572 1512 3469 1625 +f 3392 1626 3572 1625 +f 1408 3392 1625 3573 +f 3497 1628 3574 1627 +f 3574 1629 3414 1627 +f 1633 3391 1630 3575 +f 1632 3391 1633 3576 +f 3486 1634 3390 1632 +f 3420 1406 3390 1634 +f 3453 1490 3468 1437 +f 3468 1524 3413 1437 +f 1524 3492 1392 3577 +f 3492 1525 3493 1392 +f 3493 1636 3578 1392 +f 3467 1489 3494 864 +f 3494 1637 3495 864 +f 3495 1527 3501 864 +f 3484 1482 3463 864 +f 864 3581 1506 3580 +f 864 3490 1640 3581 +f 3465 1487 3490 864 +f 864 3579 1459 3582 +f 864 3583 1469 3579 +f 864 3485 1646 3583 +f 3578 1636 3467 864 +f 3501 1467 3450 864 +f 3450 1641 3584 864 +f 3482 1509 3491 864 +f 3491 1642 3585 864 +f 3585 1643 3462 1521 +f 864 3585 1521 3586 +f 3586 1522 3483 1510 +f 3463 1644 3587 864 +f 3587 1645 3439 1484 +f 3464 1511 3485 864 +f 1780 3594 1647 3588 +f 1648 3591 1654 3588 +f 3589 1650 3593 1649 +f 3590 1780 3591 1651 +f 3592 1653 3589 1651 +f 1653 3592 1781 3593 +f 3594 1781 3592 1652 +f 3595 1648 3588 1647 +f 3595 1652 3592 1651 +f 1656 3601 1662 3596 +f 1660 3600 1655 3596 +f 1657 3598 1661 3597 +f 1656 3596 1655 3597 +f 1657 3600 1658 3598 +f 1659 3601 1661 3598 +f 1660 3596 1662 3599 +f 1659 3598 1658 3599 +f 1660 3599 1658 3600 +f 1657 3597 1655 3600 +f 1656 3597 1661 3601 +f 1659 3599 1662 3601 +f 1988 1713 2008 1663 +f 2009 144 1989 135 +f 2780 1664 3602 1666 +f 2814 1665 2812 148 +f 2812 856 2841 148 +f 2841 858 2019 148 +f 1667 1787 1668 3603 +f 1669 2720 739 3603 +f 1669 3603 1668 3604 +f 2830 1669 3604 1670 +f 1670 1904 1671 3605 +f 2002 1672 2809 1726 +f 2806 827 2809 1672 +f 2835 1673 2806 1672 +f 2821 1675 3606 1676 +f 1675 2794 1710 3606 +f 1797 1723 3627 1722 +f 1980 128 3607 1684 +f 1677 2790 1684 3607 +f 2791 1677 3607 128 +f 2816 833 2791 128 +f 806 2787 1678 3608 +f 3608 128 3609 853 +f 3609 1694 2803 854 +f 2833 824 2803 1694 +f 2015 1682 2834 1679 +f 2838 1680 2726 1682 +f 2726 1681 2748 1682 +f 2748 819 3610 1682 +f 2017 1683 2837 1698 +f 1700 2797 1699 3611 +f 1995 1684 2796 1700 +f 2819 1686 2796 1684 +f 2793 1687 2819 1684 +f 2746 765 2793 1684 +f 2790 764 2746 1684 +f 2776 796 2767 1688 +f 2767 786 2018 1688 +f 2781 798 2780 1666 +f 2751 1770 3655 1690 +f 2755 776 2751 1690 +f 1785 1667 3603 739 +f 739 2721 775 3612 +f 1692 2014 1693 3614 +f 2014 1694 1981 1693 +f 2012 1695 3613 137 +f 2842 829 2810 1683 +f 2810 1696 2807 1683 +f 2807 1697 2837 1683 +f 1904 15 3615 1671 +f 2827 1671 3615 1699 +f 2799 816 2827 1699 +f 2822 840 2799 1699 +f 2797 839 2822 1699 +f 2011 147 1990 1701 +f 2010 145 2011 1701 +f 1994 1730 3616 130 +f 2789 1703 2815 130 +f 2815 1704 2786 130 +f 2786 825 3617 130 +f 2005 1705 2831 849 +f 2802 822 2831 1705 +f 1705 3617 1706 3618 +f 2773 1767 3652 233 +f 2766 1707 2773 233 +f 785 2766 233 3619 +f 2778 785 3619 143 +f 1708 3620 774 3621 +f 1783 1708 3621 737 +f 2003 1727 2836 1674 +f 2725 1709 2836 1727 +f 1798 1676 3606 1710 +f 14 1798 1710 3622 +f 2817 837 3629 14 +f 1984 130 3617 1705 +f 2006 1712 3623 1711 +f 1987 131 1985 1711 +f 1713 1987 1711 3623 +f 2779 1714 2778 143 +f 2000 1715 2813 797 +f 2811 831 2813 1715 +f 2839 832 2811 1715 +f 3 1783 737 3624 +f 3624 738 3626 1716 +f 2829 1717 3625 1719 +f 1718 2825 844 3625 +f 2826 814 3627 1723 +f 1906 1719 3625 844 +f 1721 2820 1722 3627 +f 2001 1725 2840 855 +f 2808 1724 2840 1725 +f 2004 1728 2724 744 +f 2747 745 2724 1728 +f 2800 767 2747 1728 +f 2832 1729 2800 1728 +f 1733 1799 14 3629 +f 2818 1731 3630 1730 +f 811 2745 809 3630 +f 2788 1732 3616 1730 +f 1993 1733 3629 837 +f 2763 782 2847 13 +f 2761 1734 3639 22 +f 2736 755 2761 22 +f 1735 2765 754 3631 +f 3631 22 3633 1736 +f 747 2728 1736 3633 +f 3633 22 3634 1737 +f 3635 1771 3632 16 +f 3637 1734 2763 13 +f 12 1809 24 3637 +f 3638 1734 3637 24 +f 2723 743 2785 863 +f 2785 1740 2744 863 +f 2744 1741 2732 863 +f 2732 1742 2742 863 +f 2742 803 2783 863 +f 2783 1743 2730 863 +f 2730 1745 3640 863 +f 1745 2771 1744 3640 +f 2771 792 3641 1744 +f 2740 1746 3642 1744 +f 2775 1768 3653 1769 +f 1769 2035 1744 3642 +f 2848 862 2762 1748 +f 2760 1747 2735 1749 +f 2735 783 2764 1749 +f 3643 1750 1794 1749 +f 757 2737 23 3643 +f 2846 232 2770 1753 +f 862 2784 740 3644 +f 2743 1751 2784 862 +f 2731 762 2743 862 +f 2741 758 2731 862 +f 2782 1752 2741 862 +f 2768 1755 2770 232 +f 2739 787 2768 232 +f 2169 1756 2772 1757 +f 2764 757 3643 1749 +f 2737 1758 3646 23 +f 1758 2727 1759 3646 +f 2727 1760 3647 1759 +f 1760 2756 1761 3647 +f 2756 1762 3648 1761 +f 1762 2758 1763 3648 +f 2758 1766 3649 1763 +f 3649 1766 2733 1765 +f 1764 3649 1765 3650 +f 769 3651 9 3650 +f 769 3654 2 3651 +f 1767 2773 793 3652 +f 1756 1999 233 3652 +f 1688 2018 1769 3653 +f 1708 1783 2 3654 +f 3620 1708 3654 769 +f 1770 2751 1771 3655 +f 3656 1772 2430 1773 +f 2430 1774 1874 1773 +f 1777 2429 1776 3657 +f 1854 56 3513 1775 +f 3513 1546 3660 1775 +f 1775 3658 456 3657 +f 3656 59 1864 1776 +f 2429 1772 3656 1776 +f 3658 141 1998 1779 +f 2472 456 3658 1779 +f 1547 1901 1778 3659 +f 1779 2034 156 3659 +f 141 3658 1775 3660 +f 2469 492 2497 493 +f 3590 1649 3593 1781 +f 1782 1 3651 2 +f 1782 83 1921 90 +f 1784 1716 1905 83 +f 1785 739 3612 1690 +f 1786 4 1787 5 +f 1786 6 1793 73 +f 1788 23 3646 1759 +f 1789 20 3638 24 +f 1790 99 1806 1763 +f 1790 1764 1802 7 +f 1791 22 1807 8 +f 1791 17 1803 1739 +f 1792 89 1802 9 +f 1793 16 1803 70 +f 1795 12 3637 13 +f 1795 45 1837 47 +f 1796 1723 1797 94 +f 1797 1722 2821 1676 +f 1800 82 1884 76 +f 1800 1699 3615 15 +f 1804 1761 1806 19 +f 1805 21 1807 1738 +f 1808 97 1930 10 +f 1808 1750 3643 23 +f 1810 26 1812 612 +f 40 1825 37 1811 +f 1811 604 2526 612 +f 475 2424 40 1812 +f 1816 590 2603 30 +f 1817 606 2611 33 +f 1817 32 2496 31 +f 1818 33 2534 607 +f 1819 36 1820 32 +f 1824 43 1829 527 +f 1826 40 1827 39 +f 1826 587 2532 38 +f 1827 43 1828 614 +f 1828 585 2527 603 +f 1538 2204 32 1829 +f 1832 529 2598 602 +f 1834 44 1835 45 +f 863 2843 859 1834 +f 755 2736 753 1835 +f 1837 784 2765 46 +f 859 2844 54 1838 +f 50 2769 791 1838 +f 782 2763 781 1839 +f 1734 2761 44 1840 +f 743 2723 48 1841 +f 1740 2785 805 1842 +f 1741 2744 49 1843 +f 1742 2732 760 1844 +f 803 2742 802 1845 +f 1743 2783 801 1846 +f 1848 1746 2740 51 +f 1768 2775 52 1849 +f 1850 786 2767 795 +f 798 2781 799 1851 +f 1852 54 1853 55 +f 1664 2780 53 1852 +f 1854 1775 3657 1776 +f 1665 2814 55 1855 +f 829 2842 57 1856 +f 1696 2810 828 1857 +f 1858 61 1860 59 +f 1858 255 1870 60 +f 854 2803 823 1860 +f 1691 1982 62 1861 +f 824 2833 850 1862 +f 856 2812 857 1865 +f 1697 2807 58 1866 +f 1680 2838 64 1867 +f 1681 2726 768 1868 +f 819 2748 65 1869 +f 1873 59 3656 1773 +f 1874 1774 2469 493 +f 1875 68 1876 25 +f 1876 777 1877 69 +f 1877 780 1878 21 +f 1878 751 1879 8 +f 1879 750 1880 17 +f 1880 72 1881 70 +f 1882 71 2721 847 +f 1883 847 2720 843 +f 82 1903 4 1883 +f 1886 816 2799 817 +f 1887 840 2822 838 +f 1888 839 2797 813 +f 1889 1686 2819 77 +f 1890 1687 2793 78 +f 1891 765 2746 763 +f 1892 764 2790 810 +f 1893 833 2816 807 +f 939 2967 938 1895 +f 1896 1547 1898 80 +f 932 2928 933 1896 +f 1897 81 1900 945 +f 1899 213 2111 81 +f 865 3578 864 1900 +f 938 1902 1578 1901 +f 1903 1670 3604 1668 +f 1905 1719 1906 93 +f 1906 844 3628 1720 +f 1748 2762 98 1911 +f 1747 2760 85 1912 +f 783 2735 752 1913 +f 757 2764 86 1914 +f 1758 2737 756 1916 +f 1917 99 1918 88 +f 1917 1762 2756 87 +f 1922 93 1923 92 +f 1923 94 1924 841 +f 1924 138 1925 95 +f 1925 129 1926 815 +f 1926 134 1927 812 +f 1927 133 1928 836 +f 1928 139 1929 834 +f 742 1933 101 1932 +f 740 2784 804 1933 +f 1751 2743 761 1934 +f 762 2731 748 1935 +f 758 2741 759 1936 +f 1752 2782 800 1937 +f 1940 1753 2770 790 +f 1941 1755 2768 102 +f 1942 787 2739 103 +f 1943 1757 2772 794 +f 1944 1707 2766 105 +f 1714 2779 104 1945 +f 1946 797 2813 830 +f 1947 831 2811 106 +f 1948 832 2839 108 +f 1949 1724 2808 107 +f 1950 115 1968 107 +f 1950 827 2806 826 +f 1951 109 2745 96 +f 1703 2789 808 1953 +f 1704 2815 110 1954 +f 825 2786 111 1955 +f 126 1958 848 1957 +f 1957 822 2802 821 +f 1959 451 2420 629 +f 1960 1729 2832 848 +f 629 1962 766 1961 +f 1961 767 2800 818 +f 1962 627 2421 113 +f 1963 745 2747 766 +f 113 1965 114 1964 +f 1964 1709 2725 746 +f 1965 452 2422 630 +f 630 1967 826 1966 +f 1966 1673 2835 114 +f 1967 438 2392 115 +f 1969 437 2618 116 +f 1970 441 2619 633 +f 1971 632 2620 118 +f 1972 442 2617 631 +f 1973 121 1975 631 +f 1973 634 2621 119 +f 1974 450 2417 120 +f 1976 123 2849 122 +f 903 2906 84 1977 +f 1548 2845 861 1978 +f 1684 1996 127 1980 +f 1694 3609 128 1981 +f 1982 136 3614 1693 +f 1983 130 1984 129 +f 1984 1705 2006 1711 +f 1988 1663 2009 135 +f 1989 144 2010 1701 +f 1990 136 1991 1701 +f 147 2012 137 1990 +f 1993 837 2818 1730 +f 75 1996 1684 1995 +f 1700 3611 1685 1995 +f 1998 149 2016 142 +f 797 2779 143 2000 +f 2001 1548 2168 1725 +f 855 2839 1715 2001 +f 1726 2808 1725 2002 +f 1674 2835 1672 2003 +f 744 2725 1727 2004 +f 849 2832 1728 2005 +f 2006 231 2007 1712 +f 2007 1534 2199 146 +f 2008 1713 3623 1712 +f 146 2013 1695 2012 +f 2015 1535 2016 1682 +f 2015 1679 2833 1694 +f 2017 1698 2838 1682 +f 2018 786 2781 1666 +f 149 2020 148 2019 +f 2019 858 2842 1683 +f 2020 860 2035 1666 +f 150 3357 151 2022 +f 1345 3351 1346 2023 +f 2024 1321 2025 156 +f 2025 1175 3225 1320 +f 2028 1140 3258 154 +f 2029 1179 3253 155 +f 2032 1323 3210 1326 +f 1324 3261 1325 2033 +f 2034 142 3506 252 +f 1296 3166 1226 2036 +f 1275 3119 158 2037 +f 2040 162 2047 159 +f 2041 1298 3161 1276 +f 2042 1277 3159 1248 +f 163 3320 1278 2044 +f 164 3311 1247 2045 +f 2048 1255 2081 162 +f 2049 1290 3112 1225 +f 2053 1230 3205 168 +f 2054 1228 3180 1237 +f 2057 1227 3178 1261 +f 2059 1115 3203 170 +f 2060 866 2061 171 +f 2060 1090 3176 1260 +f 1300 3326 172 2062 +f 1288 3303 173 2065 +f 1286 3172 1287 2066 +f 2067 1283 3200 1285 +f 2069 1025 3170 174 +f 2070 1027 3115 175 +f 2072 1280 3188 176 +f 2074 1251 3186 1252 +f 2075 1046 3137 177 +f 2076 866 2077 179 +f 1329 2094 202 2078 +f 2078 1094 3184 179 +f 188 3305 189 2080 +f 1103 3193 1282 2082 +f 2083 1282 3324 192 +f 1545 3511 162 2083 +f 1033 3123 184 2086 +f 1306 2103 1545 2087 +f 197 3322 186 2088 +f 2092 199 2104 198 +f 2092 1029 3323 190 +f 1374 2106 190 2093 +f 2093 1028 3139 191 +f 1119 3330 1303 2096 +f 2097 1366 3267 193 +f 2099 1223 3301 1335 +f 2100 1194 3278 195 +f 2103 1333 3276 196 +f 2104 1331 3345 1332 +f 1219 3298 1361 2106 +f 2107 1314 3272 1374 +f 2108 200 2109 203 +f 2110 866 2111 1363 +f 1330 3332 1304 2110 +f 1364 3270 1363 2112 +f 2113 1362 3296 204 +f 2115 1186 3268 206 +f 2116 1371 3211 207 +f 2119 1152 3233 219 +f 2120 210 2134 213 +f 2120 217 3338 1375 +f 2121 216 3337 211 +f 2122 1357 2133 213 +f 2125 213 2126 1326 +f 1547 3659 156 2126 +f 1327 3289 1358 2127 +f 1158 3237 1359 2128 +f 1351 3219 1353 2130 +f 1352 3286 1354 2131 +f 2132 1354 3354 1355 +f 2135 1349 3282 218 +f 2136 1372 3284 209 +f 220 2139 1548 2138 +f 2139 1231 3143 221 +f 1294 3195 1293 2140 +f 2143 1107 3145 1238 +f 2145 1058 3149 1265 +f 2147 230 2167 1263 +f 2150 1055 3307 223 +f 2151 166 2166 223 +f 2151 1269 3127 224 +f 2154 1232 3155 1268 +f 2156 1036 3125 1234 +f 2157 1035 3151 1273 +f 2159 1062 3198 227 +f 2160 1243 3309 1245 +f 228 3319 229 2163 +f 1270 3318 1302 2164 +f 2164 1295 2165 166 +f 1757 2739 232 2169 +f 2171 236 3335 1367 +f 2173 1337 2174 234 +f 2178 1316 3341 242 +f 2179 1339 3223 243 +f 2182 1138 3251 1310 +f 2183 252 2191 1310 +f 2184 1134 3221 1343 +f 2185 1133 3247 1342 +f 2187 1214 3294 1344 +f 2188 1212 3350 247 +f 2189 1318 3342 248 +f 2190 253 2194 252 +f 259 2206 234 2191 +f 2192 1340 2193 252 +f 1146 3342 1318 2193 +f 1143 3333 251 2194 +f 1369 3333 1347 2195 +f 2196 257 2199 254 +f 2200 255 2201 256 +f 2200 1535 3503 257 +f 2202 1537 2203 453 +f 142 3504 258 2204 +f 2205 1543 2206 43 +f 1578 2426 453 2205 +f 308 2264 260 2208 +f 267 2228 262 2210 +f 2212 400 2350 263 +f 2212 279 2240 338 +f 2215 264 2270 353 +f 2217 265 2351 275 +f 2217 324 2243 289 +f 2218 268 2347 398 +f 2218 282 2231 269 +f 2220 270 2352 271 +f 2220 340 2245 288 +f 2221 325 2282 326 +f 2222 369 2315 375 +f 2223 345 2316 330 +f 2224 330 2322 346 +f 2229 399 2348 266 +f 2229 283 2234 335 +f 2230 339 2292 274 +f 685 2625 276 2231 +f 2232 671 2235 339 +f 284 2675 685 2233 +f 285 2652 284 2234 +f 2235 650 2238 277 +f 646 2633 285 2236 +f 684 2673 646 2237 +f 2238 278 2241 273 +f 337 2291 338 2239 +f 286 2651 684 2239 +f 645 2631 286 2240 +f 2241 281 2256 280 +f 291 2671 681 2246 +f 2247 293 2248 727 +f 2248 294 2249 724 +f 2249 341 2250 725 +f 2250 296 2251 295 +f 2251 357 2252 718 +f 2252 325 2253 700 +f 2253 356 2254 297 +f 2254 299 2255 298 +f 2255 319 2277 300 +f 2256 677 2257 331 +f 2257 302 2258 344 +f 2258 643 2259 301 +f 2259 304 2260 303 +f 2260 305 2261 332 +f 2261 667 2262 306 +f 2262 307 2263 261 +f 334 2289 350 2266 +f 312 2300 311 2268 +f 313 2701 711 2271 +f 354 2297 353 2271 +f 711 2687 314 2272 +f 314 2683 315 2273 +f 315 2693 666 2274 +f 323 2285 360 2274 +f 666 2654 316 2275 +f 316 2627 713 2276 +f 2277 321 2278 732 +f 2278 320 2279 735 +f 2279 322 2280 716 +f 394 2344 395 2281 +f 2285 329 2327 328 +f 361 2308 362 2286 +f 2288 373 2299 301 +f 2297 382 2331 342 +f 2298 384 2307 343 +f 390 2339 358 2305 +f 2310 411 2319 363 +f 2310 272 2323 417 +f 2311 374 2383 364 +f 2312 406 2313 365 +f 2312 347 2318 433 +f 2313 432 2320 366 +f 2315 429 2380 368 +f 2317 371 2412 370 +f 2318 372 2319 412 +f 2321 430 2358 404 +f 2322 376 2381 367 +f 2323 333 2324 418 +f 2324 378 2326 377 +f 2325 423 2329 349 +f 2326 379 2333 420 +f 2329 421 2333 348 +f 2334 386 3510 1541 +f 2336 327 2338 387 +f 444 2415 391 2343 +f 2353 1542 3509 386 +f 1533 2427 455 2354 +f 404 2379 428 2357 +f 2365 406 2366 409 +f 2370 418 2371 415 +f 2371 377 2372 419 +f 2372 420 2373 706 +f 2373 421 2374 649 +f 2374 423 2375 422 +f 2375 383 2376 424 +f 2376 426 2377 427 +f 2377 385 2409 425 +f 439 2428 1533 2378 +f 2378 661 2651 435 +f 2379 663 2387 1533 +f 2383 432 2384 430 +f 2391 436 2631 440 +f 2391 437 2392 439 +f 2394 287 2669 659 +f 2397 696 2671 728 +f 2398 727 2710 443 +f 2400 725 2691 702 +f 2401 295 2706 719 +f 2402 444 2414 719 +f 731 2712 447 2406 +f 2409 380 2410 446 +f 2410 381 2411 448 +f 2412 640 2627 449 +f 1573 3514 1599 2418 +f 2418 1593 2622 635 +f 2419 112 2616 254 +f 1578 2425 467 2423 +f 2423 492 2469 1778 +f 1480 3531 1573 2426 +f 2428 1539 3508 454 +f 2430 456 2472 1774 +f 2431 457 2444 1532 +f 2431 32 2497 492 +f 2433 652 2642 459 +f 463 2495 460 2434 +f 2435 461 2653 686 +f 2438 676 2439 501 +f 2439 653 2440 512 +f 2440 679 2474 511 +f 2445 564 2648 457 +f 2446 656 2646 657 +f 2447 565 2670 466 +f 2448 660 2650 680 +f 2449 682 2672 683 +f 2451 730 2711 729 +f 2452 571 2709 726 +f 467 2454 468 2453 +f 2453 572 2692 703 +f 2455 717 2457 505 +f 2455 504 2470 477 +f 2458 609 2517 470 +f 475 2473 505 2459 +f 2460 519 2515 514 +f 520 2519 521 2464 +f 522 2520 473 2466 +f 524 2521 523 2468 +f 517 2508 476 2470 +f 2472 1779 3659 1778 +f 26 2494 478 2473 +f 2474 644 2475 479 +f 2475 669 2476 507 +f 2476 670 2477 508 +f 2477 480 2478 497 +f 2478 481 2479 498 +f 2479 482 2480 483 +f 491 2500 515 2484 +f 2485 554 2698 708 +f 2486 709 2636 487 +f 2487 555 2680 689 +f 715 2586 477 2493 +f 2498 495 2510 494 +f 2498 513 2514 525 +f 2499 621 2610 605 +f 2501 617 2502 497 +f 2501 498 2509 620 +f 2502 622 2511 508 +f 2503 499 2608 500 +f 2506 597 2514 503 +f 2509 483 2510 506 +f 599 2524 528 2516 +f 532 2535 496 2517 +f 557 2528 519 2519 +f 556 2596 522 2521 +f 2525 593 2592 591 +f 714 2586 583 2526 +f 2527 584 2577 577 +f 698 2559 600 2528 +f 2529 570 2573 530 +f 2530 573 2576 584 +f 2531 613 2579 580 +f 2535 598 2595 516 +f 615 2615 624 2537 +f 542 2642 533 2537 +f 2538 673 2541 534 +f 543 2640 542 2539 +f 535 2608 536 2540 +f 675 2666 543 2540 +f 2541 688 2544 619 +f 537 2644 675 2542 +f 538 2668 537 2543 +f 2544 665 2547 540 +f 618 2610 539 2545 +f 546 2630 538 2545 +f 539 2602 545 2546 +f 2547 559 2562 558 +f 623 2589 548 2549 +f 626 2613 625 2551 +f 2553 552 2554 551 +f 2554 599 2555 553 +f 2555 598 2556 554 +f 2556 532 2557 709 +f 2557 610 2558 555 +f 689 2680 690 2559 +f 2560 556 2561 699 +f 2561 575 2583 694 +f 2562 648 2563 560 +f 2563 662 2564 592 +f 2564 563 2565 591 +f 2565 562 2566 561 +f 2566 564 2567 594 +f 2567 656 2568 596 +f 2568 565 2569 595 +f 568 2597 569 2571 +f 723 2705 574 2576 +f 574 2690 693 2577 +f 693 2682 579 2578 +f 613 2587 577 2578 +f 579 2715 733 2579 +f 581 2717 582 2581 +f 604 2600 588 2582 +f 2583 578 2584 704 +f 2584 611 2585 576 +f 589 2612 502 2588 +f 2590 616 2609 534 +f 2606 27 2607 611 +f 2623 416 2677 636 +f 2623 637 2658 414 +f 2624 639 2678 485 +f 2624 484 2659 638 +f 2635 422 2679 318 +f 2635 317 2697 649 +f 2638 547 2657 481 +f 2638 480 2662 544 +f 2645 440 2647 654 +f 2649 659 2669 290 +f 2656 668 2658 307 +f 2657 549 2659 482 +f 2660 541 2662 670 +f 2677 415 2699 309 +f 2678 551 2700 486 +f 2686 729 2711 567 +f 2695 706 2697 705 +f 2695 310 2699 419 +f 2696 707 2700 553 +f 2708 443 2710 724 +f 2713 469 2717 581 +f 2718 846 2719 737 +f 2718 772 2754 773 +f 2723 863 2847 782 +f 2734 771 2753 750 +f 2734 751 2759 779 +f 2736 22 3631 754 +f 2738 1735 3631 1736 +f 1744 3641 788 2740 +f 2750 91 2754 774 +f 2750 770 3620 769 +f 2755 1690 3612 775 +f 2757 1737 2759 780 +f 1749 2848 1748 2760 +f 862 3644 741 2762 +f 788 3641 792 2769 +f 1756 3652 793 2772 +f 2775 1769 3642 1746 +f 2776 1768 2777 796 +f 1688 3653 1689 2776 +f 2782 862 3645 1754 +f 2787 853 2805 60 +f 2788 1730 3630 809 +f 130 3616 1732 2789 +f 2792 811 3630 1731 +f 2798 1721 3627 814 +f 1705 3618 852 2802 +f 852 3618 1706 2804 +f 2814 148 3602 1664 +f 2816 128 3608 1678 +f 2817 14 3622 835 +f 2823 1718 3625 1717 +f 2824 845 2828 74 +f 2826 1723 3628 844 +f 2827 79 2828 1671 +f 2829 1719 3626 738 +f 1670 3605 842 2830 +f 2834 1682 3610 820 +f 2845 862 2848 101 +f 2846 1753 3645 862 +f 2850 1117 2877 124 +f 1231 3112 1021 2852 +f 1294 3143 868 2853 +f 1107 3195 1108 2854 +f 1058 3145 1059 2855 +f 1057 3149 869 2856 +f 2858 1232 3127 870 +f 2859 1036 3155 871 +f 2860 1035 3125 1068 +f 2861 1062 3151 872 +f 2862 117 2863 873 +f 2862 1243 3198 1063 +f 906 2910 874 2863 +f 2864 1244 3153 873 +f 874 2890 890 2865 +f 1070 3157 1075 2865 +f 1073 3162 875 2866 +f 1279 3129 1039 2867 +f 1250 3159 876 2868 +f 1277 3161 877 2869 +f 2871 1298 3119 1031 +f 2872 1275 3166 879 +f 2873 1296 3131 881 +f 2874 1042 3121 880 +f 2875 1271 3133 882 +f 2876 1079 3168 1065 +f 1230 3180 883 2877 +f 1228 3178 884 2878 +f 1227 3203 885 2879 +f 1115 3176 1116 2880 +f 2882 1090 3202 1114 +f 2883 1085 3174 1045 +f 2884 1259 3135 887 +f 2885 1288 3172 1112 +f 2886 1286 3200 888 +f 2887 1283 3170 889 +f 2888 891 2893 889 +f 2888 1025 3115 1026 +f 2889 1027 3188 894 +f 904 2907 891 2890 +f 1024 3165 890 2891 +f 1103 3114 1023 2892 +f 2894 1280 3186 893 +f 2895 1251 3137 895 +f 2896 1046 3184 1093 +f 2897 1094 3139 896 +f 1101 3190 898 2899 +f 1281 3123 899 2900 +f 1033 3141 1052 2901 +f 1051 3193 892 2902 +f 953 2909 900 2903 +f 2905 901 2911 121 +f 947 2988 902 2906 +f 2907 952 2981 946 +f 959 2975 906 2911 +f 907 2939 927 2912 +f 942 2968 940 2912 +f 1119 3267 907 2913 +f 1118 3208 1161 2914 +f 1308 3239 1160 2915 +f 1209 3291 1210 2916 +f 1336 3241 908 2917 +f 1338 3245 909 2918 +f 2920 1138 3223 911 +f 2921 1134 3251 1171 +f 2922 1133 3221 912 +f 2923 1214 3247 1132 +f 2924 1212 3294 1213 +f 2925 940 2926 914 +f 2925 1341 3249 913 +f 2927 1146 3264 914 +f 1324 3253 916 2928 +f 1179 3258 917 2929 +f 1140 3225 1139 2930 +f 1175 3255 918 2931 +f 1174 3257 1177 2932 +f 2934 1370 3215 920 +f 2935 1181 3262 1142 +f 2936 1143 3227 921 +f 2937 1127 3217 922 +f 2938 1145 3229 915 +f 1366 3301 923 2939 +f 1223 3278 924 2940 +f 1194 3276 1192 2941 +f 1333 3299 925 2942 +f 1222 3274 926 2943 +f 2945 1331 3298 1218 +f 2946 1219 3272 1190 +f 2947 1314 3231 1148 +f 2948 1330 3270 1187 +f 2949 1364 3296 929 +f 2950 1362 3268 930 +f 2951 1186 3211 931 +f 2952 80 2959 931 +f 2952 1371 3284 1201 +f 2953 1372 3282 1200 +f 1323 3261 932 2954 +f 1327 3210 1120 2955 +f 1158 3289 1207 2956 +f 1351 3237 934 2957 +f 1352 3219 1130 2958 +f 995 2972 927 2959 +f 2960 1349 3233 1197 +f 2961 1152 3280 1198 +f 2962 1313 3235 1156 +f 1205 3286 1204 2964 +f 2966 993 2973 945 +f 2968 1000 3053 941 +f 943 2971 944 2970 +f 987 3057 936 2971 +f 992 3042 995 2973 +f 2975 956 2984 958 +f 2976 951 2979 950 +f 2977 958 2984 957 +f 949 3014 971 2978 +f 2980 955 2982 954 +f 2986 974 3041 960 +f 955 2989 961 2986 +f 971 3003 963 2989 +f 1393 3111 961 2990 +f 1394 3182 965 2991 +f 1412 3206 964 2992 +f 1389 3181 1092 2993 +f 1417 3179 966 2994 +f 1091 3204 968 2995 +f 2997 1088 3175 1086 +f 2998 1084 3136 1089 +f 2999 1113 3173 969 +f 3000 1439 3201 970 +f 3001 1082 3171 1081 +f 3002 1447 3116 1083 +f 1099 3187 1098 3003 +f 3005 1407 3138 1047 +f 3006 1048 3185 1095 +f 3007 1049 3140 1096 +f 3008 1030 3118 1050 +f 1425 3192 1102 3010 +f 1034 3191 1100 3011 +f 1411 3124 973 3012 +f 1105 3142 1106 3013 +f 1436 3194 1104 3014 +f 1387 3144 975 3015 +f 1444 3196 1053 3016 +f 1109 3146 976 3017 +f 1430 3150 1061 3018 +f 1445 3197 1060 3019 +f 3021 1110 3148 1056 +f 3022 1431 3128 978 +f 3023 1069 3156 979 +f 3024 1423 3126 1037 +f 3025 1038 3152 980 +f 3026 1064 3199 1067 +f 3027 1111 3154 1066 +f 3028 1066 3029 981 +f 3028 950 3041 974 +f 3029 1402 3169 1080 +f 3030 1044 3134 1043 +f 3031 1377 3122 982 +f 3032 1032 3132 983 +f 3033 1078 3167 1076 +f 1400 3160 984 3035 +f 1072 3130 1040 3036 +f 1419 3163 1041 3037 +f 1397 3158 1074 3038 +f 1071 3164 986 3039 +f 1378 3113 949 3040 +f 3043 998 3052 990 +f 3044 991 3047 988 +f 3045 990 3052 989 +f 1012 3083 1006 3046 +f 3048 1020 3050 994 +f 3055 1015 3110 997 +f 1020 3058 1184 3055 +f 1006 3072 1004 3058 +f 1470 3207 1184 3059 +f 1471 3266 1185 3060 +f 1195 3302 1224 3061 +f 1196 3279 1193 3062 +f 1492 3277 1001 3063 +f 1493 3300 1002 3064 +f 3066 1149 3273 1003 +f 3067 1150 3232 1151 +f 3068 1523 3271 1188 +f 3069 1217 3297 1189 +f 3070 1526 3269 1005 +f 3071 1122 3212 1121 +f 1203 3283 1123 3072 +f 3074 1154 3234 1153 +f 3075 1199 3281 1155 +f 3076 1157 3236 1007 +f 3077 1206 3214 1125 +f 1483 3288 1010 3079 +f 1468 3287 1009 3080 +f 1485 3220 1131 3081 +f 1159 3238 1011 3082 +f 1451 3290 1208 3083 +f 1529 3240 1162 3084 +f 1163 3292 1013 3085 +f 1515 3242 1165 3086 +f 1164 3246 1169 3087 +f 1168 3293 1211 3088 +f 3090 1503 3244 1166 +f 3091 1517 3224 1172 +f 3092 1481 3252 1137 +f 3093 1135 3222 1136 +f 3094 1504 3248 1216 +f 3095 1215 3295 1014 +f 3096 1466 3250 1170 +f 3097 1170 3098 1018 +f 3097 988 3110 1015 +f 3098 1183 3265 1147 +f 3099 1501 3230 1016 +f 3100 1128 3218 1129 +f 3101 1457 3228 1144 +f 3102 1479 3263 1182 +f 1499 3256 1176 3104 +f 1476 3226 1141 3105 +f 1498 3259 1017 3106 +f 1180 3254 1173 3107 +f 1473 3260 1019 3108 +f 1450 3209 1012 3109 +f 3116 1442 3424 1082 +f 1408 3415 1049 3118 +f 1077 3386 1420 3120 +f 1434 3408 1038 3126 +f 3132 1390 3377 1078 +f 3134 1552 3361 1377 +f 1257 3303 1288 3135 +f 1437 3413 1097 3138 +f 3142 1410 3394 1411 +f 3144 1588 3428 1444 +f 1432 3418 1431 3148 +f 3150 1594 3387 1445 +f 3153 1242 3309 1243 +f 1405 3371 1423 3156 +f 3157 165 3321 1073 +f 1558 3383 1397 3163 +f 1550 3405 1044 3169 +f 1441 3396 1439 3171 +f 3173 1611 3397 1084 +f 3175 1610 3426 1087 +f 1607 3395 1412 3181 +f 3183 1290 3325 1292 +f 1630 3391 1407 3185 +f 3189 1446 3430 1447 +f 3191 1623 3416 1425 +f 3192 1409 3393 1030 +f 3194 1618 3419 1105 +f 3196 1587 3427 1109 +f 3197 1404 3429 1110 +f 3199 1422 3410 1111 +f 3201 1440 3421 1113 +f 3208 235 3330 1119 +f 3212 1527 3495 1526 +f 1124 3337 1356 3213 +f 1482 3484 1157 3214 +f 1126 3457 1478 3216 +f 3218 1584 3448 1457 +f 1519 3478 1504 3222 +f 3230 1575 3433 1128 +f 1329 3332 1330 3231 +f 1509 3482 1507 3234 +f 3238 1487 3465 1485 +f 3240 1454 3499 1163 +f 1518 3488 1517 3244 +f 3246 1513 3459 1168 +f 3249 1312 3350 1212 +f 3250 1465 3449 1183 +f 3255 152 3343 1174 +f 3257 1319 3353 1370 +f 1497 3454 1180 3259 +f 1489 3467 1217 3269 +f 3271 1524 3468 1150 +f 3273 1628 3497 1191 +f 1488 3466 1195 3279 +f 3280 210 3358 1313 +f 3285 1467 3501 1122 +f 3287 1646 3485 1483 +f 3288 1511 3464 1206 +f 3290 1640 3490 1159 +f 3292 1528 3498 1515 +f 3293 1530 3500 1503 +f 3295 1505 3479 1466 +f 3297 1525 3492 1523 +f 3299 185 3355 1222 +f 3305 187 3313 1029 +f 1603 3552 1604 3359 +f 1565 3402 1420 3360 +f 3362 1413 3553 1605 +f 1381 3521 1399 3363 +f 3365 1398 3523 1380 +f 3366 1376 3543 1382 +f 1626 3392 1030 3367 +f 1606 3373 1389 3368 +f 3369 1573 3517 1385 +f 3370 1032 3376 1386 +f 1599 3550 1600 3371 +f 3372 1620 3566 1621 +f 3375 1557 3525 1561 +f 1567 3401 1419 3375 +f 1553 3514 1391 3376 +f 3379 1424 3412 1392 +f 1388 3567 1622 3380 +f 3384 1568 3526 1563 +f 1494 3471 1438 3393 +f 1488 3445 1620 3394 +f 865 3553 1413 3395 +f 865 3560 1396 3397 +f 1608 3400 1415 3398 +f 865 3556 1416 3400 +f 3401 1528 3499 1558 +f 1569 3500 1564 3402 +f 3403 1573 3518 1401 +f 1596 3417 1110 3407 +f 1599 3551 1601 3408 +f 3410 1599 3515 1549 +f 3411 1379 3564 1616 +f 1524 3577 1635 3413 +f 3415 1614 3497 1627 +f 3418 1593 3548 1597 +f 1619 3466 1410 3419 +f 1634 3453 1437 3420 +f 865 3558 1443 3426 +f 3427 1593 3545 1590 +f 3429 1593 3547 1595 +f 1448 3473 1478 3432 +f 3434 1619 3565 1617 +f 3436 1514 3524 1562 +f 3438 1566 3522 1560 +f 1644 3463 1206 3439 +f 1460 3445 1196 3440 +f 3440 1495 3567 1455 +f 1456 3534 1581 3441 +f 3442 1463 3542 1583 +f 1520 3489 1135 3443 +f 3444 1486 3582 1459 +f 1631 3576 1633 3446 +f 1462 3472 1498 3447 +f 1578 3542 1463 3448 +f 3450 1202 3481 1641 +f 1638 3579 1469 3451 +f 3452 1624 3569 1496 +f 1578 3538 1577 3454 +f 3455 1475 3536 1474 +f 1582 3541 1500 3457 +f 1578 3533 1586 3458 +f 1642 3491 1154 3462 +f 864 3587 1484 3464 +f 864 3582 1486 3465 +f 1491 3471 1221 3469 +f 1578 3536 1579 3473 +f 3474 1578 3535 1477 +f 1452 3520 1502 3476 +f 1570 3487 1503 3477 +f 3478 1573 3531 1574 +f 3480 1639 3580 1506 +f 864 3584 1508 3482 +f 3484 864 3586 1510 +f 1632 3576 1631 3486 +f 3488 1573 3529 1516 +f 3505 1540 3508 166 +f 3512 141 3660 1546 +f 3518 1555 3519 1556 +f 3539 864 3580 1449 +f 3548 1599 3549 1598 +f 3563 1615 3564 1379 +f 3571 1512 3572 1383 +f 3574 1633 3575 1629 +f 3588 1654 3591 1780 +f 3589 1649 3590 1651 +f 1653 3593 1650 3589 +f 3590 1781 3594 1780 +f 3591 1648 3595 1651 +f 1652 3595 1647 3594 +f 3613 1692 3614 136 +f 3632 771 3636 1739 +f 3634 1739 3636 779 +f 3635 5 3655 1771 +f 3638 1738 3639 1734 +# 3672 faces, 0 coords texture + +# End of File diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 0e211c33ce..c0acb4f25d 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -19,10 +19,19 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -TEST_CASE("Load object", "[Hollowing]") { - Slic3r::TriangleMesh mesh = load_model("20mm_cube.obj"); +static bool _check_normals(const Slic3r::sla::Contour3D &mesh) +{ + for (auto & face : mesh.faces3) + { + + } - Slic3r::sla::Contour3D imesh = Slic3r::sla::convert_mesh(mesh); + return false; +} + +TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +{ + Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{load_model("20mm_cube.obj")}; auto ptr = Slic3r::meshToVolume(imesh, {}); REQUIRE(ptr); @@ -31,6 +40,8 @@ TEST_CASE("Load object", "[Hollowing]") { REQUIRE(!omesh.empty()); + + std::fstream outfile{"out.obj", std::ios::out}; omesh.to_obj(outfile); diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index e8921ba486..ecc68db0a4 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,5 +1,5 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp) +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp sla_print_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 229eb42676..e4d5e05d2f 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -1,9 +1,9 @@ -#include - #include #include #include +#include + // Debug #include @@ -50,21 +50,21 @@ void check_validity(const TriangleMesh &input_mesh, ASSUME_NO_REPAIR) { TriangleMesh mesh{input_mesh}; - + if (flags & ASSUME_NO_EMPTY) { REQUIRE_FALSE(mesh.empty()); } else if (mesh.empty()) return; // If it can be empty and it is, there is nothing left to do. - + REQUIRE(stl_validate(&mesh.stl)); - + bool do_update_shared_vertices = false; mesh.repair(do_update_shared_vertices); - + if (flags & ASSUME_NO_REPAIR) { REQUIRE_FALSE(mesh.needed_repair()); } - + if (flags & ASSUME_MANIFOLD) { mesh.require_shared_vertices(); if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj"); @@ -82,36 +82,36 @@ struct PadByproducts void _test_concave_hull(const Polygons &hull, const ExPolygons &polys) { REQUIRE(polys.size() >=hull.size()); - + double polys_area = 0; for (const ExPolygon &p : polys) polys_area += p.area(); - + double cchull_area = 0; for (const Slic3r::Polygon &p : hull) cchull_area += p.area(); - + REQUIRE(cchull_area >= Approx(polys_area)); - + size_t cchull_holes = 0; for (const Slic3r::Polygon &p : hull) cchull_holes += p.is_clockwise() ? 1 : 0; - + REQUIRE(cchull_holes == 0); - + Polygons intr = diff(to_polygons(polys), hull); REQUIRE(intr.empty()); } void test_concave_hull(const ExPolygons &polys) { sla::PadConfig pcfg; - + Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}}; - + _test_concave_hull(cchull.polygons(), polys); - + coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance()); ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta); Polygons waffl = sla::offset_waffle_style(cchull, delta); - + _test_concave_hull(to_polygons(wafflex), polys); _test_concave_hull(waffl, polys); } @@ -121,23 +121,23 @@ void test_pad(const std::string & obj_filename, PadByproducts & out) { REQUIRE(padcfg.validate().empty()); - + TriangleMesh mesh = load_model(obj_filename); - + REQUIRE_FALSE(mesh.empty()); - + // Create pad skeleton only from the model Slic3r::sla::pad_blueprint(mesh, out.model_contours); - + test_concave_hull(out.model_contours); - + REQUIRE_FALSE(out.model_contours.empty()); - + // Create the pad geometry for the model contours only Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); - + check_validity(out.mesh); - + auto bb = out.mesh.bounding_box(); REQUIRE(bb.max.z() - bb.min.z() == Approx(padcfg.full_height())); } @@ -166,42 +166,42 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, double gnd = stree.ground_level; double H1 = cfg.max_solo_pillar_height_mm; double H2 = cfg.max_dual_pillar_height_mm; - + for (const sla::Head &head : stree.heads()) { REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || head.bridge_id != sla::ID_UNSET)); } - + for (const sla::Pillar &pillar : stree.pillars()) { if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { double h = pillar.height; - + if (h > H1) REQUIRE(pillar.links >= 1); else if(h > H2) { REQUIRE(pillar.links >= 2); } } - + REQUIRE(pillar.links <= cfg.pillar_cascade_neighbors); REQUIRE(pillar.bridges <= cfg.max_bridges_on_pillar); } - + double max_bridgelen = 0.; auto chck_bridge = [&cfg](const sla::Bridge &bridge, double &max_brlen) { Vec3d n = bridge.endp - bridge.startp; double d = sla::distance(n); max_brlen = std::max(d, max_brlen); - + double z = n.z(); double polar = std::acos(z / d); double slope = -polar + PI / 2.; REQUIRE(std::abs(slope) >= cfg.bridge_slope - EPSILON); }; - + for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); REQUIRE(max_bridgelen <= cfg.max_bridge_length_mm); - + max_bridgelen = 0; for (auto &bridge : stree.crossbridges()) chck_bridge(bridge, max_bridgelen); - + double md = cfg.max_pillar_link_distance_mm / std::cos(-cfg.bridge_slope); REQUIRE(max_bridgelen <= md); } @@ -212,35 +212,35 @@ void test_supports(const std::string & obj_filename, { using namespace Slic3r; TriangleMesh mesh = load_model(obj_filename); - + REQUIRE_FALSE(mesh.empty()); - + TriangleMeshSlicer slicer{&mesh}; - + auto bb = mesh.bounding_box(); double zmin = bb.min.z(); double zmax = bb.max.z(); double gnd = zmin - supportcfg.object_elevation_mm; auto layer_h = 0.05f; - + out.slicegrid = grid(float(gnd), float(zmax), layer_h); slicer.slice(out.slicegrid , CLOSING_RADIUS, &out.model_slices, []{}); - + // Create the special index-triangle mesh with spatial indexing which // is the input of the support point and support mesh generators sla::EigenMesh3D emesh{mesh}; - + // Create the support point generator sla::SLAAutoSupports::Config autogencfg; autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); sla::SLAAutoSupports point_gen{emesh, out.model_slices, out.slicegrid, autogencfg, [] {}, [](int) {}}; - + // Get the calculated support points. std::vector support_points = point_gen.output(); - + int validityflags = ASSUME_NO_REPAIR; - + // If there is no elevation, support points shall be removed from the // bottom of the object. if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { @@ -249,32 +249,32 @@ void test_supports(const std::string & obj_filename, } else { // Should be support points at least on the bottom of the model REQUIRE_FALSE(support_points.empty()); - + // Also the support mesh should not be empty. validityflags |= ASSUME_NO_EMPTY; } - + // Generate the actual support tree sla::SupportTreeBuilder treebuilder; treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); - + check_support_tree_integrity(treebuilder, supportcfg); - + const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); - + check_validity(output_mesh, validityflags); - + // Quick check if the dimensions and placement of supports are correct auto obb = output_mesh.bounding_box(); - + double allowed_zmin = zmin - supportcfg.object_elevation_mm; - + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; - + REQUIRE(obb.min.z() >= allowed_zmin); REQUIRE(obb.max.z() <= zmax); - + // Move out the support tree into the byproducts, we can examine it further // in various tests. out.obj_fname = std::move(obj_filename); @@ -296,7 +296,7 @@ void export_failed_case(const std::vector &support_slices, const ExPolygons &sup_slice = support_slices[n]; const ExPolygons &mod_slice = byproducts.model_slices[n]; Polygons intersections = intersection(sup_slice, mod_slice); - + std::stringstream ss; if (!intersections.empty()) { ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; @@ -307,7 +307,7 @@ void export_failed_case(const std::vector &support_slices, svg.Close(); } } - + TriangleMesh m; byproducts.supporttree.retrieve_full_mesh(m); m.merge(byproducts.input_mesh); @@ -321,56 +321,56 @@ void test_support_model_collision( const sla::SupportConfig &input_supportcfg = {}) { SupportByproducts byproducts; - + sla::SupportConfig supportcfg = input_supportcfg; - + // Set head penetration to a small negative value which should ensure that // the supports will not touch the model body. supportcfg.head_penetration_mm = -0.15; - + // TODO: currently, the tailheads penetrating into the model body do not // respect the penetration parameter properly. No issues were reported so // far but we should definitely fix this. supportcfg.ground_facing_only = true; - + test_supports(obj_filename, supportcfg, byproducts); - + // Slice the support mesh given the slice grid of the model. std::vector support_slices = byproducts.supporttree.slice(byproducts.slicegrid, CLOSING_RADIUS); - + // The slices originate from the same slice grid so the numbers must match - + bool support_mesh_is_empty = byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); - + if (support_mesh_is_empty) REQUIRE(support_slices.empty()); else REQUIRE(support_slices.size() == byproducts.model_slices.size()); - + bool notouch = true; for (size_t n = 0; notouch && n < support_slices.size(); ++n) { const ExPolygons &sup_slice = support_slices[n]; const ExPolygons &mod_slice = byproducts.model_slices[n]; - + Polygons intersections = intersection(sup_slice, mod_slice); - + notouch = notouch && intersections.empty(); } - + if (!notouch) export_failed_case(support_slices, byproducts); - + REQUIRE(notouch); } -const char * const BELOW_PAD_TEST_OBJECTS[] = { +const char *const BELOW_PAD_TEST_OBJECTS[] = { "20mm_cube.obj", "V.obj", }; -const char * const AROUND_PAD_TEST_OBJECTS[] = { +const char *const AROUND_PAD_TEST_OBJECTS[] = { "20mm_cube.obj", "V.obj", "frog_legs.obj", @@ -392,46 +392,46 @@ template void test_pairhash() I A[nums] = {0}, B[nums] = {0}; std::unordered_set CH; std::unordered_map> ints; - + std::random_device rd; std::mt19937 gen(rd()); - + const I Ibits = int(sizeof(I) * CHAR_BIT); const II IIbits = int(sizeof(II) * CHAR_BIT); - + int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits; if (std::is_signed::value) bits -= 1; const I Imin = 0; const I Imax = I(std::pow(2., bits) - 1); - + std::uniform_int_distribution dis(Imin, Imax); - + for (size_t i = 0; i < nums;) { I a = dis(gen); if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; } } - + for (size_t i = 0; i < nums;) { I b = dis(gen); if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; } } - + for (size_t i = 0; i < nums; ++i) { I a = A[i], b = B[i]; - + REQUIRE(a != b); - + II hash_ab = sla::pairhash(a, b); II hash_ba = sla::pairhash(b, a); REQUIRE(hash_ab == hash_ba); - + auto it = ints.find(hash_ab); - + if (it != ints.end()) { REQUIRE(( (it->second.first == a && it->second.second == b) || (it->second.first == b && it->second.second == a) - )); + )); } else ints[hash_ab] = std::make_pair(a, b); } @@ -446,72 +446,72 @@ TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { TEST_CASE("Flat pad geometry is valid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Disable wings padcfg.wall_height_mm = .0; - + for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("WingedPadGeometryIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 1.; - + for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("FlatPadAroundObjectIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 0.; // padcfg.embed_object.stick_stride_mm = 0.; padcfg.embed_object.enabled = true; padcfg.embed_object.everywhere = true; - + for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("WingedPadAroundObjectIsValid", "[SLASupportGeneration]") { sla::PadConfig padcfg; - + // Add some wings to the pad to test the cavity padcfg.wall_height_mm = 1.; padcfg.embed_object.enabled = true; padcfg.embed_object.everywhere = true; - + for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg); } TEST_CASE("ElevatedSupportGeometryIsValid", "[SLASupportGeneration]") { sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 5.; - + for (auto fname : SUPPORT_TEST_MODELS) test_supports(fname); } TEST_CASE("FloorSupportGeometryIsValid", "[SLASupportGeneration]") { sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 0; - + for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg); } TEST_CASE("ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration]") { - + sla::SupportConfig supportcfg; - + for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); } TEST_CASE("FloorSupportsDoNotPierceModel", "[SLASupportGeneration]") { - + sla::SupportConfig supportcfg; supportcfg.object_elevation_mm = 0; - + for (auto fname : SUPPORT_TEST_MODELS) test_support_model_collision(fname, supportcfg); } @@ -525,7 +525,7 @@ TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") { // Default Prusa SL1 display parameters sla::Raster::Resolution res{2560, 1440}; sla::Raster::PixelDim pixdim{120. / res.width_px, 68. / res.height_px}; - + sla::Raster raster; raster.reset(res, pixdim); REQUIRE_FALSE(raster.empty()); @@ -547,54 +547,54 @@ static void check_raster_transformations(sla::Raster::Orientation o, double disp_w = 120., disp_h = 68.; sla::Raster::Resolution res{2560, 1440}; sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; - + auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); sla::Raster::Trafo trafo{o, mirroring}; trafo.origin_x = bb.center().x(); trafo.origin_y = bb.center().y(); - + sla::Raster raster{res, pixdim, trafo}; - + // create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors) coord_t pw = 32 * coord_t(std::ceil(scaled(pixdim.w_mm))); coord_t ph = 32 * coord_t(std::ceil(scaled(pixdim.h_mm))); ExPolygon box; box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}}; - + double tr_x = scaled(20.), tr_y = tr_x; - + box.translate(tr_x, tr_y); ExPolygon expected_box = box; - + // Now calculate the position of the translated box according to output // trafo. if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.); - + if (mirroring[X]) for (auto &p : expected_box.contour.points) p.x() = -p.x(); - + if (mirroring[Y]) for (auto &p : expected_box.contour.points) p.y() = -p.y(); - + raster.draw(box); - + Point expected_coords = expected_box.contour.bounding_box().center(); double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm; double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm; auto w = size_t(std::floor(rx)); auto h = res.height_px - size_t(std::floor(ry)); - + REQUIRE((w < res.width_px && h < res.height_px)); - + auto px = raster.read_pixel(w, h); - + if (px != FullWhite) { sla::PNGImage img; std::fstream outf("out.png", std::ios::out); - + outf << img.serialize(raster); } - + REQUIRE(px == FullWhite); } @@ -603,7 +603,7 @@ TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") { sla::Raster::MirrorX, sla::Raster::MirrorY, sla::Raster::MirrorXY}; - + sla::Raster::Orientation orientations[] = {sla::Raster::roLandscape, sla::Raster::roPortrait}; for (auto orientation : orientations) @@ -615,7 +615,7 @@ static ExPolygon square_with_hole(double v) { ExPolygon poly; coord_t V = scaled(v / 2.); - + poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}}; poly.holes.emplace_back(); V = V / 2; @@ -631,16 +631,16 @@ static double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim) static double raster_white_area(const sla::Raster &raster) { if (raster.empty()) return std::nan(""); - + auto res = raster.resolution(); double a = 0; - + for (size_t x = 0; x < res.width_px; ++x) for (size_t y = 0; y < res.height_px; ++y) { auto px = raster.read_pixel(x, y); a += pixel_area(px, raster.pixel_dimensions()); } - + return a; } @@ -648,15 +648,15 @@ static double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd) { auto lines = p.lines(); double pix_err = pixel_area(FullWhite, pd) / 2.; - + // Worst case is when a line is parallel to the shorter axis of one pixel, // when the line will be composed of the max number of pixels double pix_l = std::min(pd.h_mm, pd.w_mm); - + double error = 0.; for (auto &l : lines) error += (unscaled(l.length()) / pix_l) * pix_err; - + return error; } @@ -664,28 +664,42 @@ TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { double disp_w = 120., disp_h = 68.; sla::Raster::Resolution res{2560, 1440}; sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px}; - + sla::Raster raster{res, pixdim}; auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)}); - + ExPolygon poly = square_with_hole(10.); poly.translate(bb.center().x(), bb.center().y()); raster.draw(poly); - + double a = poly.area() / (scaled(1.) * scaled(1.)); double ra = raster_white_area(raster); double diff = std::abs(a - ra); - + REQUIRE(diff <= predict_error(poly, pixdim)); - + raster.clear(); poly = square_with_hole(60.); poly.translate(bb.center().x(), bb.center().y()); raster.draw(poly); - + a = poly.area() / (scaled(1.) * scaled(1.)); ra = raster_white_area(raster); diff = std::abs(a - ra); - + REQUIRE(diff <= predict_error(poly, pixdim)); } + +TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]") +{ + sla::Contour3D cntr; + + { + std::fstream infile{"extruder_idler_quads.obj", std::ios::in}; + cntr.from_obj(infile); + } + + + + +} diff --git a/tests/sla_print/sla_print_tests_main.cpp b/tests/sla_print/sla_print_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/sla_print/sla_print_tests_main.cpp @@ -0,0 +1 @@ +#include From d9d11e5686607fa4659dbd9f9bfef3760f73bc60 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 5 Nov 2019 09:43:42 +0100 Subject: [PATCH 018/130] Add additional parameters to openvdbutils --- src/libslic3r/OpenVDBUtils.cpp | 39 +++++++++++++++++++++++----- src/libslic3r/OpenVDBUtils.hpp | 14 +++++++--- tests/libslic3r/test_hollowing.cpp | 41 +++++++++++++++++++++++++----- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index a5d4f0db69..9842db2da9 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -50,18 +50,32 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n, pos = {p.x(), p.y(), p.z()}; } -openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, + float exteriorBandWidth, + float interiorBandWidth, + int flags, const openvdb::math::Transform &tr) { + openvdb::initialize(); return openvdb::tools::meshToVolume( - TriangleMeshDataAdapter{mesh}, tr); + TriangleMeshDataAdapter{mesh}, tr, exteriorBandWidth, + interiorBandWidth, flags); } +// TODO: Do I need to call initialize? Seems to work without it as well but the +// docs say it should be called ones. It does a mutex lock-unlock sequence all +// even if was called previously. + openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + float exteriorBandWidth, + float interiorBandWidth, + int flags, const openvdb::math::Transform &tr) { + openvdb::initialize(); return openvdb::tools::meshToVolume( - Contour3DDataAdapter{mesh}, tr); + Contour3DDataAdapter{mesh}, tr, exteriorBandWidth, interiorBandWidth, + flags); } inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } @@ -69,11 +83,14 @@ inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } -sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +template +sla::Contour3D _volumeToMesh(const Grid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { + openvdb::initialize(); + std::vector points; std::vector triangles; std::vector quads; @@ -93,4 +110,12 @@ sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, return ret; } +sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + return _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles); +} + } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index e424038876..ee740dd06b 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -7,11 +7,17 @@ namespace Slic3r { -openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, - const openvdb::math::Transform &tr); +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + const openvdb::math::Transform &tr = {}); -openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, - const openvdb::math::Transform &tr); +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D &mesh, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + const openvdb::math::Transform &tr = {}); sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, double isovalue = 0.0, diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index c0acb4f25d..b74eb76aba 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -29,23 +29,50 @@ static bool _check_normals(const Slic3r::sla::Contour3D &mesh) return false; } -TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +TEST_CASE("Passing OpenVDB grid conversion produce similar geometry.", "[Hollowing]") { - Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{load_model("20mm_cube.obj")}; + Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); + Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{in_mesh}; auto ptr = Slic3r::meshToVolume(imesh, {}); REQUIRE(ptr); - Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -1., 0.0, true); + std::cout << "Grid class = " << ptr->getGridClass() << std::endl; + + Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -2.9, 1.0, true); + + std::cout << "Triangle count: " << omesh.faces3.size() << std::endl; + std::cout << "Quad count: " << omesh.faces4.size() << std::endl; REQUIRE(!omesh.empty()); + SECTION("Converting to Contour3D to TriangleMesh") { + Slic3r::TriangleMesh msh = Slic3r::sla::to_triangle_mesh(omesh); + + msh.require_shared_vertices(); + msh.WriteOBJFile("out_tr.obj"); + + REQUIRE(msh.volume() == Approx(in_mesh.volume())); + } - +// omesh.faces4.clear(); std::fstream outfile{"out.obj", std::ios::out}; omesh.to_obj(outfile); - imesh.merge(omesh); - std::fstream merged_outfile("merged_out.obj", std::ios::out); - imesh.to_obj(merged_outfile); } + +//TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +//{ +// Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{load_model("20mm_cube.obj")}; +// auto ptr = Slic3r::meshToVolume(imesh, {}); + +// REQUIRE(ptr); + +// Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -1., 0.0, true); + +// REQUIRE(!omesh.empty()); + +// imesh.merge(omesh); +// std::fstream merged_outfile("merged_out.obj", std::ios::out); +// imesh.to_obj(merged_outfile); +//} From 04bcdff1105694469a127983135295670624fb6a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 4 Nov 2019 17:01:26 +0100 Subject: [PATCH 019/130] Added new hollowing gizmo Basically just copied SLA support gizmo and removed all functionality that will not be needed --- resources/icons/hollow.svg | 42 + src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 1019 +++++++++++++++++++++ src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 160 ++++ src/slic3r/GUI/Gizmos/GLGizmos.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 23 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 7 files changed, 1241 insertions(+), 7 deletions(-) create mode 100644 resources/icons/hollow.svg create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp create mode 100644 src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp diff --git a/resources/icons/hollow.svg b/resources/icons/hollow.svg new file mode 100644 index 0000000000..119fb6afcc --- /dev/null +++ b/resources/icons/hollow.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 17b76e6296..5a527990cb 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -47,6 +47,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoFlatten.hpp GUI/Gizmos/GLGizmoCut.cpp GUI/Gizmos/GLGizmoCut.hpp + GUI/Gizmos/GLGizmoHollow.cpp + GUI/Gizmos/GLGizmoHollow.hpp GUI/GLSelectionRectangle.cpp GUI/GLSelectionRectangle.hpp GUI/GLTexture.hpp diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp new file mode 100644 index 0000000000..8063d58904 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -0,0 +1,1019 @@ +// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. +#include "GLGizmoHollow.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Gizmos/GLGizmos.hpp" + +#include + +//#include +//#include +//#include + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_ObjectSettings.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/MeshUtils.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/SLAPrint.hpp" + + +namespace Slic3r { +namespace GUI { + +GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) + : GLGizmoBase(parent, icon_filename, sprite_id) + , m_quadric(nullptr) + , m_its(nullptr) +{ + m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + // using GLU_FILL does not work when the instance's transformation + // contains mirroring (normals are reverted) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +} + +GLGizmoHollow::~GLGizmoHollow() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} + +bool GLGizmoHollow::on_init() +{ + m_shortcut_key = WXK_CONTROL_L; + + m_desc["head_diameter"] = _(L("Head diameter")) + ": "; + m_desc["lock_supports"] = _(L("Lock supports under new islands")); + m_desc["remove_selected"] = _(L("Remove selected points")); + m_desc["remove_all"] = _(L("Remove all points")); + m_desc["apply_changes"] = _(L("Apply changes")); + m_desc["discard_changes"] = _(L("Discard changes")); + m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; + m_desc["points_density"] = _(L("Support points density")) + ": "; + m_desc["auto_generate"] = _(L("Auto-generate points")); + m_desc["manual_editing"] = _(L("Manual editing")); + m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; + m_desc["reset_direction"] = _(L("Reset direction")); + + return true; +} + +void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Selection& selection) +{ + if (! model_object || selection.is_empty()) { + m_model_object = nullptr; + return; + } + + if (m_model_object != model_object || m_model_object_id != model_object->id()) { + m_model_object = model_object; + m_print_object_idx = -1; + } + + m_active_instance = selection.get_instance_idx(); + + if (model_object && selection.is_from_single_instance()) + { + // Cache the bb - it's needed for dealing with the clipping plane quite often + // It could be done inside update_mesh but one has to account for scaling of the instance. + //FIXME calling ModelObject::instance_bounding_box() is expensive! + m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); + + if (is_mesh_update_necessary()) { + update_mesh(); + reload_cache(); + } + + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + } + else + m_parent.toggle_model_objects_visibility(true, nullptr, -1); + } +} + + + +void GLGizmoHollow::on_render() const +{ + const Selection& selection = m_parent.get_selection(); + + // If current m_model_object does not match selection, ask GLCanvas3D to turn us off + if (m_state == On + && (m_model_object != selection.get_model()->objects[selection.get_object_idx()] + || m_active_instance != selection.get_instance_idx() + || m_model_object_id != m_model_object->id())) { + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); + return; + } + + if (! m_its || ! m_mesh) + const_cast(this)->update_mesh(); + + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + + if (m_quadric != nullptr && selection.is_from_single_instance()) + render_points(selection, false); + + m_selection_rectangle.render(m_parent); + render_clipping_plane(selection); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoHollow::render_clipping_plane(const Selection& selection) const +{ + if (m_clipping_plane_distance == 0.f) + return; + + // Get transformation of the instance + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + Geometry::Transformation trafo = vol->get_instance_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + + // Get transformation of supports + Geometry::Transformation supports_trafo; + supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), vol->get_sla_shift_z())); + supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2))); + // I don't know why, but following seems to be correct. + supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2), + 1, + 1.)); + + // Now initialize the TMS for the object, perform the cut and save the result. + if (! m_object_clipper) { + m_object_clipper.reset(new MeshClipper); + m_object_clipper->set_mesh(*m_mesh); + } + m_object_clipper->set_plane(*m_clipping_plane); + m_object_clipper->set_transformation(trafo); + + + // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. + // First we need a pointer to the respective SLAPrintObject. The index into objects vector is + // cached so we don't have todo it on each render. We only search for the po if needed: + if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) { + m_print_objects_count = m_parent.sla_print()->objects().size(); + m_print_object_idx = -1; + for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { + ++m_print_object_idx; + if (po->model_object()->id() == m_model_object->id()) + break; + } + } + if (m_print_object_idx >= 0) { + const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; + + if (print_object->is_step_done(slaposSupportTree)) { + // If the supports are already calculated, save the timestamp of the respective step + // so we can later tell they were recalculated. + size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; + + if (! m_supports_clipper || (int)timestamp != m_old_timestamp) { + // The timestamp has changed. + m_supports_clipper.reset(new MeshClipper); + // The mesh should already have the shared vertices calculated. + m_supports_clipper->set_mesh(print_object->support_mesh()); + m_old_timestamp = timestamp; + } + m_supports_clipper->set_plane(*m_clipping_plane); + m_supports_clipper->set_transformation(supports_trafo); + } + else + // The supports are not valid. We better dump the cached data. + m_supports_clipper.reset(); + } + + // At this point we have the triangulated cuts for both the object and supports - let's render. + if (! m_object_clipper->get_triangles().empty()) { + ::glPushMatrix(); + ::glColor3f(1.0f, 0.37f, 0.0f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : m_object_clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } + + if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { + // The supports are hidden in the editing mode, so it makes no sense to render the cuts. + ::glPushMatrix(); + ::glColor3f(1.0f, 0.f, 0.37f); + ::glBegin(GL_TRIANGLES); + for (const Vec3f& point : m_supports_clipper->get_triangles()) + ::glVertex3f(point(0), point(1), point(2)); + ::glEnd(); + ::glPopMatrix(); + } +} + + +void GLGizmoHollow::on_render_for_picking() const +{ + const Selection& selection = m_parent.get_selection(); +#if ENABLE_RENDER_PICKING_PASS + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); +#endif + + glsafe(::glEnable(GL_DEPTH_TEST)); + render_points(selection, true); +} + +void GLGizmoHollow::render_points(const Selection& selection, bool picking) const +{ + if (!picking) + glsafe(::glEnable(GL_LIGHTING)); + + const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); + const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); + const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); + + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0.0, 0.0, m_z_shift)); + glsafe(::glMultMatrixd(instance_matrix.data())); + + float render_color[4]; + size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); + for (size_t i = 0; i < cache_size; ++i) + { + const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; + const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; + + if (is_mesh_point_clipped(support_point.pos.cast())) + continue; + + // First decide about the color of the point. + if (picking) { + std::array color = picking_color_component(i); + render_color[0] = color[0]; + render_color[1] = color[1]; + render_color[2] = color[2]; + render_color[3] = color[3]; + } + else { + render_color[3] = 1.f; + if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active + render_color[0] = 0.f; + render_color[1] = 1.0f; + render_color[2] = 1.0f; + } + else { // neigher hover nor picking + render_color[0] = point_selected ? 1.0f : 0.7f; + render_color[1] = point_selected ? 0.3f : 0.7f; + render_color[2] = point_selected ? 0.3f : 0.7f; + } + } + glsafe(::glColor4fv(render_color)); + float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f}; + glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); + + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + // Matrices set, we can render the point mark now. + // If in editing mode, we'll also render a cone pointing to the sphere. + if (m_editing_mode) { + // in case the normal is not yet cached, find and cache it + if (m_editing_cache[i].normal == Vec3f::Zero()) + m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + + const double cone_radius = 0.25; // mm + const double cone_height = 0.75; + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale)); + ::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1); + glsafe(::glTranslatef(0.f, 0.f, cone_height)); + ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); + glsafe(::glPopMatrix()); + } + ::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + + glsafe(::glPopMatrix()); + } + + { + // Reset emissive component to zero (the default value) + float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f }; + glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); + } + + if (!picking) + glsafe(::glDisable(GL_LIGHTING)); + + glsafe(::glPopMatrix()); +} + + + +bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const +{ + if (m_clipping_plane_distance == 0.f) + return false; + + Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; + transformed_point(2) += m_z_shift; + return m_clipping_plane->is_point_clipped(transformed_point); +} + + + +bool GLGizmoHollow::is_mesh_update_necessary() const +{ + return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) + && ((m_model_object->id() != m_model_object_id) || m_its == nullptr); +} + + + +void GLGizmoHollow::update_mesh() +{ + if (! m_model_object) + return; + + wxBusyCursor wait; + // 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(); + m_its = &m_mesh->its; + + // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. + if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) + m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); + + m_model_object_id = m_model_object->id(); +} + + + +// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal +// Return false if no intersection was found, true otherwise. +bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) +{ + // if the gizmo doesn't have the V, F structures for igl, calculate them first: + if (! m_mesh_raycaster) + update_mesh(); + + const Camera& camera = m_parent.get_camera(); + const Selection& selection = m_parent.get_selection(); + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + Geometry::Transformation trafo = volume->get_instance_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + + // The raycaster query + Vec3f hit; + Vec3f normal; + if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; + } + else + return false; +} + +// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. +// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is +// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo +// concludes that the event was not intended for it, it should return false. +bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + if (m_editing_mode) { + + // left down with shift - show the selection rectangle: + if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { + if (m_hover_id == -1) { + if (shift_down || alt_down) { + m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); + } + } + else { + if (m_editing_cache[m_hover_id].selected) + unselect_point(m_hover_id); + else { + if (!alt_down) + select_point(m_hover_id); + } + } + + return true; + } + + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point"))); + m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; + } + else + return false; + } + else + select_point(NoPoints); + + return true; + } + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + // First collect positions of all the points in world coordinates. + Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + std::vector points; + for (unsigned int i=0; i()); + + // Now ask the rectangle which of the points are inside. + std::vector points_inside; + std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); + for (size_t idx : points_idxs) + points_inside.push_back(points[idx].cast()); + + // Only select/deselect points that are actually visible + for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + { + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); + } + return true; + } + + // left up with no selection rectangle + if (action == SLAGizmoEventType::LeftUp) { + if (m_wait_for_up_event) { + m_wait_for_up_event = false; + return true; + } + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_wait_for_up_event) + return true; // point has been placed and the button not released yet + // this prevents GLCanvas from starting scene rotation + + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); + return true; + } + + return false; + } + + if (action == SLAGizmoEventType::Delete) { + // delete key pressed + delete_selected_points(); + return true; + } + + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + delete_selected_points(); + return true; + } + return false; + } + + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; + } + } + + if (action == SLAGizmoEventType::MouseWheelUp && control_down) { + m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); + update_clipping_plane(true); + return true; + } + + if (action == SLAGizmoEventType::MouseWheelDown && control_down) { + m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); + update_clipping_plane(true); + return true; + } + + if (action == SLAGizmoEventType::ResetClippingPlane) { + update_clipping_plane(); + return true; + } + + return false; +} + +void GLGizmoHollow::delete_selected_points(bool force) +{ + if (! m_editing_mode) { + std::cout << "DEBUGGING: delete_selected_points called out of editing mode!" << std::endl; + std::abort(); + } + + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point"))); + + for (unsigned int idx=0; idx pos_and_normal; + if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) + return; + m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first; + m_editing_cache[m_hover_id].support_point.is_new_island = false; + m_editing_cache[m_hover_id].normal = pos_and_normal.second; + // Do not update immediately, wait until the mouse is released. + // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } + } +} + +std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const +{ + std::vector out; + + if (!m_model_object) + return out; + + const DynamicPrintConfig& object_cfg = m_model_object->config; + const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + std::unique_ptr default_cfg = nullptr; + + for (const std::string& key : keys) { + if (object_cfg.has(key)) + out.push_back(object_cfg.option(key)); + else + if (print_cfg.has(key)) + out.push_back(print_cfg.option(key)); + else { // we must get it from defaults + if (default_cfg == nullptr) + default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys)); + out.push_back(default_cfg->option(key)); + } + } + + return out; +} + + +ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const +{ + if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + return ClippingPlane::ClipsNothing(); + else + return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); +} + + +void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) +{ + if (!m_model_object) + return; + + bool first_run = true; // This is a hack to redraw the button when all points are removed, + // so it is not delayed until the background process finishes. +RENDER_AGAIN: + //m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); + //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); + //ImGui::SetNextWindowSize(ImVec2(window_size)); + + const float approx_height = m_imgui->scaled(18.0f); + y = std::min(y, bottom_limit - approx_height); + m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); + m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + + const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); + const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f); + const float minimal_slider_width = m_imgui->scaled(4.f); + const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f); + const float lock_supports_width_approx = m_imgui->calc_text_size(m_desc.at("lock_supports")).x + m_imgui->scaled(2.f); + + float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); + window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); + + + bool force_refresh = false; + bool remove_selected = false; + bool remove_all = false; + + if (m_editing_mode) { + + float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; + if (m_new_point_head_diameter > diameter_upper_cap) + m_new_point_head_diameter = diameter_upper_cap; + m_imgui->text(m_desc.at("head_diameter")); + ImGui::SameLine(diameter_slider_left); + ImGui::PushItemWidth(window_width - diameter_slider_left); + + // Following is a nasty way to: + // - save the initial value of the slider before one starts messing with it + // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene + // - take correct undo/redo snapshot after the user is done with moving the slider + float initial_value = m_new_point_head_diameter; + ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); + if (ImGui::IsItemClicked()) { + if (m_old_point_head_diameter == 0.f) + m_old_point_head_diameter = initial_value; + } + if (ImGui::IsItemEdited()) { + for (auto& cache_entry : m_editing_cache) + if (cache_entry.selected) + cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + // momentarily restore the old value to take snapshot + for (auto& cache_entry : m_editing_cache) + if (cache_entry.selected) + cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f; + float backup = m_new_point_head_diameter; + m_new_point_head_diameter = m_old_point_head_diameter; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change point head diameter"))); + m_new_point_head_diameter = backup; + for (auto& cache_entry : m_editing_cache) + if (cache_entry.selected) + cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; + m_old_point_head_diameter = 0.f; + } + + m_imgui->disabled_begin(m_selection_empty); + remove_selected = m_imgui->button(m_desc.at("remove_selected")); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(m_editing_cache.empty()); + remove_all = m_imgui->button(m_desc.at("remove_all")); + m_imgui->disabled_end(); + + m_imgui->text(" "); // vertical gap + } + else { // not in editing mode: + m_imgui->text(m_desc.at("minimal_distance")); + ImGui::SameLine(settings_sliders_left); + ImGui::PushItemWidth(window_width - settings_sliders_left); + + std::vector opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"}); + float density = static_cast(opts[0])->value; + float minimal_point_distance = static_cast(opts[1])->value; + + ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); + bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider + bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider + bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + + m_imgui->text(m_desc.at("points_density")); + ImGui::SameLine(settings_sliders_left); + + ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%"); + slider_clicked |= ImGui::IsItemClicked(); + slider_edited |= ImGui::IsItemEdited(); + slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + + if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo + m_minimal_point_distance_stash = minimal_point_distance; + m_density_stash = density; + } + if (slider_edited) { + m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + } + if (slider_released) { + m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; + m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); + m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + wxGetApp().obj_list()->update_and_show_object_settings_item(); + } + + + m_imgui->disabled_begin(m_normal_cache.empty()); + remove_all = m_imgui->button(m_desc.at("remove_all")); + m_imgui->disabled_end(); + + } + + + // Following is rendered in both editing and non-editing mode: + m_imgui->text(""); + if (m_clipping_plane_distance == 0.f) + m_imgui->text(m_desc.at("clipping_of_view")); + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this](){ + update_clipping_plane(); + }); + } + } + + ImGui::SameLine(clipping_slider_left); + ImGui::PushItemWidth(window_width - clipping_slider_left); + if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + update_clipping_plane(true); + + + if (m_imgui->button("?")) { + wxGetApp().CallAfter([]() { + SlaGizmoHelpDialog help_dlg; + help_dlg.ShowModal(); + }); + } + + m_imgui->end(); + + if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode + m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); + force_refresh = true; + } + m_old_editing_state = m_editing_mode; + + if (remove_selected || remove_all) { + force_refresh = false; + m_parent.set_as_dirty(); + bool was_in_editing = m_editing_mode; + if (remove_all) { + select_point(AllPoints); + delete_selected_points(true); // true - delete regardless of locked status + } + if (remove_selected) + delete_selected_points(false); // leave locked points + + if (first_run) { + first_run = false; + goto RENDER_AGAIN; + } + } + + if (force_refresh) + m_parent.set_as_dirty(); +} + +bool GLGizmoHollow::on_is_activable() const +{ + const Selection& selection = m_parent.get_selection(); + + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA + || !selection.is_from_single_instance()) + return false; + + // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. + const Selection::IndicesList& list = selection.get_volume_idxs(); + for (const auto& idx : list) + if (selection.get_volume(idx)->is_outside && selection.get_volume(idx)->composite_id.volume_id >= 0) + return false; + + return true; +} + +bool GLGizmoHollow::on_is_selectable() const +{ + return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA); +} + +std::string GLGizmoHollow::on_get_name() const +{ + return (_(L("Hollowing")) + " [H]").ToUTF8().data(); +} + + + +void GLGizmoHollow::on_set_state() +{ + // m_model_object pointer can be invalid (for instance because of undo/redo action), + // we should recover it from the object id + m_model_object = nullptr; + for (const auto mo : wxGetApp().model().objects) { + if (mo->id() == m_model_object_id) { + m_model_object = mo; + break; + } + } + + if (m_state == m_old_state) + return; + + if (m_state == On && m_old_state != On) { // the gizmo was just turned on + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); + if (is_mesh_update_necessary()) + update_mesh(); + + // we'll now reload support points: + if (m_model_object) + reload_cache(); + + m_parent.toggle_model_objects_visibility(false); + if (m_model_object) + m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + + // Set default head diameter from config. + const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; + } + if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off + bool will_ask = m_model_object && false; + if (will_ask) { + wxGetApp().CallAfter([this]() { + }); + // refuse to be turned off so the gizmo is active when the CallAfter is executed + m_state = m_old_state; + } + else { + // we are actually shutting down + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); + m_parent.toggle_model_objects_visibility(true); + m_normal_cache.clear(); + m_clipping_plane_distance = 0.f; + // Release clippers and the AABB raycaster. + m_its = nullptr; + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_mesh_raycaster.reset(); + } + } + m_old_state = m_state; +} + + + +void GLGizmoHollow::on_start_dragging() +{ + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + m_point_before_drag = m_editing_cache[m_hover_id]; + } + else + m_point_before_drag = CacheEntry(); +} + + +void GLGizmoHollow::on_stop_dragging() +{ + if (m_hover_id != -1) { + CacheEntry backup = m_editing_cache[m_hover_id]; + + if (m_point_before_drag.support_point.pos != Vec3f::Zero() // some point was touched + && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected + { + m_editing_cache[m_hover_id] = m_point_before_drag; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point"))); + m_editing_cache[m_hover_id] = backup; + } + } + m_point_before_drag = CacheEntry(); +} + + + +void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) +{ + ar(m_clipping_plane_distance, + *m_clipping_plane, + m_model_object_id, + m_new_point_head_diameter, + m_normal_cache, + m_editing_cache, + m_selection_empty + ); +} + + + +void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const +{ + ar(m_clipping_plane_distance, + *m_clipping_plane, + m_model_object_id, + m_new_point_head_diameter, + m_normal_cache, + m_editing_cache, + m_selection_empty + ); +} + + + +void GLGizmoHollow::select_point(int i) +{ + if (! m_editing_mode) { + std::cout << "DEBUGGING: select_point called when out of editing mode!" << std::endl; + std::abort(); + } + + if (i == AllPoints || i == NoPoints) { + for (auto& point_and_selection : m_editing_cache) + point_and_selection.selected = ( i == AllPoints ); + m_selection_empty = (i == NoPoints); + + if (i == AllPoints) + m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f; + } + else { + m_editing_cache[i].selected = true; + m_selection_empty = false; + m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f; + } +} + + +void GLGizmoHollow::unselect_point(int i) +{ + if (! m_editing_mode) { + std::cout << "DEBUGGING: unselect_point called when out of editing mode!" << std::endl; + std::abort(); + } + + m_editing_cache[i].selected = false; + m_selection_empty = true; + for (const CacheEntry& ce : m_editing_cache) { + if (ce.selected) { + m_selection_empty = false; + break; + } + } +} + + +bool GLGizmoHollow::unsaved_changes() const +{ + if (m_editing_cache.size() != m_normal_cache.size()) + return true; + + for (size_t i=0; iget_normal() != Vec3d::Zero() ? + m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); + + const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); + float dist = normal.dot(center); + *m_clipping_plane = ClippingPlane(normal, (dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); + m_parent.set_as_dirty(); +} + + + + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp new file mode 100644 index 0000000000..40c7788459 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -0,0 +1,160 @@ +#ifndef slic3r_GLGizmoHollow_hpp_ +#define slic3r_GLGizmoHollow_hpp_ + +#include "GLGizmoBase.hpp" +#include "slic3r/GUI/GLSelectionRectangle.hpp" + +#include "libslic3r/SLA/SLACommon.hpp" +#include + +#include + + +namespace Slic3r { +namespace GUI { + +class ClippingPlane; +class MeshClipper; +class MeshRaycaster; +enum class SLAGizmoEventType : unsigned char; + +class GLGizmoHollow : public GLGizmoBase +{ +private: + ModelObject* m_model_object = nullptr; + ObjectID m_model_object_id = 0; + int m_active_instance = -1; + float m_active_instance_bb_radius; // to cache the bb + mutable double m_z_shift = 0.f; + bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); + + const float RenderPointScale = 1.f; + + GLUquadricObj* m_quadric; + + std::unique_ptr m_mesh_raycaster; + const TriangleMesh* m_mesh; + const indexed_triangle_set* m_its; + mutable const TriangleMesh* m_supports_mesh; + mutable std::vector m_triangles; + mutable std::vector m_supports_triangles; + mutable int m_old_timestamp = -1; + mutable int m_print_object_idx = -1; + mutable int m_print_objects_count = -1; + + class CacheEntry { + public: + CacheEntry() : + support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {} + + CacheEntry(const sla::SupportPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero()) : + support_point(point), selected(sel), normal(norm) {} + + bool operator==(const CacheEntry& rhs) const { + return (support_point == rhs.support_point); + } + + bool operator!=(const CacheEntry& rhs) const { + return ! ((*this) == rhs); + } + + sla::SupportPoint support_point; + bool selected; // whether the point is selected + Vec3f normal; + + template + void serialize(Archive & ar) + { + ar(support_point, selected, normal); + } + }; + +public: + GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + ~GLGizmoHollow() override; + void set_sla_support_data(ModelObject* model_object, const Selection& selection); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + void delete_selected_points(bool force = false); + ClippingPlane get_sla_clipping_plane() const; + + bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } + +private: + bool on_init() override; + void on_update(const UpdateData& data) override; + void on_render() const override; + void on_render_for_picking() const override; + + //void render_selection_rectangle() const; + void render_points(const Selection& selection, bool picking = false) const; + void render_clipping_plane(const Selection& selection) const; + bool is_mesh_update_necessary() const; + void update_mesh(); + bool unsaved_changes() const; + + bool m_editing_mode = true; // Is editing mode active? + bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). + float m_new_point_head_diameter; // Size of a new point. + CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited + float m_old_point_head_diameter = 0.; // the same + float m_minimal_point_distance_stash = 0.f; // and again + float m_density_stash = 0.f; // and again + mutable std::vector m_editing_cache; // a support point and whether it is currently selected + std::vector m_normal_cache; // to restore after discarding changes or undo/redo + + float m_clipping_plane_distance = 0.f; + std::unique_ptr m_clipping_plane; + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; + + GLSelectionRectangle m_selection_rectangle; + + bool m_wait_for_up_event = false; + bool m_selection_empty = true; + EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) + + mutable std::unique_ptr m_object_clipper; + mutable std::unique_ptr m_supports_clipper; + + std::vector get_config_options(const std::vector& keys) const; + bool is_mesh_point_clipped(const Vec3d& point) const; + //void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; + + // Methods that do the model_object and editing cache synchronization, + // editing mode selection, etc: + enum { + AllPoints = -2, + NoPoints, + }; + void select_point(int i); + void unselect_point(int i); + void reload_cache(); + void update_clipping_plane(bool keep_normal = false) const; + +protected: + void on_set_state() override; + void on_set_hover_id() override + + { + if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) + m_hover_id = -1; + } + void on_start_dragging() override; + void on_stop_dragging() override; + void on_render_input_window(float x, float y, float bottom_limit) override; + + std::string on_get_name() const override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; +}; + + + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoHollow_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmos.hpp b/src/slic3r/GUI/Gizmos/GLGizmos.hpp index 272fa098a3..9f97c42b46 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmos.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmos.hpp @@ -32,5 +32,6 @@ enum class SLAGizmoEventType : unsigned char { #include "slic3r/GUI/Gizmos/GLGizmoFlatten.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp" #include "slic3r/GUI/Gizmos/GLGizmoCut.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #endif //slic3r_GLGizmos_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 25dab03360..1f581c9b5b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -94,6 +94,7 @@ bool GLGizmosManager::init() m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5)); + m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 6)); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { @@ -350,6 +351,7 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) return; dynamic_cast(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); + dynamic_cast(m_gizmos[Hollow].get())->set_sla_support_data(model_object, m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -358,15 +360,22 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p if (!m_enabled || m_gizmos.empty()) return false; - return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + if (m_current == SlaSupports) + return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + if (m_current == Hollow) + return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + return false; } ClippingPlane GLGizmosManager::get_sla_clipping_plane() const { - if (!m_enabled || m_current != SlaSupports || m_gizmos.empty()) + if (!m_enabled || (m_current != SlaSupports && m_current != Hollow) || m_gizmos.empty()) return ClippingPlane::ClipsNothing(); - return dynamic_cast(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); + if (m_current == SlaSupports) + return dynamic_cast(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); + else + return dynamic_cast(m_gizmos[Hollow].get())->get_sla_clipping_plane(); } bool GLGizmosManager::wants_reslice_supports_on_undo() const @@ -464,7 +473,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown()) { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { @@ -489,10 +498,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports)) + else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) // don't allow dragging objects with the Sla gizmo on processed = true; - else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) + else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here m_parent.set_as_dirty(); @@ -568,7 +577,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports || m_current == Hollow) && !m_parent.is_mouse_dragging()) { // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 0defb13483..0368e433ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -60,6 +60,7 @@ public: Flatten, Cut, SlaSupports, + Hollow, Undefined }; From 3fe160e60ad7529339c956ab4208bb228b9e6932 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Nov 2019 10:16:10 +0100 Subject: [PATCH 020/130] Added possibility to change hole height and taper The parameters are yet not saved in ModelObject and they are common for all holes --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 39 +++++++++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 2 ++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 8063d58904..6c06bbbe64 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -195,14 +195,14 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const // At this point we have the triangulated cuts for both the object and supports - let's render. if (! m_object_clipper->get_triangles().empty()) { - ::glPushMatrix(); + ::glPushMatrix(); ::glColor3f(1.0f, 0.37f, 0.0f); ::glBegin(GL_TRIANGLES); for (const Vec3f& point : m_object_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); - ::glPopMatrix(); - } + ::glPopMatrix(); + } if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { // The supports are hidden in the editing mode, so it makes no sense to render the cuts. @@ -212,8 +212,8 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const for (const Vec3f& point : m_supports_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); - ::glPopMatrix(); - } + ::glPopMatrix(); + } } @@ -221,7 +221,7 @@ void GLGizmoHollow::on_render_for_picking() const { const Selection& selection = m_parent.get_selection(); #if ENABLE_RENDER_PICKING_PASS - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); #endif glsafe(::glEnable(GL_DEPTH_TEST)); @@ -257,7 +257,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons render_color[0] = color[0]; render_color[1] = color[1]; render_color[2] = color[2]; - render_color[3] = color[3]; + render_color[3] = color[3]; } else { render_color[3] = 1.f; @@ -270,6 +270,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons render_color[0] = point_selected ? 1.0f : 0.7f; render_color[1] = point_selected ? 0.3f : 0.7f; render_color[2] = point_selected ? 0.3f : 0.7f; + render_color[3] = 0.5f; } } glsafe(::glColor4fv(render_color)); @@ -296,16 +297,20 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - const double cone_radius = 0.25; // mm - const double cone_height = 0.75; + const double cone_radius = double(support_point.head_front_radius) * RenderPointScale; //0.25; // mm + const double cone_height = m_new_cone_height; + //const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.); + const double cone_rad_diff = m_new_cone_angle*cone_radius; glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale)); - ::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1); + glsafe(::glTranslatef(0.f, 0.f, -cone_height/2.)); + //::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1); + ::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1); glsafe(::glTranslatef(0.f, 0.f, cone_height)); - ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); + //::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1); glsafe(::glPopMatrix()); } - ::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); + //::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); if (vol->is_left_handed()) glFrontFace(GL_CCW); @@ -623,7 +628,7 @@ RENDER_AGAIN: //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); //ImGui::SetNextWindowSize(ImVec2(window_size)); - + const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -686,6 +691,10 @@ RENDER_AGAIN: m_old_point_head_diameter = 0.f; } + // !!!! Something as above should be done for the cone angle + ImGui::SliderFloat(" ", &m_new_cone_angle, -1.f, 1.f, "%.1f"); + ImGui::SliderFloat(" ", &m_new_cone_height, 0.1f, 10.f, "%.1f"); + m_imgui->disabled_begin(m_selection_empty); remove_selected = m_imgui->button(m_desc.at("remove_selected")); m_imgui->disabled_end(); @@ -757,7 +766,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 40c7788459..60e2fbf612 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -95,6 +95,8 @@ private: bool m_editing_mode = true; // Is editing mode active? bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). float m_new_point_head_diameter; // Size of a new point. + float m_new_cone_angle = 0.f; + float m_new_cone_height = 5.f; CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited float m_old_point_head_diameter = 0.; // the same float m_minimal_point_distance_stash = 0.f; // and again From 013e61322103c0968fd4cec1da320f3800d384c5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 5 Nov 2019 14:40:22 +0100 Subject: [PATCH 021/130] Hollowing gizmo can now actually trigger the hollowing and render result --- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 71 +++++++++++++++++++++-- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 7 +++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 +- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 2c2676ae77..dc25f77d80 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -486,6 +486,7 @@ public: void set_color_by(const std::string& value); const Camera& get_camera() const { return m_camera; } + const Shader& get_shader() const { return m_shader; } BoundingBoxf3 volumes_bounding_box() const; BoundingBoxf3 scene_bounding_box() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 6c06bbbe64..5b6620ef68 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -17,6 +17,7 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" +#include "libslic3r/OpenVDBUtils.hpp" namespace Slic3r { @@ -57,6 +58,7 @@ bool GLGizmoHollow::on_init() m_desc["manual_editing"] = _(L("Manual editing")); m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; m_desc["reset_direction"] = _(L("Reset direction")); + m_desc["hollow"] = _(L("Hollow")); return true; } @@ -114,6 +116,12 @@ void GLGizmoHollow::on_render() const if (! m_its || ! m_mesh) const_cast(this)->update_mesh(); + if (m_volume_with_cavity) { + m_parent.get_shader().start_using(); + m_volume_with_cavity->render(); + m_parent.get_shader().stop_using(); + } + glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -152,7 +160,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const // Now initialize the TMS for the object, perform the cut and save the result. if (! m_object_clipper) { m_object_clipper.reset(new MeshClipper); - m_object_clipper->set_mesh(*m_mesh); + m_object_clipper->set_mesh(*mesh()); } m_object_clipper->set_plane(*m_clipping_plane); m_object_clipper->set_transformation(trafo); @@ -362,8 +370,15 @@ void GLGizmoHollow::update_mesh() m_mesh = &m_model_object->volumes.front()->mesh(); m_its = &m_mesh->its; - // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. - if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) + // If this is different mesh than last time + if (m_model_object_id != m_model_object->id()) { + m_cavity_mesh.reset(); // dump the cavity + m_volume_with_cavity.reset(); + m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + m_mesh_raycaster.reset(); + } + + if (! m_mesh_raycaster) m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); m_model_object_id = m_model_object->id(); @@ -579,6 +594,32 @@ void GLGizmoHollow::on_update(const UpdateData& data) } } + +void GLGizmoHollow::hollow_mesh(float offset, float adaptibility) +{ + Slic3r::sla::Contour3D imesh{*m_mesh}; + auto ptr = meshToVolume(imesh, {}); + sla::Contour3D omesh = volumeToMesh(*ptr, -offset, adaptibility, true); + + if (omesh.empty()) + return; + + imesh.merge(omesh); + m_cavity_mesh.reset(new TriangleMesh); + *m_cavity_mesh = sla::to_triangle_mesh(imesh); + m_cavity_mesh.get()->require_shared_vertices(); + m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); + m_object_clipper.reset(); + + // create a new GLVolume that only has the cavity inside + m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); + m_volume_with_cavity->finalize_geometry(true); + m_volume_with_cavity->set_volume_transformation(m_model_object->volumes.front()->get_transformation()); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); + m_parent.toggle_model_objects_visibility(false, m_model_object, m_active_instance); +} + std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const { std::vector out; @@ -654,6 +695,10 @@ RENDER_AGAIN: if (m_editing_mode) { + if (m_imgui->button(m_desc.at("hollow"))) { + hollow_mesh(m_offset, m_adaptibility); + } + float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; if (m_new_point_head_diameter > diameter_upper_cap) m_new_point_head_diameter = diameter_upper_cap; @@ -692,7 +737,11 @@ RENDER_AGAIN: } // !!!! Something as above should be done for the cone angle + m_imgui->text("Hole taper: "); + ImGui::SameLine(); ImGui::SliderFloat(" ", &m_new_cone_angle, -1.f, 1.f, "%.1f"); + m_imgui->text("Hole height: "); + ImGui::SameLine(); ImGui::SliderFloat(" ", &m_new_cone_height, 0.1f, 10.f, "%.1f"); m_imgui->disabled_begin(m_selection_empty); @@ -704,6 +753,14 @@ RENDER_AGAIN: m_imgui->disabled_end(); m_imgui->text(" "); // vertical gap + + + m_imgui->text("Offset: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); + m_imgui->text("Adaptibility: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_adaptibility, 0.f, 1.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); @@ -766,7 +823,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); @@ -834,6 +891,10 @@ std::string GLGizmoHollow::on_get_name() const } +const TriangleMesh* GLGizmoHollow::mesh() const { + return (! m_mesh ? nullptr : (m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); +} + void GLGizmoHollow::on_set_state() { @@ -886,6 +947,8 @@ void GLGizmoHollow::on_set_state() m_object_clipper.reset(); m_supports_clipper.reset(); m_mesh_raycaster.reset(); + m_cavity_mesh.reset(); + m_volume_with_cavity.reset(); } } m_old_state = m_state; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 60e2fbf612..82455a9f40 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -33,6 +33,8 @@ private: GLUquadricObj* m_quadric; std::unique_ptr m_mesh_raycaster; + std::unique_ptr m_cavity_mesh; + std::unique_ptr m_volume_with_cavity; const TriangleMesh* m_mesh; const indexed_triangle_set* m_its; mutable const TriangleMesh* m_supports_mesh; @@ -90,7 +92,9 @@ private: void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); + void hollow_mesh(float offset = 2.f, float adaptability = 1.f); bool unsaved_changes() const; + const TriangleMesh* mesh() const; bool m_editing_mode = true; // Is editing mode active? bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). @@ -104,6 +108,9 @@ private: mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo + float m_offset = 2.f; + float m_adaptibility = 1.f; + float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1f581c9b5b..be200b56d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -415,7 +415,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports) { + if (m_current == SlaSupports || m_current == Hollow) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) processed = true; @@ -676,7 +676,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case 'r' : case 'R' : { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) processed = true; break; @@ -688,7 +688,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) case WXK_DELETE: #endif /* __APPLE__ */ { - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Delete)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete)) processed = true; break; From 07fb9f6559554d089d5c3e7241597a126c330e4b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 5 Nov 2019 14:48:00 +0100 Subject: [PATCH 022/130] Add oversampling and gaussian filter to hollowing. * Additional steps hollowing and drilling in SPAPrint * Remove SLABoilerPlate.hpp as it was empty. --- src/libslic3r/CMakeLists.txt | 1 - src/libslic3r/OpenVDBUtils.cpp | 8 +-- src/libslic3r/OpenVDBUtils.hpp | 14 ++--- src/libslic3r/SLA/SLABoilerPlate.hpp | 23 ------- src/libslic3r/SLA/SLABoostAdapter.hpp | 2 +- src/libslic3r/SLA/SLACommon.hpp | 2 + src/libslic3r/SLA/SLAPad.cpp | 2 +- src/libslic3r/SLA/SLARotfinder.cpp | 2 +- src/libslic3r/SLA/SLASupportTree.cpp | 2 +- src/libslic3r/SLA/SLASupportTreeBuilder.hpp | 2 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 2 +- src/libslic3r/SLAPrint.cpp | 29 ++++++--- src/libslic3r/SLAPrint.hpp | 2 + tests/libslic3r/test_hollowing.cpp | 68 ++++++++------------- 14 files changed, 66 insertions(+), 93 deletions(-) delete mode 100644 src/libslic3r/SLA/SLABoilerPlate.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 47e49eadb0..1fe6a38519 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -182,7 +182,6 @@ add_library(libslic3r STATIC ${OpenVDBUtils_SOURCES} SLA/SLACommon.hpp SLA/SLACommon.cpp - SLA/SLABoilerPlate.hpp SLA/SLAPad.hpp SLA/SLAPad.cpp SLA/SLASupportTreeBuilder.hpp diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 9842db2da9..97a57315b6 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -51,10 +51,10 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n, } openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, + const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, - int flags, - const openvdb::math::Transform &tr) + int flags) { openvdb::initialize(); return openvdb::tools::meshToVolume( @@ -67,10 +67,10 @@ openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, // even if was called previously. openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, - int flags, - const openvdb::math::Transform &tr) + int flags) { openvdb::initialize(); return openvdb::tools::meshToVolume( diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index ee740dd06b..eae1e051dc 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -2,22 +2,22 @@ #define OPENVDBUTILS_HPP #include -#include +#include #include namespace Slic3r { -openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, +openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, + const openvdb::math::Transform &tr = {}, float exteriorBandWidth = 3.0f, float interiorBandWidth = 3.0f, - int flags = 0, - const openvdb::math::Transform &tr = {}); + int flags = 0); -openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D &mesh, +openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr = {}, float exteriorBandWidth = 3.0f, float interiorBandWidth = 3.0f, - int flags = 0, - const openvdb::math::Transform &tr = {}); + int flags = 0); sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, double isovalue = 0.0, diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp deleted file mode 100644 index 9be91ac1d1..0000000000 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SLABOILERPLATE_HPP -#define SLABOILERPLATE_HPP - -#include -#include -#include - -#include -#include - -#include "SLACommon.hpp" -#include "SLASpatIndex.hpp" - -namespace Slic3r { - -typedef Eigen::Matrix Vec4i; - -namespace sla { - -} -} - -#endif // SLABOILERPLATE_HPP diff --git a/src/libslic3r/SLA/SLABoostAdapter.hpp b/src/libslic3r/SLA/SLABoostAdapter.hpp index 1e9daf4613..5147929eb8 100644 --- a/src/libslic3r/SLA/SLABoostAdapter.hpp +++ b/src/libslic3r/SLA/SLABoostAdapter.hpp @@ -1,7 +1,7 @@ #ifndef SLABOOSTADAPTER_HPP #define SLABOOSTADAPTER_HPP -#include "SLA/SLABoilerPlate.hpp" +#include "SLA/SLACommon.hpp" #include namespace boost { diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 7cdc626629..634c5611f4 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include "SLASpatIndex.hpp" diff --git a/src/libslic3r/SLA/SLAPad.cpp b/src/libslic3r/SLA/SLAPad.cpp index 264cfba9ff..9e5c306d1e 100644 --- a/src/libslic3r/SLA/SLAPad.cpp +++ b/src/libslic3r/SLA/SLAPad.cpp @@ -1,5 +1,5 @@ #include "SLAPad.hpp" -#include "SLABoilerPlate.hpp" +#include "SLACommon.hpp" #include "SLASpatIndex.hpp" #include "ConcaveHull.hpp" diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index 2e64059d68..5938469ed2 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -2,7 +2,7 @@ #include #include -#include "SLABoilerPlate.hpp" +#include "SLACommon.hpp" #include "SLARotfinder.hpp" #include "SLASupportTree.hpp" #include "Model.hpp" diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index fea8bf731c..219c4b6588 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -5,7 +5,7 @@ #include #include "SLASupportTree.hpp" -#include "SLABoilerPlate.hpp" +#include "SLACommon.hpp" #include "SLASpatIndex.hpp" #include "SLASupportTreeBuilder.hpp" diff --git a/src/libslic3r/SLA/SLASupportTreeBuilder.hpp b/src/libslic3r/SLA/SLASupportTreeBuilder.hpp index c0d9f04c0f..fec553fe82 100644 --- a/src/libslic3r/SLA/SLASupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SLASupportTreeBuilder.hpp @@ -2,7 +2,7 @@ #define SUPPORTTREEBUILDER_HPP #include "SLAConcurrency.hpp" -#include "SLABoilerPlate.hpp" +#include "SLACommon.hpp" #include "SLASupportTree.hpp" #include "SLAPad.hpp" #include diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 5e10c28c91..03d4a563c0 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -1,6 +1,6 @@ #include #include "SLA/SLASupportTree.hpp" -#include "SLA/SLABoilerPlate.hpp" +#include "SLA/SLACommon.hpp" #include "SLA/SLASpatIndex.hpp" // Workaround: IGL signed_distance.h will define PI in the igl namespace. diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 2a1ae74d74..8c03853e3d 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -52,7 +52,9 @@ namespace { // should add up to 100 (%) const std::array OBJ_STEP_LEVELS = { - 30, // slaposObjectSlice, + 5, // slaposHollowing, + 20, // slaposObjectSlice, + 5, // slaposDrillHolesIfHollowed 20, // slaposSupportPoints, 10, // slaposSupportTree, 10, // slaposPad, @@ -63,14 +65,17 @@ const std::array OBJ_STEP_LEVELS = std::string OBJ_STEP_LABELS(size_t idx) { switch (idx) { - case slaposObjectSlice: return L("Slicing model"); - case slaposSupportPoints: return L("Generating support points"); - case slaposSupportTree: return L("Generating support tree"); - case slaposPad: return L("Generating pad"); - case slaposSliceSupports: return L("Slicing supports"); + case slaposHollowing: return L("Hollowing out the model"); + case slaposObjectSlice: return L("Slicing model"); + case slaposDrillHolesIfHollowed: return L("Drilling holes into hollowed model."); + case slaposSupportPoints: return L("Generating support points"); + case slaposSupportTree: return L("Generating support tree"); + case slaposPad: return L("Generating pad"); + case slaposSliceSupports: return L("Slicing supports"); default:; } - assert(false); return "Out of bounds!"; + assert(false); + return "Out of bounds!"; }; // Should also add up to 100 (%) @@ -1460,7 +1465,7 @@ void SLAPrint::process() slaposFn pobj_program[] = { - slice_model, support_points, support_tree, generate_pad, slice_supports + [](SLAPrintObject&){}, slice_model, [](SLAPrintObject&){}, support_points, support_tree, generate_pad, slice_supports }; // We want to first process all objects... @@ -1760,8 +1765,14 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); // propagate to dependent steps - if (step == slaposObjectSlice) { + if (step == slaposHollowing) { invalidated |= this->invalidate_all_steps(); + } else if (step == slaposObjectSlice) { + invalidated |= this->invalidate_steps({ slaposDrillHolesIfHollowed, slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); + } else if (step == slaposDrillHolesIfHollowed) { + invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposPad, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSupportPoints) { invalidated |= this->invalidate_steps({ slaposSupportTree, slaposPad, slaposSliceSupports }); invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 2dc2a9040a..38e3737754 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -18,7 +18,9 @@ enum SLAPrintStep : unsigned int { }; enum SLAPrintObjectStep : unsigned int { + slaposHollowing, slaposObjectSlice, + slaposDrillHolesIfHollowed, slaposSupportPoints, slaposSupportTree, slaposPad, diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index b74eb76aba..4b7465a3da 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -3,8 +3,11 @@ #include #include "libslic3r/OpenVDBUtils.hpp" +#include #include "libslic3r/Format/OBJ.hpp" +#include + #if defined(WIN32) || defined(_WIN32) #define PATH_SEPARATOR R"(\)" #else @@ -19,60 +22,39 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -static bool _check_normals(const Slic3r::sla::Contour3D &mesh) -{ - for (auto & face : mesh.faces3) - { - - } - - return false; -} - -TEST_CASE("Passing OpenVDB grid conversion produce similar geometry.", "[Hollowing]") +TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") { Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); + in_mesh.scale(3.); Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{in_mesh}; - auto ptr = Slic3r::meshToVolume(imesh, {}); + + Benchmark bench; + bench.start(); + + openvdb::math::Transform tr; + auto ptr = Slic3r::meshToVolume(imesh, {}, 0.0f, 10.0f); REQUIRE(ptr); - std::cout << "Grid class = " << ptr->getGridClass() << std::endl; + openvdb::tools::Filter{*ptr}.gaussian(1, 3); - Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -2.9, 1.0, true); - std::cout << "Triangle count: " << omesh.faces3.size() << std::endl; - std::cout << "Quad count: " << omesh.faces4.size() << std::endl; + double iso_surface = -3.0; + double adaptivity = 0.5; + Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, iso_surface, adaptivity); REQUIRE(!omesh.empty()); - SECTION("Converting to Contour3D to TriangleMesh") { - Slic3r::TriangleMesh msh = Slic3r::sla::to_triangle_mesh(omesh); - - msh.require_shared_vertices(); - msh.WriteOBJFile("out_tr.obj"); - - REQUIRE(msh.volume() == Approx(in_mesh.volume())); - } + imesh.merge(omesh); -// omesh.faces4.clear(); - std::fstream outfile{"out.obj", std::ios::out}; + for (auto &p : imesh.points) p /= 3.; + + bench.stop(); + + std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl; + std::fstream merged_outfile("merged_out.obj", std::ios::out); + imesh.to_obj(merged_outfile); + + std::fstream outfile("out.obj", std::ios::out); omesh.to_obj(outfile); - } - -//TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") -//{ -// Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{load_model("20mm_cube.obj")}; -// auto ptr = Slic3r::meshToVolume(imesh, {}); - -// REQUIRE(ptr); - -// Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, -1., 0.0, true); - -// REQUIRE(!omesh.empty()); - -// imesh.merge(omesh); -// std::fstream merged_outfile("merged_out.obj", std::ios::out); -// imesh.to_obj(merged_outfile); -//} From f913be2d93ff69c0fdf2f6db36cb735ed7f48fb3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 5 Nov 2019 17:00:11 +0100 Subject: [PATCH 023/130] Streamlined hollowing method. --- tests/libslic3r/test_hollowing.cpp | 43 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 4b7465a3da..8f57828a10 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -22,39 +22,46 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +static Slic3r::TriangleMesh hollowed_interior(const Slic3r::TriangleMesh &mesh, + double min_thickness) { - Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); - in_mesh.scale(3.); - Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{in_mesh}; + Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{mesh}; - Benchmark bench; - bench.start(); + double scale = std::max(1.0, 3. / min_thickness); + double offset = scale * min_thickness; + float range = float(std::max(2 * offset, scale)); - openvdb::math::Transform tr; - auto ptr = Slic3r::meshToVolume(imesh, {}, 0.0f, 10.0f); + for (auto &p : imesh.points) p *= scale; + auto ptr = Slic3r::meshToVolume(imesh, {}, 0.1f * float(offset), range); REQUIRE(ptr); - openvdb::tools::Filter{*ptr}.gaussian(1, 3); + openvdb::tools::Filter{*ptr}.gaussian(int(scale), 1); - - double iso_surface = -3.0; - double adaptivity = 0.5; + double iso_surface = -offset; + double adaptivity = 0.; Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, iso_surface, adaptivity); REQUIRE(!omesh.empty()); - imesh.merge(omesh); + for (auto &p : omesh.points) p /= scale; - for (auto &p : imesh.points) p /= 3.; + return to_triangle_mesh(omesh); +} + +TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") +{ + Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); + Benchmark bench; + bench.start(); + + Slic3r::TriangleMesh out_mesh = hollowed_interior(in_mesh, 2); bench.stop(); std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl; - std::fstream merged_outfile("merged_out.obj", std::ios::out); - imesh.to_obj(merged_outfile); - std::fstream outfile("out.obj", std::ios::out); - omesh.to_obj(outfile); + in_mesh.merge(out_mesh); + in_mesh.require_shared_vertices(); + in_mesh.WriteOBJFile("merged_out.obj"); } From bdf6f7342ef539a3f198aa0446311b3add9bd820 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 6 Nov 2019 13:38:43 +0100 Subject: [PATCH 024/130] Hollowing step in SLAPrint process, PrintConfig params added. --- resources/icons/hollowing.svg | 25 +++++ resources/icons/white/hollowing.svg | 25 +++++ src/libslic3r/Model.cpp | 7 +- src/libslic3r/OpenVDBUtils.cpp | 122 ++++++++++++++++++++---- src/libslic3r/OpenVDBUtils.hpp | 23 +++-- src/libslic3r/PrintConfig.cpp | 16 ++++ src/libslic3r/PrintConfig.hpp | 22 ++++- src/libslic3r/SLAPrint.cpp | 56 ++++++++++- src/libslic3r/SLAPrint.hpp | 10 +- src/slic3r/GUI/GUI_ObjectList.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 22 +++-- src/slic3r/GUI/Preset.cpp | 2 + src/slic3r/GUI/Tab.cpp | 5 + tests/libslic3r/test_hollowing.cpp | 40 ++++---- 14 files changed, 311 insertions(+), 65 deletions(-) create mode 100644 resources/icons/hollowing.svg create mode 100644 resources/icons/white/hollowing.svg diff --git a/resources/icons/hollowing.svg b/resources/icons/hollowing.svg new file mode 100644 index 0000000000..65c7592c83 --- /dev/null +++ b/resources/icons/hollowing.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/white/hollowing.svg b/resources/icons/white/hollowing.svg new file mode 100644 index 0000000000..65c7592c83 --- /dev/null +++ b/resources/icons/white/hollowing.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 061c5bd50a..607f11bc65 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1142,7 +1142,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper) { upper->add_volume(*volume); } if (keep_lower) { lower->add_volume(*volume); } } - else { + else if (! volume->mesh().empty()) { + TriangleMesh upper_mesh, lower_mesh; // Transform the mesh by the combined transformation matrix. @@ -1150,7 +1151,9 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b TriangleMesh mesh(volume->mesh()); mesh.transform(instance_matrix * volume_matrix, true); volume->reset_mesh(); - + + mesh.require_shared_vertices(); + // Perform cut TriangleMeshSlicer tms(&mesh); tms.cut(float(z), &upper_mesh, &lower_mesh); diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 97a57315b6..08c69bdb5d 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -2,6 +2,9 @@ #include "OpenVDBUtils.hpp" #include #include +#include +#include +#include "MTUtils.hpp" namespace Slic3r { @@ -50,6 +53,11 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n, pos = {p.x(), p.y(), p.z()}; } + +// TODO: Do I need to call initialize? Seems to work without it as well but the +// docs say it should be called ones. It does a mutex lock-unlock sequence all +// even if was called previously. + openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, const openvdb::math::Transform &tr, float exteriorBandWidth, @@ -62,11 +70,7 @@ openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, interiorBandWidth, flags); } -// TODO: Do I need to call initialize? Seems to work without it as well but the -// docs say it should be called ones. It does a mutex lock-unlock sequence all -// even if was called previously. - -openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, +static openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D &mesh, const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, @@ -84,10 +88,10 @@ inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1 inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } template -sla::Contour3D _volumeToMesh(const Grid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +sla::Contour3D __volumeToMesh(const Grid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { openvdb::initialize(); @@ -102,20 +106,106 @@ sla::Contour3D _volumeToMesh(const Grid &grid, ret.points.reserve(points.size()); ret.faces3.reserve(triangles.size()); ret.faces4.reserve(quads.size()); - + for (auto &v : points) ret.points.emplace_back(to_vec3d(v)); for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); - + return ret; } -sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +template inline +Mesh _volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue = 0.0, + double adaptivity = 0.0, + bool relaxDisorientedTriangles = true); + +template<> inline +TriangleMesh _volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { - return _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles); + return to_triangle_mesh(__volumeToMesh(grid, isovalue, adaptivity, + relaxDisorientedTriangles)); +} + +template<> inline +sla::Contour3D _volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + return __volumeToMesh(grid, isovalue, adaptivity, + relaxDisorientedTriangles); +} + +TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) +{ + return _volumeToMesh(grid, isovalue, adaptivity, + relaxDisorientedTriangles); +} + +template> +inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } + +template> +inline void _scale(S s, sla::Contour3D &m) +{ + for (auto &p : m.points) p *= s; +} + +template +remove_cvref_t _hollowed_interior(Mesh &&mesh, + double min_thickness, + int oversampling, + double smoothing) +{ + using MMesh = remove_cvref_t; + MMesh imesh{std::forward(mesh)}; + + // I can't figure out how to increase the grid resolution through openvdb API + // so the model will be scaled up before conversion and the result scaled + // down. Voxels have a unit size. If I set voxelSize smaller, it scales + // the whole geometry down, and doesn't increase the number of voxels. + auto scale = double(oversampling); + + _scale(scale, imesh); + + double offset = scale * min_thickness; + float range = float(std::max(2 * offset, scale)); + auto gridptr = meshToVolume(imesh, {}, 0.1f * float(offset), range); + + assert(gridptr); + + if (!gridptr) { + BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; + return MMesh{}; + } + + // Filtering: + int width = int(smoothing * scale); + int count = 1; + openvdb::tools::Filter{*gridptr}.gaussian(width, count); + + double iso_surface = -offset; + double adaptivity = 0.; + auto omesh = _volumeToMesh(*gridptr, iso_surface, adaptivity); + + _scale(1. / scale, omesh); + + return omesh; +} + +TriangleMesh hollowed_interior(const TriangleMesh &mesh, + double min_thickness, + int oversampling, + double smoothing) +{ + return _hollowed_interior(mesh, min_thickness, oversampling, smoothing); } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index eae1e051dc..8e55300bd2 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -13,16 +13,21 @@ openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, float interiorBandWidth = 3.0f, int flags = 0); -openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D & mesh, - const openvdb::math::Transform &tr = {}, - float exteriorBandWidth = 3.0f, - float interiorBandWidth = 3.0f, - int flags = 0); +TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, + double isovalue = 0.0, + double adaptivity = 0.0, + bool relaxDisorientedTriangles = true); -sla::Contour3D volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue = 0.0, - double adaptivity = 0.0, - bool relaxDisorientedTriangles = true); +sla::Contour3D hollowed_interior(sla::Contour3D&& mesh, double min_thickness); +sla::Contour3D hollowed_interior(sla::Contour3D& mesh, double min_thickness); + +// Generate an interior for any solid geometry maintaining a given minimum +// wall thickness. The returned mesh has triangles with normals facing inside +// the mesh so the result can be directly merged with the input to finish the +// hollowing. +// TODO: The thicknes is not strictly maintained due to the used gaussian filter +TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, + int oversampling = 3, double smoothing = 0.5); } // namespace Slic3r diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e639a6734d..4cfd81bc55 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2830,6 +2830,22 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.3)); + + def = this->add("hollowing_enable", coBool); + def->label = L("Enable hollowing"); + def->category = L("Hollowing"); + def->tooltip = L("Hollow out a model to have an empty interior"); + def->mode = comSimple; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("hollowing_min_thickness", coFloat); + def->label = L("Hollowing thickness"); + def->category = L("Hollowing"); + def->tooltip = L("Minimum wall thickness of a hollowed model."); + def->sidetext = L("mm"); + def->min = 1; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloat(4)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4b007fc51c..6bcc3f9ad2 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1014,7 +1014,7 @@ public: ConfigOptionFloat support_base_height /*= 1.0*/; // The minimum distance of the pillar base from the model in mm. - ConfigOptionFloat support_base_safety_distance; /*= 1.0*/; + ConfigOptionFloat support_base_safety_distance; /*= 1.0*/ // The default angle for connecting support sticks and junctions. ConfigOptionFloat support_critical_angle /*= 45*/; @@ -1059,7 +1059,7 @@ public: // ///////////////////////////////////////////////////////////////////////// // Zero elevation mode parameters: - // - The object pad will be derived from the the model geometry. + // - The object pad will be derived from the model geometry. // - There will be a gap between the object pad and the generated pad // according to the support_base_safety_distance parameter. // - The two pads will be connected with tiny connector sticks @@ -1081,6 +1081,22 @@ public: // How much should the tiny connectors penetrate into the model body ConfigOptionFloat pad_object_connector_penetration; + + // ///////////////////////////////////////////////////////////////////////// + // Model hollowing parameters: + // - Models can be hollowed out as part of the SLA print process + // - Thickness of the hollowed model walls can be adjusted + // - + // - Additional holes will be drilled into the hollow model to allow for + // - resin removal. + // ///////////////////////////////////////////////////////////////////////// + + ConfigOptionBool hollowing_enable; + + // The minimum thickness of the model walls to maintain. Note that the + // resulting walls may be thicker due to smoothing out fine cavities where + // resin could stuck. + ConfigOptionFloat hollowing_min_thickness; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -1118,6 +1134,8 @@ protected: OPT_PTR(pad_object_connector_stride); OPT_PTR(pad_object_connector_width); OPT_PTR(pad_object_connector_penetration); + OPT_PTR(hollowing_enable); + OPT_PTR(hollowing_min_thickness); } }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 8c03853e3d..0f5cb20b8c 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -3,6 +3,7 @@ #include "SLA/SLAPad.hpp" #include "SLA/SLAAutoSupports.hpp" #include "ClipperUtils.hpp" +#include "OpenVDBUtils.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" @@ -47,6 +48,14 @@ public: } }; +class SLAPrintObject::HollowingData +{ +public: + + TriangleMesh interior; + // std::vector +}; + namespace { // should add up to 100 (%) @@ -752,6 +761,28 @@ void SLAPrint::process() // 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 const double ostepd = (max_objstatus - min_objstatus) / (objcount * 100.0); + + auto hollow_model = [](SLAPrintObject &po) { + + if (!po.m_config.hollowing_enable.getBool()) { + BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; + po.m_hollowing_data.reset(); + return; + } else { + BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; + } + + if (!po.m_hollowing_data) + po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + + double thickness = po.m_config.hollowing_min_thickness.getFloat(); + + po.m_hollowing_data->interior = + hollowed_interior(po.transformed_mesh(), thickness, 4, 0.5); + + if (po.m_hollowing_data->interior.empty()) + BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; + }; // The slicing will be performed on an imaginary 1D grid which starts from // the bottom of the bounding box created around the supported model. So @@ -765,7 +796,20 @@ 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](SLAPrintObject& po) { - const TriangleMesh& mesh = po.transformed_mesh(); + + TriangleMesh hollowed_mesh; + + bool is_hollowing = po.m_config.hollowing_enable.getBool() && + po.m_hollowing_data; + + if (is_hollowing) { + hollowed_mesh = po.transformed_mesh(); + hollowed_mesh.merge(po.m_hollowing_data->interior); + hollowed_mesh.require_shared_vertices(); + } + + const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : + po.transformed_mesh(); // We need to prepare the slice index... @@ -1465,12 +1509,12 @@ void SLAPrint::process() slaposFn pobj_program[] = { - [](SLAPrintObject&){}, slice_model, [](SLAPrintObject&){}, support_points, support_tree, generate_pad, slice_supports + hollow_model, slice_model, [](SLAPrintObject&){}, support_points, support_tree, generate_pad, slice_supports }; // We want to first process all objects... std::vector level1_obj_steps = { - slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad + slaposHollowing, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposPad }; // and then slice all supports to allow preview to be displayed ASAP @@ -1707,7 +1751,11 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector steps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if ( opt_key == "layer_height" + if ( opt_key == "hollowing_enable" + || opt_key == "hollowing_min_thickness") { + steps.emplace_back(slaposHollowing); + } else if ( + opt_key == "layer_height" || opt_key == "faded_layers" || opt_key == "pad_enable" || opt_key == "pad_wall_thickness" diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 38e3737754..1b8b126e23 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -74,8 +74,11 @@ public: // Support mesh is only valid if this->is_step_done(slaposSupportTree) is true. const TriangleMesh& support_mesh() const; // Get a pad mesh centered around origin in XY, and with zero rotation around Z applied. - // Support mesh is only valid if this->is_step_done(slaposBasePool) is true. + // Support mesh is only valid if this->is_step_done(slaposPad) is true. const TriangleMesh& pad_mesh() const; + + // Ready after this->is_step_done(slaposHollowing) is true + const TriangleMesh& hollowed_interior_mesh() const; // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; @@ -288,9 +291,12 @@ private: // Caching the transformed (m_trafo) raw mesh of the object mutable CachedObject m_transformed_rmesh; - + class SupportData; std::unique_ptr m_supportdata; + + class HollowingData; + std::unique_ptr m_hollowing_data; }; using PrintObjects = std::vector; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a88980a8d4..45d55c0b94 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -98,6 +98,7 @@ ObjectList::ObjectList(wxWindow* parent) : // ptSLA CATEGORY_ICON[L("Supports")] = create_scaled_bitmap(nullptr, "support"/*"sla_supports"*/); CATEGORY_ICON[L("Pad")] = create_scaled_bitmap(nullptr, "pad"); + CATEGORY_ICON[L("Hollowing")] = create_scaled_bitmap(nullptr, "hollowing"); } // create control diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 5b6620ef68..34173366d2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -597,16 +597,20 @@ void GLGizmoHollow::on_update(const UpdateData& data) void GLGizmoHollow::hollow_mesh(float offset, float adaptibility) { - Slic3r::sla::Contour3D imesh{*m_mesh}; - auto ptr = meshToVolume(imesh, {}); - sla::Contour3D omesh = volumeToMesh(*ptr, -offset, adaptibility, true); +// Slic3r::sla::Contour3D imesh{*m_mesh}; +// auto ptr = meshToVolume(imesh, {}); +// sla::Contour3D omesh = volumeToMesh(*ptr, -offset, adaptibility, true); - if (omesh.empty()) +// if (omesh.empty()) +// return; + +// imesh.merge(omesh); + TriangleMesh cavity = hollowed_interior(*m_mesh, double(offset), int(adaptibility)); + if (cavity.empty()) return; - - imesh.merge(omesh); - m_cavity_mesh.reset(new TriangleMesh); - *m_cavity_mesh = sla::to_triangle_mesh(imesh); + + m_cavity_mesh.reset(new TriangleMesh(cavity)); + m_cavity_mesh->merge(*m_mesh); m_cavity_mesh.get()->require_shared_vertices(); m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); m_object_clipper.reset(); @@ -616,7 +620,7 @@ void GLGizmoHollow::hollow_mesh(float offset, float adaptibility) m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); m_volume_with_cavity->finalize_geometry(true); m_volume_with_cavity->set_volume_transformation(m_model_object->volumes.front()->get_transformation()); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); m_parent.toggle_model_objects_visibility(false, m_model_object, m_active_instance); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index a360aaf1a0..edbb5fb750 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -496,6 +496,8 @@ const std::vector& Preset::sla_print_options() "pad_object_connector_stride", "pad_object_connector_width", "pad_object_connector_penetration", + "hollowing_enable", + "hollowing_min_thickness", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c2a258e69b..22c4bfb9bf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3563,6 +3563,11 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_object_connector_stride"); optgroup->append_single_option_line("pad_object_connector_width"); optgroup->append_single_option_line("pad_object_connector_penetration"); + + page = add_options_page(_(L("Hollowing")), "hollowing"); + optgroup = page->new_optgroup(_(L("Hollowing"))); + optgroup->append_single_option_line("hollowing_enable"); + optgroup->append_single_option_line("hollowing_min_thickness"); page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 8f57828a10..b463fd863e 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -22,40 +22,38 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -static Slic3r::TriangleMesh hollowed_interior(const Slic3r::TriangleMesh &mesh, - double min_thickness) -{ - Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{mesh}; +//static Slic3r::TriangleMesh hollowed_interior(const Slic3r::TriangleMesh &mesh, +// double min_thickness) +//{ +// Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{mesh}; - double scale = std::max(1.0, 3. / min_thickness); - double offset = scale * min_thickness; - float range = float(std::max(2 * offset, scale)); +// double scale = std::max(1.0, 3. / min_thickness); +// double offset = scale * min_thickness; +// float range = float(std::max(2 * offset, scale)); - for (auto &p : imesh.points) p *= scale; - auto ptr = Slic3r::meshToVolume(imesh, {}, 0.1f * float(offset), range); +// for (auto &p : imesh.points) p *= scale; +// auto ptr = Slic3r::meshToVolume(imesh, {}, 0.1f * float(offset), range); - REQUIRE(ptr); +// REQUIRE(ptr); - openvdb::tools::Filter{*ptr}.gaussian(int(scale), 1); +// openvdb::tools::Filter{*ptr}.gaussian(int(scale), 1); - double iso_surface = -offset; - double adaptivity = 0.; - Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, iso_surface, adaptivity); +// double iso_surface = -offset; +// double adaptivity = 0.; +// Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, iso_surface, adaptivity); - REQUIRE(!omesh.empty()); +// for (auto &p : omesh.points) p /= scale; - for (auto &p : omesh.points) p /= scale; - - return to_triangle_mesh(omesh); -} +// return to_triangle_mesh(omesh); +//} TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") { - Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); + Slic3r::TriangleMesh in_mesh = load_model("zaba.obj"); Benchmark bench; bench.start(); - Slic3r::TriangleMesh out_mesh = hollowed_interior(in_mesh, 2); + Slic3r::TriangleMesh out_mesh = hollowed_interior(in_mesh, 0.5); bench.stop(); From a82f1268f328b3c38c049896cadde2a551dc2871 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 6 Nov 2019 14:25:03 +0100 Subject: [PATCH 025/130] Hollowed interior is now visible in preview --- src/libslic3r/SLAPrint.cpp | 8 ++++++++ src/slic3r/GUI/GLCanvas3D.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 17 +++-------------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 3 +-- tests/libslic3r/test_hollowing.cpp | 2 +- 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0f5cb20b8c..2e8a704005 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1975,6 +1975,14 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const return EMPTY_MESH; } +const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const +{ + if (m_hollowing_data && m_config.hollowing_enable.getBool()) + return m_hollowing_data->interior; + + return EMPTY_MESH; +} + const TriangleMesh &SLAPrintObject::transformed_mesh() const { // we need to transform the raw mesh... // currently all the instances share the same x and y rotation and scaling diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 268b4dc30b..52d1df3179 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5362,6 +5362,8 @@ void GLCanvas3D::_load_sla_shells() unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { add_volume(*obj, 0, instance, obj->transformed_mesh(), GLVolume::MODEL_COLOR[0], true); + if (! obj->hollowed_interior_mesh().empty()) + add_volume(*obj, -int(slaposHollowing), instance, obj->hollowed_interior_mesh(), GLVolume::MODEL_COLOR[0], false); // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 34173366d2..2c3620dd7b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -595,17 +595,9 @@ void GLGizmoHollow::on_update(const UpdateData& data) } -void GLGizmoHollow::hollow_mesh(float offset, float adaptibility) +void GLGizmoHollow::hollow_mesh(float offset) { -// Slic3r::sla::Contour3D imesh{*m_mesh}; -// auto ptr = meshToVolume(imesh, {}); -// sla::Contour3D omesh = volumeToMesh(*ptr, -offset, adaptibility, true); - -// if (omesh.empty()) -// return; - -// imesh.merge(omesh); - TriangleMesh cavity = hollowed_interior(*m_mesh, double(offset), int(adaptibility)); + TriangleMesh cavity = hollowed_interior(*m_mesh, double(offset)); if (cavity.empty()) return; @@ -700,7 +692,7 @@ RENDER_AGAIN: if (m_editing_mode) { if (m_imgui->button(m_desc.at("hollow"))) { - hollow_mesh(m_offset, m_adaptibility); + hollow_mesh(m_offset); } float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; @@ -762,9 +754,6 @@ RENDER_AGAIN: m_imgui->text("Offset: "); ImGui::SameLine(); ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); - m_imgui->text("Adaptibility: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_adaptibility, 0.f, 1.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 82455a9f40..ba950594a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -92,7 +92,7 @@ private: void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); - void hollow_mesh(float offset = 2.f, float adaptability = 1.f); + void hollow_mesh(float offset = 2.f); bool unsaved_changes() const; const TriangleMesh* mesh() const; @@ -109,7 +109,6 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo float m_offset = 2.f; - float m_adaptibility = 1.f; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index b463fd863e..9a2ea2e726 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -49,7 +49,7 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") { - Slic3r::TriangleMesh in_mesh = load_model("zaba.obj"); + Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); Benchmark bench; bench.start(); From bc3d22348aa6ee03c86aeab93dbdd1d2da7694b9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 7 Nov 2019 09:34:34 +0100 Subject: [PATCH 026/130] Hollowing params: accuracy and smoothness --- src/libslic3r/OpenVDBUtils.cpp | 28 ++++++++++++++++++------- src/libslic3r/OpenVDBUtils.hpp | 5 +---- src/libslic3r/PrintConfig.cpp | 19 +++++++++++++++++ src/libslic3r/PrintConfig.hpp | 9 ++++++++ src/libslic3r/SLAPrint.cpp | 10 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 24 +++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 9 ++++++-- src/slic3r/GUI/Preset.cpp | 2 ++ src/slic3r/GUI/Tab.cpp | 2 ++ 9 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 08c69bdb5d..53f9d5ae9f 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -158,20 +158,35 @@ inline void _scale(S s, sla::Contour3D &m) for (auto &p : m.points) p *= s; } +static void filter_grid(openvdb::FloatGrid &grid, double scale, double gain) +{ + static const double ROUNDNESS_COEFF = 1.; + + // Filtering: + if (gain > 0.) { + double rounding = ROUNDNESS_COEFF * gain; + int width = int(rounding * scale); + int count = 1; + openvdb::tools::Filter{grid}.gaussian(width, count); + } +} + template remove_cvref_t _hollowed_interior(Mesh &&mesh, double min_thickness, - int oversampling, + double accuracy, double smoothing) { using MMesh = remove_cvref_t; MMesh imesh{std::forward(mesh)}; + static const double ACCURACY_COEFF = 7.; + // I can't figure out how to increase the grid resolution through openvdb API // so the model will be scaled up before conversion and the result scaled // down. Voxels have a unit size. If I set voxelSize smaller, it scales // the whole geometry down, and doesn't increase the number of voxels. - auto scale = double(oversampling); + auto scale = (1.0 + ACCURACY_COEFF * accuracy); // max 8x upscale, min is native voxel size _scale(scale, imesh); @@ -186,10 +201,7 @@ remove_cvref_t _hollowed_interior(Mesh &&mesh, return MMesh{}; } - // Filtering: - int width = int(smoothing * scale); - int count = 1; - openvdb::tools::Filter{*gridptr}.gaussian(width, count); + filter_grid(*gridptr, scale, smoothing); double iso_surface = -offset; double adaptivity = 0.; @@ -202,10 +214,10 @@ remove_cvref_t _hollowed_interior(Mesh &&mesh, TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, - int oversampling, + double accuracy, double smoothing) { - return _hollowed_interior(mesh, min_thickness, oversampling, smoothing); + return _hollowed_interior(mesh, min_thickness, accuracy, smoothing); } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index 8e55300bd2..95b95db7ce 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -18,16 +18,13 @@ TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, double adaptivity = 0.0, bool relaxDisorientedTriangles = true); -sla::Contour3D hollowed_interior(sla::Contour3D&& mesh, double min_thickness); -sla::Contour3D hollowed_interior(sla::Contour3D& mesh, double min_thickness); - // Generate an interior for any solid geometry maintaining a given minimum // wall thickness. The returned mesh has triangles with normals facing inside // the mesh so the result can be directly merged with the input to finish the // hollowing. // TODO: The thicknes is not strictly maintained due to the used gaussian filter TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, - int oversampling = 3, double smoothing = 0.5); + double accuracy = 0.5, double smoothing = 0.5); } // namespace Slic3r diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4cfd81bc55..e621d4b340 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2846,6 +2846,25 @@ void PrintConfigDef::init_sla_params() def->min = 1; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(4)); + + def = this->add("hollowing_accuracy", coFloat); + def->label = L("Hollowing accuracy"); + def->category = L("Hollowing"); + def->tooltip = L("Performance vs accuracy of calculation. Lower values may produce unwanted artifacts."); + def->min = 0; + def->max = 1; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.5)); + + def = this->add("hollowing_smoothness", coFloat); + def->label = L("Hollowing smoothness"); + def->category = L("Hollowing"); + def->tooltip = L("The cavity shape is a smoothed version of the outside original shape. " + "Possible values span from 0 to 1 and control the amount of surface smoothing."); + def->min = 0; + def->max = 1; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.5)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6bcc3f9ad2..d07999c886 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1097,6 +1097,13 @@ public: // resulting walls may be thicker due to smoothing out fine cavities where // resin could stuck. ConfigOptionFloat hollowing_min_thickness; + + // Indirectly controls the voxel size (resolution) used by openvdb + ConfigOptionFloat hollowing_accuracy; + + // Indirectly controls the amount of filtering used to blur geometry + // features in the created cavity. + ConfigOptionFloat hollowing_smoothness; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -1136,6 +1143,8 @@ protected: OPT_PTR(pad_object_connector_penetration); OPT_PTR(hollowing_enable); OPT_PTR(hollowing_min_thickness); + OPT_PTR(hollowing_accuracy); + OPT_PTR(hollowing_smoothness); } }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 2e8a704005..d2200bcfb2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -776,9 +776,10 @@ void SLAPrint::process() po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); double thickness = po.m_config.hollowing_min_thickness.getFloat(); - + double accuracy = po.m_config.hollowing_accuracy.getFloat(); + double blur = po.m_config.hollowing_smoothness.getFloat(); po.m_hollowing_data->interior = - hollowed_interior(po.transformed_mesh(), thickness, 4, 0.5); + hollowed_interior(po.transformed_mesh(), thickness, accuracy, blur); if (po.m_hollowing_data->interior.empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; @@ -1752,7 +1753,10 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectormerge(*m_mesh); m_cavity_mesh.get()->require_shared_vertices(); @@ -692,7 +694,7 @@ RENDER_AGAIN: if (m_editing_mode) { if (m_imgui->button(m_desc.at("hollow"))) { - hollow_mesh(m_offset); + hollow_mesh(); } float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; @@ -748,12 +750,22 @@ RENDER_AGAIN: remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); - m_imgui->text(" "); // vertical gap +// m_imgui->text(" "); // vertical gap m_imgui->text("Offset: "); ImGui::SameLine(); ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); + + // TODO: only in expert mode: + m_imgui->text("Accuracy: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_accuracy, 0.f, 1.f, "%.1f"); + + // TODO: only in expert mode: + m_imgui->text("Smoothness: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_smoothness, 0.f, 1.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index ba950594a2..0cf1e1ecb1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -92,7 +92,7 @@ private: void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); - void hollow_mesh(float offset = 2.f); + void hollow_mesh(); bool unsaved_changes() const; const TriangleMesh* mesh() const; @@ -108,14 +108,19 @@ private: mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo - float m_offset = 2.f; + float m_offset = 2.0f; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; + + float m_accuracy = 0.5f; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; + + + float m_smoothness = 0.5f; GLSelectionRectangle m_selection_rectangle; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index edbb5fb750..483bff8e55 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -498,6 +498,8 @@ const std::vector& Preset::sla_print_options() "pad_object_connector_penetration", "hollowing_enable", "hollowing_min_thickness", + "hollowing_accuracy", + "hollowing_smoothness", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 22c4bfb9bf..d20e0ee6c8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3568,6 +3568,8 @@ void TabSLAPrint::build() optgroup = page->new_optgroup(_(L("Hollowing"))); optgroup->append_single_option_line("hollowing_enable"); optgroup->append_single_option_line("hollowing_min_thickness"); + optgroup->append_single_option_line("hollowing_accuracy"); + optgroup->append_single_option_line("hollowing_smoothness"); page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); From 9836533cb3fc86812619e118af825a23b861fa88 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 7 Nov 2019 14:25:03 +0100 Subject: [PATCH 027/130] Hollowing task triggered by the gizmo now spawns a UI job to not block the UI thread The AABB tree calculation is still done in the UI thread, so it gets blocked for some time --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 120 ++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 7 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 3 +- src/slic3r/GUI/Plater.cpp | 66 +++++++++++- src/slic3r/GUI/Plater.hpp | 1 + 5 files changed, 125 insertions(+), 72 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 5b6620ef68..f0aaca9ade 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -5,19 +5,13 @@ #include -//#include -//#include -//#include - #include "slic3r/GUI/GUI_App.hpp" -#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/MeshUtils.hpp" #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" -#include "libslic3r/OpenVDBUtils.hpp" namespace Slic3r { @@ -26,7 +20,6 @@ namespace GUI { GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) , m_quadric(nullptr) - , m_its(nullptr) { m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_quadric = ::gluNewQuadric(); @@ -48,8 +41,8 @@ bool GLGizmoHollow::on_init() m_desc["head_diameter"] = _(L("Head diameter")) + ": "; m_desc["lock_supports"] = _(L("Lock supports under new islands")); - m_desc["remove_selected"] = _(L("Remove selected points")); - m_desc["remove_all"] = _(L("Remove all points")); + m_desc["remove_selected"] = _(L("Remove selected holes")); + m_desc["remove_all"] = _(L("Remove all holes")); m_desc["apply_changes"] = _(L("Apply changes")); m_desc["discard_changes"] = _(L("Discard changes")); m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; @@ -113,7 +106,7 @@ void GLGizmoHollow::on_render() const return; } - if (! m_its || ! m_mesh) + if (! m_mesh) const_cast(this)->update_mesh(); if (m_volume_with_cavity) { @@ -212,7 +205,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const ::glPopMatrix(); } - if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { + if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty()) { // The supports are hidden in the editing mode, so it makes no sense to render the cuts. ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); @@ -310,12 +303,14 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons //const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.); const double cone_rad_diff = m_new_cone_angle*cone_radius; glsafe(::glPushMatrix()); - glsafe(::glTranslatef(0.f, 0.f, -cone_height/2.)); + glsafe(::glTranslated(0., 0., -cone_height/2.)); //::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1); ::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1); - glsafe(::glTranslatef(0.f, 0.f, cone_height)); - //::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); + glsafe(::glTranslated(0., 0., cone_height)); ::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1); + glsafe(::glTranslated(0., 0., -cone_height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, cone_radius+cone_rad_diff, 24, 1); glsafe(::glPopMatrix()); } //::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); @@ -354,7 +349,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const bool GLGizmoHollow::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_model_object_id) || m_its == nullptr); + && ((m_model_object->id() != m_model_object_id) || ! m_mesh); } @@ -368,7 +363,6 @@ void GLGizmoHollow::update_mesh() // 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(); - m_its = &m_mesh->its; // If this is different mesh than last time if (m_model_object_id != m_model_object->id()) { @@ -449,7 +443,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos if (m_selection_empty) { std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); m_parent.set_as_dirty(); m_wait_for_up_event = true; @@ -563,7 +557,7 @@ void GLGizmoHollow::delete_selected_points(bool force) std::abort(); } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); for (unsigned int idx=0; idxhollow(); +} + +void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr mesh) +{ + // Called from Plater when the UI job finishes + m_cavity_mesh = std::move(mesh); - imesh.merge(omesh); - m_cavity_mesh.reset(new TriangleMesh); - *m_cavity_mesh = sla::to_triangle_mesh(imesh); - m_cavity_mesh.get()->require_shared_vertices(); m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); m_object_clipper.reset(); + m_volume_with_cavity.reset(); - // create a new GLVolume that only has the cavity inside - m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); - m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); - m_volume_with_cavity->finalize_geometry(true); - m_volume_with_cavity->set_volume_transformation(m_model_object->volumes.front()->get_transformation()); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); - m_parent.toggle_model_objects_visibility(false, m_model_object, m_active_instance); + if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside + Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); + volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); + m_volume_with_cavity->finalize_geometry(true); + m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); + } + m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); } std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const @@ -695,9 +697,8 @@ RENDER_AGAIN: if (m_editing_mode) { - if (m_imgui->button(m_desc.at("hollow"))) { - hollow_mesh(m_offset, m_adaptibility); - } + if (m_imgui->button(m_desc.at("hollow"))) + hollow_mesh(); float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; if (m_new_point_head_diameter > diameter_upper_cap) @@ -728,7 +729,7 @@ RENDER_AGAIN: cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f; float backup = m_new_point_head_diameter; m_new_point_head_diameter = m_old_point_head_diameter; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change point head diameter"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); m_new_point_head_diameter = backup; for (auto& cache_entry : m_editing_cache) if (cache_entry.selected) @@ -757,10 +758,10 @@ RENDER_AGAIN: m_imgui->text("Offset: "); ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); + ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); m_imgui->text("Adaptibility: "); ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_adaptibility, 0.f, 1.f, "%.1f"); + ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); @@ -912,7 +913,7 @@ void GLGizmoHollow::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); if (is_mesh_update_necessary()) update_mesh(); @@ -929,27 +930,16 @@ void GLGizmoHollow::on_set_state() m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_model_object && false; - if (will_ask) { - wxGetApp().CallAfter([this]() { - }); - // refuse to be turned off so the gizmo is active when the CallAfter is executed - m_state = m_old_state; - } - else { - // we are actually shutting down - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); - m_parent.toggle_model_objects_visibility(true); - m_normal_cache.clear(); - m_clipping_plane_distance = 0.f; - // Release clippers and the AABB raycaster. - m_its = nullptr; - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_mesh_raycaster.reset(); - m_cavity_mesh.reset(); - m_volume_with_cavity.reset(); - } + //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); + m_parent.toggle_model_objects_visibility(true); + m_normal_cache.clear(); + m_clipping_plane_distance = 0.f; + // Release clippers and the AABB raycaster. + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_mesh_raycaster.reset(); + m_cavity_mesh.reset(); + m_volume_with_cavity.reset(); } m_old_state = m_state; } @@ -977,7 +967,7 @@ void GLGizmoHollow::on_stop_dragging() && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected { m_editing_cache[m_hover_id] = m_point_before_drag; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point"))); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); m_editing_cache[m_hover_id] = backup; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 82455a9f40..afaaf659b5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -36,7 +36,6 @@ private: std::unique_ptr m_cavity_mesh; std::unique_ptr m_volume_with_cavity; const TriangleMesh* m_mesh; - const indexed_triangle_set* m_its; mutable const TriangleMesh* m_supports_mesh; mutable std::vector m_triangles; mutable std::vector m_supports_triangles; @@ -78,6 +77,8 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(bool force = false); ClippingPlane get_sla_clipping_plane() const; + void update_hollowed_mesh(std::unique_ptr mesh); + void get_hollowing_parameters(TriangleMesh const** object_mesh, float& offset, float& adaptability) const; bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } @@ -92,7 +93,7 @@ private: void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); - void hollow_mesh(float offset = 2.f, float adaptability = 1.f); + void hollow_mesh(); bool unsaved_changes() const; const TriangleMesh* mesh() const; @@ -109,7 +110,7 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo float m_offset = 2.f; - float m_adaptibility = 1.f; + float m_adaptability = 1.f; float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 0368e433ed..2c4d713161 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -158,6 +158,7 @@ public: void update_data(); EType get_current_type() const { return m_current; } + GLGizmoBase* get_current() const; bool is_running() const; bool handle_shortcut(int key); @@ -206,8 +207,6 @@ private: float get_total_overlay_height() const; float get_total_overlay_width() const; - GLGizmoBase* get_current() const; - bool generate_icons_texture() const; void update_on_off_state(const Vec2d& mouse_pos); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c67fac733a..eb7ec49779 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,6 +33,7 @@ #include "libslic3r/Format/3mf.hpp" #include "libslic3r/GCode/PreviewData.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/OpenVDBUtils.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" @@ -1559,7 +1560,8 @@ struct Plater::priv enum class Jobs : size_t { Arrange, - Rotoptimize + Rotoptimize, + Hollow }; class ArrangeJob : public Job @@ -1703,6 +1705,20 @@ struct Plater::priv void process() override; }; + class HollowJob : public Job + { + public: + using Job::Job; + void prepare() override; + void process() override; + void finalize() override; + private: + std::unique_ptr m_output; + const TriangleMesh* m_object_mesh = nullptr; + float m_offset = 0.f; + float m_adaptability = 0.f; + }; + // 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. @@ -1714,6 +1730,7 @@ struct Plater::priv ArrangeJob arrange_job{m_plater}; RotoptimizeJob rotoptimize_job{m_plater}; + HollowJob hollow_job{m_plater}; // To create a new job, just define a new subclass of Job, implement // the process and the optional prepare() and finalize() methods @@ -1721,7 +1738,8 @@ struct Plater::priv // if it cannot run concurrently with other jobs in this group std::vector> m_jobs{arrange_job, - rotoptimize_job}; + rotoptimize_job, + hollow_job}; public: ExclusiveJobGroup(priv *_plater) : m_plater(_plater) {} @@ -1816,6 +1834,7 @@ struct Plater::priv void reset(); void mirror(Axis axis); void arrange(); + void hollow(); void sla_optimize_rotation(); void split_object(); void split_volume(); @@ -2703,6 +2722,12 @@ void Plater::priv::arrange() m_ui_jobs.start(Jobs::Arrange); } +void Plater::priv::hollow() +{ + this->take_snapshot(_(L("Hollow"))); + m_ui_jobs.start(Jobs::Hollow); +} + // 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() { @@ -2834,6 +2859,38 @@ void Plater::priv::RotoptimizeJob::process() : _(L("Orientation found."))); } +void Plater::priv::HollowJob::prepare() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + const GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); + assert(gizmo_hollow); + gizmo_hollow->get_hollowing_parameters(&m_object_mesh, m_offset, m_adaptability); + m_output.reset(); +} + +void Plater::priv::HollowJob::process() +{ + Slic3r::sla::Contour3D imesh{*m_object_mesh}; + auto ptr = meshToVolume(imesh, {}); + sla::Contour3D omesh = volumeToMesh(*ptr, -m_offset, m_adaptability, true); + + if (omesh.empty()) + return; + + imesh.merge(omesh); + m_output.reset(new TriangleMesh()); + *m_output = sla::to_triangle_mesh(imesh); + m_output->require_shared_vertices(); +} + +void Plater::priv::HollowJob::finalize() +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); + assert(gizmo_hollow); + gizmo_hollow->update_hollowed_mesh(std::move(m_output)); +} + void Plater::priv::split_object() { int obj_idx = get_selected_object_idx(); @@ -4661,6 +4718,11 @@ void Plater::export_toolpaths_to_obj() const p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } +void Plater::hollow() +{ + p->hollow(); +} + void Plater::reslice() { // Stop arrange and (or) optimize rotation tasks. diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 00ceb89bc3..9ede19d719 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -190,6 +190,7 @@ public: void reload_from_disk(); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj() const; + void hollow(); void reslice(); void reslice_SLA_supports(const ModelObject &object, bool postpone_error_messages = false); void changed_object(int obj_idx); From 4b088658096ad38937795a9153ffd8406ce827a3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 8 Nov 2019 09:21:30 +0100 Subject: [PATCH 028/130] hollowing params renamed, filtering generalized --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/OpenVDBUtils.cpp | 31 ++++---------- src/libslic3r/OpenVDBUtils.hpp | 6 ++- src/libslic3r/PrintConfig.cpp | 4 +- src/libslic3r/PrintConfig.hpp | 8 ++-- src/libslic3r/SLA/Hollowing.cpp | 55 +++++++++++++++++++++++++ src/libslic3r/SLA/Hollowing.hpp | 17 ++++++++ src/libslic3r/SLAPrint.cpp | 12 +++--- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 11 ++--- src/slic3r/GUI/Preset.cpp | 4 +- src/slic3r/GUI/Tab.cpp | 4 +- tests/libslic3r/test_hollowing.cpp | 29 +------------ 12 files changed, 111 insertions(+), 72 deletions(-) create mode 100644 src/libslic3r/SLA/Hollowing.cpp create mode 100644 src/libslic3r/SLA/Hollowing.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e4b6ccd100..e28917b5f3 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -204,6 +204,8 @@ add_library(libslic3r STATIC SLA/SLARasterWriter.cpp SLA/ConcaveHull.hpp SLA/ConcaveHull.cpp + SLA/Hollowing.hpp + SLA/Hollowing.cpp ) encoding_check(libslic3r) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 53f9d5ae9f..91ccdd5c4e 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -2,7 +2,7 @@ #include "OpenVDBUtils.hpp" #include #include -#include +#include #include #include "MTUtils.hpp" @@ -158,35 +158,22 @@ inline void _scale(S s, sla::Contour3D &m) for (auto &p : m.points) p *= s; } -static void filter_grid(openvdb::FloatGrid &grid, double scale, double gain) -{ - static const double ROUNDNESS_COEFF = 1.; - - // Filtering: - if (gain > 0.) { - double rounding = ROUNDNESS_COEFF * gain; - int width = int(rounding * scale); - int count = 1; - openvdb::tools::Filter{grid}.gaussian(width, count); - } -} - template remove_cvref_t _hollowed_interior(Mesh &&mesh, double min_thickness, - double accuracy, - double smoothing) + double quality, + HollowingFilter filt) { using MMesh = remove_cvref_t; MMesh imesh{std::forward(mesh)}; - static const double ACCURACY_COEFF = 7.; + static const double QUALITY_COEFF = 7.; // I can't figure out how to increase the grid resolution through openvdb API // so the model will be scaled up before conversion and the result scaled // down. Voxels have a unit size. If I set voxelSize smaller, it scales // the whole geometry down, and doesn't increase the number of voxels. - auto scale = (1.0 + ACCURACY_COEFF * accuracy); // max 8x upscale, min is native voxel size + auto scale = (1.0 + QUALITY_COEFF * quality); // max 8x upscale, min is native voxel size _scale(scale, imesh); @@ -201,7 +188,7 @@ remove_cvref_t _hollowed_interior(Mesh &&mesh, return MMesh{}; } - filter_grid(*gridptr, scale, smoothing); + if (filt) filt(*gridptr, min_thickness, scale); double iso_surface = -offset; double adaptivity = 0.; @@ -214,10 +201,10 @@ remove_cvref_t _hollowed_interior(Mesh &&mesh, TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, - double accuracy, - double smoothing) + double quality, + HollowingFilter filt) { - return _hollowed_interior(mesh, min_thickness, accuracy, smoothing); + return _hollowed_interior(mesh, min_thickness, quality, filt); } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index 95b95db7ce..bd52e81eec 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -18,13 +18,15 @@ TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, double adaptivity = 0.0, bool relaxDisorientedTriangles = true); +using HollowingFilter = std::function; + // Generate an interior for any solid geometry maintaining a given minimum // wall thickness. The returned mesh has triangles with normals facing inside // the mesh so the result can be directly merged with the input to finish the // hollowing. -// TODO: The thicknes is not strictly maintained due to the used gaussian filter TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, - double accuracy = 0.5, double smoothing = 0.5); + double quality = 0.5, + HollowingFilter filt = nullptr); } // namespace Slic3r diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e621d4b340..96b84cf817 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2847,7 +2847,7 @@ void PrintConfigDef::init_sla_params() def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(4)); - def = this->add("hollowing_accuracy", coFloat); + def = this->add("hollowing_quality", coFloat); def->label = L("Hollowing accuracy"); def->category = L("Hollowing"); def->tooltip = L("Performance vs accuracy of calculation. Lower values may produce unwanted artifacts."); @@ -2856,7 +2856,7 @@ void PrintConfigDef::init_sla_params() def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.5)); - def = this->add("hollowing_smoothness", coFloat); + def = this->add("hollowing_flatness", coFloat); def->label = L("Hollowing smoothness"); def->category = L("Hollowing"); def->tooltip = L("The cavity shape is a smoothed version of the outside original shape. " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d07999c886..9ed63e5d4c 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1099,11 +1099,11 @@ public: ConfigOptionFloat hollowing_min_thickness; // Indirectly controls the voxel size (resolution) used by openvdb - ConfigOptionFloat hollowing_accuracy; + ConfigOptionFloat hollowing_quality; // Indirectly controls the amount of filtering used to blur geometry // features in the created cavity. - ConfigOptionFloat hollowing_smoothness; + ConfigOptionFloat hollowing_flatness; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -1143,8 +1143,8 @@ protected: OPT_PTR(pad_object_connector_penetration); OPT_PTR(hollowing_enable); OPT_PTR(hollowing_min_thickness); - OPT_PTR(hollowing_accuracy); - OPT_PTR(hollowing_smoothness); + OPT_PTR(hollowing_quality); + OPT_PTR(hollowing_flatness); } }; diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp new file mode 100644 index 0000000000..4309003134 --- /dev/null +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -0,0 +1,55 @@ +#include "Hollowing.hpp" +#include +#include +#include +#include + +namespace Slic3r { +namespace sla { + +namespace { + +void filter_grid_sla(openvdb::FloatGrid &grid, double scale, double /*thickness*/, double flatness) +{ + static const double ROUNDNESS_COEFF = 1.; + + // Filtering: + if (flatness > 0.) { + double rounding = ROUNDNESS_COEFF * flatness; + int width = int(rounding * scale); + int count = 1; + openvdb::tools::Filter{grid}.gaussian(width, count); + } +} +// openvdb::tools::levelSetRebuild(grid, -float(thickness * 2)); +// filter_grid_sla(grid, scale, thickness, flatness); + +// openvdb::tools::levelSetRebuild(grid, float(thickness)); + + +void redist_grid_sla(openvdb::FloatGrid &grid, double scale, double thickness, double flatness) +{ +// openvdb::tools::levelSetRebuild(grid, -float(scale * thickness)); + + + openvdb::tools::LevelSetFilter filt{grid}; + +// filt.gaussian(int(flatness * scale)); + +// openvdb::tools::levelSetRebuild(grid, float(scale * thickness)); + //grid = openvdb::tools::topologyToLevelSet(grid); +} + +} + +TriangleMesh generate_interior(const TriangleMesh &mesh, + double min_thickness, + double quality, + double flatness) +{ + namespace plc = std::placeholders; + auto filt = std::bind(filter_grid_sla, plc::_1, plc::_2, plc::_3, flatness); + return hollowed_interior(mesh, min_thickness, quality, filt); +} + +}} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp new file mode 100644 index 0000000000..6abbe9f994 --- /dev/null +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -0,0 +1,17 @@ +#ifndef HOLLOWINGFILTER_HPP +#define HOLLOWINGFILTER_HPP + +#include + +namespace Slic3r { +namespace sla { + +TriangleMesh generate_interior(const TriangleMesh &mesh, + double min_thickness, + double quality = 0.5, + double flatness = 0.5); + +} +} + +#endif // HOLLOWINGFILTER_H diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d2200bcfb2..b150a4f768 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -3,7 +3,7 @@ #include "SLA/SLAPad.hpp" #include "SLA/SLAAutoSupports.hpp" #include "ClipperUtils.hpp" -#include "OpenVDBUtils.hpp" +#include "SLA/Hollowing.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" @@ -776,10 +776,10 @@ void SLAPrint::process() po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); double thickness = po.m_config.hollowing_min_thickness.getFloat(); - double accuracy = po.m_config.hollowing_accuracy.getFloat(); - double blur = po.m_config.hollowing_smoothness.getFloat(); + double accuracy = po.m_config.hollowing_quality.getFloat(); + double blur = po.m_config.hollowing_flatness.getFloat(); po.m_hollowing_data->interior = - hollowed_interior(po.transformed_mesh(), thickness, accuracy, blur); + generate_interior(po.transformed_mesh(), thickness, accuracy, blur); if (po.m_hollowing_data->interior.empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; @@ -1754,8 +1754,8 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector& Preset::sla_print_options() "pad_object_connector_penetration", "hollowing_enable", "hollowing_min_thickness", - "hollowing_accuracy", - "hollowing_smoothness", + "hollowing_quality", + "hollowing_flatness", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d20e0ee6c8..333384802a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3568,8 +3568,8 @@ void TabSLAPrint::build() optgroup = page->new_optgroup(_(L("Hollowing"))); optgroup->append_single_option_line("hollowing_enable"); optgroup->append_single_option_line("hollowing_min_thickness"); - optgroup->append_single_option_line("hollowing_accuracy"); - optgroup->append_single_option_line("hollowing_smoothness"); + optgroup->append_single_option_line("hollowing_quality"); + optgroup->append_single_option_line("hollowing_flatness"); page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 9a2ea2e726..4264774a3e 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -2,7 +2,7 @@ #include #include -#include "libslic3r/OpenVDBUtils.hpp" +#include "libslic3r/SLA/Hollowing.hpp" #include #include "libslic3r/Format/OBJ.hpp" @@ -22,38 +22,13 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -//static Slic3r::TriangleMesh hollowed_interior(const Slic3r::TriangleMesh &mesh, -// double min_thickness) -//{ -// Slic3r::sla::Contour3D imesh = Slic3r::sla::Contour3D{mesh}; - -// double scale = std::max(1.0, 3. / min_thickness); -// double offset = scale * min_thickness; -// float range = float(std::max(2 * offset, scale)); - -// for (auto &p : imesh.points) p *= scale; -// auto ptr = Slic3r::meshToVolume(imesh, {}, 0.1f * float(offset), range); - -// REQUIRE(ptr); - -// openvdb::tools::Filter{*ptr}.gaussian(int(scale), 1); - -// double iso_surface = -offset; -// double adaptivity = 0.; -// Slic3r::sla::Contour3D omesh = Slic3r::volumeToMesh(*ptr, iso_surface, adaptivity); - -// for (auto &p : omesh.points) p /= scale; - -// return to_triangle_mesh(omesh); -//} - TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") { Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); Benchmark bench; bench.start(); - Slic3r::TriangleMesh out_mesh = hollowed_interior(in_mesh, 0.5); + Slic3r::TriangleMesh out_mesh = Slic3r::sla::generate_interior(in_mesh, 0.5); bench.stop(); From 645f13a0ae7eb606e8a745eddbad864b7c28c46d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 8 Nov 2019 14:05:56 +0100 Subject: [PATCH 029/130] Drain holes are now saved in ModelObject Internal changes in GLGizmoHollow.cpp --- src/libslic3r/Model.cpp | 4 + src/libslic3r/Model.hpp | 5 +- src/libslic3r/SLA/SLACommon.hpp | 33 ++ src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 545 +++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 32 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 19 +- 6 files changed, 289 insertions(+), 349 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 061c5bd50a..fa865a8b1b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -606,6 +606,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) assert(this->config.id() == rhs.config.id()); this->sla_support_points = rhs.sla_support_points; this->sla_points_status = rhs.sla_points_status; + this->sla_drain_holes = rhs.sla_drain_holes; this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment this->layer_height_profile = rhs.layer_height_profile; this->printable = rhs.printable; @@ -646,6 +647,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) assert(this->config.id() == rhs.config.id()); this->sla_support_points = std::move(rhs.sla_support_points); this->sla_points_status = std::move(rhs.sla_points_status); + this->sla_drain_holes = std::move(rhs.sla_drain_holes); this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment this->layer_height_profile = std::move(rhs.layer_height_profile); this->origin_translation = std::move(rhs.origin_translation); @@ -1099,6 +1101,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper) { upper->set_model(nullptr); upper->sla_support_points.clear(); + lower->sla_drain_holes.clear(); upper->sla_points_status = sla::PointsStatus::NoPoints; upper->clear_volumes(); upper->input_file = ""; @@ -1107,6 +1110,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_lower) { lower->set_model(nullptr); lower->sla_support_points.clear(); + lower->sla_drain_holes.clear(); lower->sla_points_status = sla::PointsStatus::NoPoints; lower->clear_volumes(); lower->input_file = ""; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 410c2d3ef4..f0641a1822 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -203,6 +203,9 @@ public: // the SLA gizmo and the backend). sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; + // Holes to be drilled into the object so resin can flow out + std::vector sla_drain_holes; + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment @@ -372,7 +375,7 @@ private: template void serialize(Archive &ar) { ar(cereal::base_class(this)); Internal::StaticSerializationWrapper config_wrapper(config); - ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, printable, origin_translation, + ar(name, input_file, instances, volumes, config_wrapper, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, sla_drain_holes, printable, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 634c5611f4..6e56b6de75 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -80,6 +80,39 @@ struct SupportPoint using SupportPoints = std::vector; +struct DrainHole +{ + Vec3f m_pos; + Vec3f m_normal; + float m_radius; + float m_height; + + DrainHole() + : m_pos(Vec3f::Zero()), m_normal(Vec3f::UnitZ()), m_radius(5.f), + m_height(10.f) + {} + + DrainHole(Vec3f position, Vec3f normal, float radius, float height) + : m_pos(position) + , m_normal(normal) + , m_radius(radius) + , m_height(height) + {} + + bool operator==(const DrainHole &sp) const + { + return (m_pos == sp.m_pos) && (m_normal == sp.m_normal) + && is_approx(m_radius, sp.m_radius) && is_approx(m_height, sp.m_height); + } + + bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } + + template void serialize(Archive &ar) + { + ar(m_pos, m_normal, m_radius, m_height); + } +}; + struct Contour3D; /// An index-triangle structure for libIGL functions. Also serves as an diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f0aaca9ade..806069663b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -52,6 +52,7 @@ bool GLGizmoHollow::on_init() m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; m_desc["reset_direction"] = _(L("Reset direction")); m_desc["hollow"] = _(L("Hollow")); + m_desc["show_supports"] = _(L("Show supports")); return true; } @@ -243,13 +244,13 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glsafe(::glMultMatrixd(instance_matrix.data())); float render_color[4]; - size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size(); + size_t cache_size = m_model_object->sla_drain_holes.size(); for (size_t i = 0; i < cache_size; ++i) { - const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i]; - const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false; + const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; + const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped(support_point.pos.cast())) + if (is_mesh_point_clipped(drain_hole.m_pos.cast())) continue; // First decide about the color of the point. @@ -262,7 +263,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons } else { render_color[3] = 1.f; - if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active + if (size_t(m_hover_id) == i) { render_color[0] = 0.f; render_color[1] = 1.0f; render_color[2] = 1.0f; @@ -280,43 +281,34 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); + glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) glFrontFace(GL_CW); // Matrices set, we can render the point mark now. - // If in editing mode, we'll also render a cone pointing to the sphere. - if (m_editing_mode) { - // in case the normal is not yet cached, find and cache it - if (m_editing_cache[i].normal == Vec3f::Zero()) - m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * drain_hole.m_normal.cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + + const double cylinder_radius = double(drain_hole.m_radius) * RenderPointScale; //0.25; // mm + const double stick_out_length = 1.; + const double cone_height = m_new_hole_height + stick_out_length; + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -cone_height+stick_out_length)); + ::gluCylinder(m_quadric, cylinder_radius, cylinder_radius, cone_height, 24, 1); + glsafe(::glTranslated(0., 0., cone_height)); + ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); + glsafe(::glTranslated(0., 0., -cone_height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); + glsafe(::glPopMatrix()); - const double cone_radius = double(support_point.head_front_radius) * RenderPointScale; //0.25; // mm - const double cone_height = m_new_cone_height; - //const double cone_rad_diff = m_new_cone_angle*(cone_radius/(cone_height/2.))*(cone_height/2.); - const double cone_rad_diff = m_new_cone_angle*cone_radius; - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -cone_height/2.)); - //::gluCylinder(m_quadric, cone_radius, cone_radius, cone_height, 24, 1); - ::gluCylinder(m_quadric, cone_radius+cone_rad_diff, cone_radius-cone_rad_diff, cone_height, 24, 1); - glsafe(::glTranslated(0., 0., cone_height)); - ::gluDisk(m_quadric, 0.0, cone_radius-cone_rad_diff, 24, 1); - glsafe(::glTranslated(0., 0., -cone_height)); - glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, cone_radius+cone_rad_diff, 24, 1); - glsafe(::glPopMatrix()); - } - //::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); if (vol->is_left_handed()) glFrontFace(GL_CCW); - glsafe(::glPopMatrix()); } @@ -412,122 +404,121 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair pos_and_normal; - if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second); - m_parent.set_as_dirty(); - m_wait_for_up_event = true; - } - else - return false; + // left down without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + std::pair pos_and_normal; + if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); + m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first, pos_and_normal.second, m_new_hole_radius, m_new_hole_height); + m_selected.push_back(false); + assert(m_selected.size == m_model_object->sla_drain_holes.size()); + m_parent.set_as_dirty(); + m_wait_for_up_event = true; } else - select_point(NoPoints); + return false; + } + else + select_point(NoPoints); + return true; + } + + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { + // Is this a selection or deselection rectangle? + GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + + // First collect positions of all the points in world coordinates. + Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + std::vector points; + for (unsigned int i=0; isla_drain_holes.size(); ++i) + points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].m_pos.cast()); + + // Now ask the rectangle which of the points are inside. + std::vector points_inside; + std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); + for (size_t idx : points_idxs) + points_inside.push_back(points[idx].cast()); + + // Only select/deselect points that are actually visible + for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + { + if (rectangle_status == GLSelectionRectangle::Deselect) + unselect_point(points_idxs[idx]); + else + select_point(points_idxs[idx]); + } + return true; + } + + // left up with no selection rectangle + if (action == SLAGizmoEventType::LeftUp) { + if (m_wait_for_up_event) { + m_wait_for_up_event = false; + return true; + } + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging) { + if (m_wait_for_up_event) + return true; // point has been placed and the button not released yet + // this prevents GLCanvas from starting scene rotation + + if (m_selection_rectangle.is_dragging()) { + m_selection_rectangle.dragging(mouse_position); return true; } - // left up with selection rectangle - select points inside the rectangle: - if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) { - // Is this a selection or deselection rectangle? - GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); + return false; + } - // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); - trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - std::vector points; - for (unsigned int i=0; i()); + if (action == SLAGizmoEventType::Delete) { + // delete key pressed + delete_selected_points(); + return true; + } - // Now ask the rectangle which of the points are inside. - std::vector points_inside; - std::vector points_idxs = m_selection_rectangle.stop_dragging(m_parent, points); - for (size_t idx : points_idxs) - points_inside.push_back(points[idx].cast()); - - // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) - { - if (rectangle_status == GLSelectionRectangle::Deselect) - unselect_point(points_idxs[idx]); - else - select_point(points_idxs[idx]); - } - return true; - } - - // left up with no selection rectangle - if (action == SLAGizmoEventType::LeftUp) { - if (m_wait_for_up_event) { - m_wait_for_up_event = false; - return true; - } - } - - // dragging the selection rectangle: - if (action == SLAGizmoEventType::Dragging) { - if (m_wait_for_up_event) - return true; // point has been placed and the button not released yet - // this prevents GLCanvas from starting scene rotation - - if (m_selection_rectangle.is_dragging()) { - m_selection_rectangle.dragging(mouse_position); - return true; - } - - return false; - } - - if (action == SLAGizmoEventType::Delete) { - // delete key pressed + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); delete_selected_points(); return true; } + return false; + } - if (action == SLAGizmoEventType::RightDown) { - if (m_hover_id != -1) { - select_point(NoPoints); - select_point(m_hover_id); - delete_selected_points(); - return true; - } - return false; - } - - if (action == SLAGizmoEventType::SelectAll) { - select_point(AllPoints); - return true; - } + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { @@ -552,39 +543,26 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos void GLGizmoHollow::delete_selected_points(bool force) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: delete_selected_points called out of editing mode!" << std::endl; - std::abort(); - } - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); - for (unsigned int idx=0; idxsla_drain_holes.size(); ++idx) { + if (m_selected[idx]) { + m_selected.erase(m_selected.begin()+idx); + m_model_object->sla_drain_holes.erase(m_model_object->sla_drain_holes.begin() + (idx--)); } } select_point(NoPoints); - - //m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void GLGizmoHollow::on_update(const UpdateData& data) { - if (! m_editing_mode) - return; - else { - if (m_hover_id != -1) { - std::pair pos_and_normal; - if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) - return; - m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first; - m_editing_cache[m_hover_id].support_point.is_new_island = false; - m_editing_cache[m_hover_id].normal = pos_and_normal.second; - // Do not update immediately, wait until the mouse is released. - // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - } + if (m_hover_id != -1) { + std::pair pos_and_normal; + if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) + return; + m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first; + m_model_object->sla_drain_holes[m_hover_id].m_normal = pos_and_normal.second; } } @@ -661,17 +639,12 @@ ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_model_object) + if (! m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. RENDER_AGAIN: - //m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); - //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); - //ImGui::SetNextWindowSize(ImVec2(window_size)); - const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -679,7 +652,6 @@ RENDER_AGAIN: m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f); const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f); @@ -690,125 +662,72 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); - bool force_refresh = false; bool remove_selected = false; bool remove_all = false; - if (m_editing_mode) { + if (m_imgui->button(m_desc.at("hollow"))) + hollow_mesh(); - if (m_imgui->button(m_desc.at("hollow"))) - hollow_mesh(); + float diameter_upper_cap = 20.f; //static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; + if (m_new_hole_radius > diameter_upper_cap) + m_new_hole_radius = diameter_upper_cap; + m_imgui->text(m_desc.at("head_diameter")); + ImGui::SameLine(diameter_slider_left); + ImGui::PushItemWidth(window_width - diameter_slider_left); - float diameter_upper_cap = static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; - if (m_new_point_head_diameter > diameter_upper_cap) - m_new_point_head_diameter = diameter_upper_cap; - m_imgui->text(m_desc.at("head_diameter")); - ImGui::SameLine(diameter_slider_left); - ImGui::PushItemWidth(window_width - diameter_slider_left); - - // Following is a nasty way to: - // - save the initial value of the slider before one starts messing with it - // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene - // - take correct undo/redo snapshot after the user is done with moving the slider - float initial_value = m_new_point_head_diameter; - ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f"); - if (ImGui::IsItemClicked()) { - if (m_old_point_head_diameter == 0.f) - m_old_point_head_diameter = initial_value; - } - if (ImGui::IsItemEdited()) { - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; - } - if (ImGui::IsItemDeactivatedAfterEdit()) { - // momentarily restore the old value to take snapshot - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f; - float backup = m_new_point_head_diameter; - m_new_point_head_diameter = m_old_point_head_diameter; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); - m_new_point_head_diameter = backup; - for (auto& cache_entry : m_editing_cache) - if (cache_entry.selected) - cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f; - m_old_point_head_diameter = 0.f; - } - - // !!!! Something as above should be done for the cone angle - m_imgui->text("Hole taper: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_new_cone_angle, -1.f, 1.f, "%.1f"); - m_imgui->text("Hole height: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_new_cone_height, 0.1f, 10.f, "%.1f"); - - m_imgui->disabled_begin(m_selection_empty); - remove_selected = m_imgui->button(m_desc.at("remove_selected")); - m_imgui->disabled_end(); - - m_imgui->disabled_begin(m_editing_cache.empty()); - remove_all = m_imgui->button(m_desc.at("remove_all")); - m_imgui->disabled_end(); - - m_imgui->text(" "); // vertical gap - - - m_imgui->text("Offset: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); - m_imgui->text("Adaptibility: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); + // Following is a nasty way to: + // - save the initial value of the slider before one starts messing with it + // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene + // - take correct undo/redo snapshot after the user is done with moving the slider + float initial_value = m_new_hole_radius; + ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f"); + if (ImGui::IsItemClicked()) { + if (m_old_hole_radius == 0.f) + m_old_hole_radius = initial_value; } - else { // not in editing mode: - m_imgui->text(m_desc.at("minimal_distance")); - ImGui::SameLine(settings_sliders_left); - ImGui::PushItemWidth(window_width - settings_sliders_left); - - std::vector opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"}); - float density = static_cast(opts[0])->value; - float minimal_point_distance = static_cast(opts[1])->value; - - ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm"); - bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider - bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider - bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider - - m_imgui->text(m_desc.at("points_density")); - ImGui::SameLine(settings_sliders_left); - - ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%"); - slider_clicked |= ImGui::IsItemClicked(); - slider_edited |= ImGui::IsItemEdited(); - slider_released |= ImGui::IsItemDeactivatedAfterEdit(); - - if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo - m_minimal_point_distance_stash = minimal_point_distance; - m_density_stash = density; - } - if (slider_edited) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; - } - if (slider_released) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; - wxGetApp().obj_list()->update_and_show_object_settings_item(); - } - - - m_imgui->disabled_begin(m_normal_cache.empty()); - remove_all = m_imgui->button(m_desc.at("remove_all")); - m_imgui->disabled_end(); - + if (ImGui::IsItemEdited()) { + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + // momentarily restore the old value to take snapshot + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_old_hole_radius; + float backup = m_new_hole_radius; + m_new_hole_radius = m_old_hole_radius; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); + m_new_hole_radius = backup; + for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_old_hole_radius = 0.f; } + // !!!! Something as above should be done for the undo/redo + m_imgui->text("Hole height: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_new_hole_height, 0.1f, 10.f, "%.1f"); + + m_imgui->disabled_begin(m_selection_empty); + remove_selected = m_imgui->button(m_desc.at("remove_selected")); + m_imgui->disabled_end(); + + m_imgui->disabled_begin(m_model_object->sla_drain_holes.empty()); + remove_all = m_imgui->button(m_desc.at("remove_all")); + m_imgui->disabled_end(); + + m_imgui->text(" "); // vertical gap + + + m_imgui->text("Offset: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); + m_imgui->text("Adaptibility: "); + ImGui::SameLine(); + ImGui::SliderFloat(" ", &m_adaptability, 0.f, 1.f, "%.1f"); // Following is rendered in both editing and non-editing mode: m_imgui->text(""); @@ -827,7 +746,6 @@ RENDER_AGAIN: if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); - if (m_imgui->button("?")) { wxGetApp().CallAfter([]() { SlaGizmoHelpDialog help_dlg; @@ -835,18 +753,18 @@ RENDER_AGAIN: }); } - m_imgui->end(); - - if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode - m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); + if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); force_refresh = true; } - m_old_editing_state = m_editing_mode; + + m_imgui->end(); + if (remove_selected || remove_all) { force_refresh = false; m_parent.set_as_dirty(); - bool was_in_editing = m_editing_mode; + if (remove_all) { select_point(AllPoints); delete_selected_points(true); // true - delete regardless of locked status @@ -927,12 +845,11 @@ void GLGizmoHollow::on_set_state() // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; + m_new_hole_radius = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); - m_normal_cache.clear(); m_clipping_plane_distance = 0.f; // Release clippers and the AABB raycaster. m_object_clipper.reset(); @@ -951,27 +868,27 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_point_before_drag = m_editing_cache[m_hover_id]; + m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].m_pos; } else - m_point_before_drag = CacheEntry(); + m_hole_before_drag = Vec3f::Zero(); } void GLGizmoHollow::on_stop_dragging() { if (m_hover_id != -1) { - CacheEntry backup = m_editing_cache[m_hover_id]; + Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].m_pos; - if (m_point_before_drag.support_point.pos != Vec3f::Zero() // some point was touched - && backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected + if (m_hole_before_drag != Vec3f::Zero() // some point was touched + && backup != m_hole_before_drag) // and it was moved, not just selected { - m_editing_cache[m_hover_id] = m_point_before_drag; + m_model_object->sla_drain_holes[m_hover_id].m_pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_editing_cache[m_hover_id] = backup; + m_model_object->sla_drain_holes[m_hover_id].m_pos = backup; } } - m_point_before_drag = CacheEntry(); + m_hole_before_drag = Vec3f::Zero(); } @@ -981,9 +898,8 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) ar(m_clipping_plane_distance, *m_clipping_plane, m_model_object_id, - m_new_point_head_diameter, - m_normal_cache, - m_editing_cache, + m_new_hole_radius, + m_selected, m_selection_empty ); } @@ -995,9 +911,8 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const ar(m_clipping_plane_distance, *m_clipping_plane, m_model_object_id, - m_new_point_head_diameter, - m_normal_cache, - m_editing_cache, + m_new_hole_radius, + m_selected, m_selection_empty ); } @@ -1006,63 +921,41 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const void GLGizmoHollow::select_point(int i) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: select_point called when out of editing mode!" << std::endl; - std::abort(); - } - if (i == AllPoints || i == NoPoints) { - for (auto& point_and_selection : m_editing_cache) - point_and_selection.selected = ( i == AllPoints ); + m_selected.assign(m_selected.size(), i == AllPoints); m_selection_empty = (i == NoPoints); if (i == AllPoints) - m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f; + m_new_hole_radius = m_model_object->sla_drain_holes[0].m_radius; } else { - m_editing_cache[i].selected = true; + while (size_t(i) >= m_selected.size()) + m_selected.push_back(false); + m_selected[i] = true; m_selection_empty = false; - m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f; + m_new_hole_radius = m_model_object->sla_drain_holes[i].m_radius; } } void GLGizmoHollow::unselect_point(int i) { - if (! m_editing_mode) { - std::cout << "DEBUGGING: unselect_point called when out of editing mode!" << std::endl; - std::abort(); - } - - m_editing_cache[i].selected = false; + m_selected[i] = false; m_selection_empty = true; - for (const CacheEntry& ce : m_editing_cache) { - if (ce.selected) { + for (const bool sel : m_selected) { + if (sel) { m_selection_empty = false; break; } } } - -bool GLGizmoHollow::unsaved_changes() const -{ - if (m_editing_cache.size() != m_normal_cache.size()) - return true; - - for (size_t i=0; isla_drain_holes.size(), false); } - void GLGizmoHollow::update_clipping_plane(bool keep_normal) const { Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index afaaf659b5..ce3d60f27b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -25,7 +25,7 @@ private: ObjectID m_model_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb - mutable double m_z_shift = 0.f; + mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); const float RenderPointScale = 1.f; @@ -46,27 +46,26 @@ private: class CacheEntry { public: CacheEntry() : - support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {} + drain_hole(sla::DrainHole()), selected(false) {} - CacheEntry(const sla::SupportPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero()) : - support_point(point), selected(sel), normal(norm) {} + CacheEntry(const sla::DrainHole& point, bool sel = false) : + drain_hole(point), selected(sel) {} bool operator==(const CacheEntry& rhs) const { - return (support_point == rhs.support_point); + return (drain_hole == rhs.drain_hole); } bool operator!=(const CacheEntry& rhs) const { return ! ((*this) == rhs); } - sla::SupportPoint support_point; + sla::DrainHole drain_hole; bool selected; // whether the point is selected - Vec3f normal; template void serialize(Archive & ar) { - ar(support_point, selected, normal); + ar(drain_hole, selected); } }; @@ -97,17 +96,14 @@ private: bool unsaved_changes() const; const TriangleMesh* mesh() const; - bool m_editing_mode = true; // Is editing mode active? - bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). - float m_new_point_head_diameter; // Size of a new point. - float m_new_cone_angle = 0.f; - float m_new_cone_height = 5.f; - CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited - float m_old_point_head_diameter = 0.; // the same + bool m_show_supports = true; + float m_new_hole_radius; // Size of a new hole. + float m_new_hole_height = 5.f; + float m_old_hole_radius = 0.; // undo/redo - so we know what state was edited + Vec3f m_hole_before_drag = Vec3f::Zero(); float m_minimal_point_distance_stash = 0.f; // and again float m_density_stash = 0.f; // and again - mutable std::vector m_editing_cache; // a support point and whether it is currently selected - std::vector m_normal_cache; // to restore after discarding changes or undo/redo + mutable std::vector m_selected; // which holes are currently selected float m_offset = 2.f; float m_adaptability = 1.f; @@ -148,7 +144,7 @@ protected: void on_set_hover_id() override { - if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) + if (int(m_model_object->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } void on_start_dragging() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index be200b56d6..f4427638b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -750,20 +750,31 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) if (evt.GetEventType() == wxEVT_KEY_UP) { - if (m_current == SlaSupports) + if (m_current == SlaSupports || m_current == Hollow) { - GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + bool is_editing = true; + bool is_rectangle_dragging = false; + + if (m_current == SlaSupports) { + GLGizmoSlaSupports* gizmo = dynamic_cast(get_current()); + is_editing = gizmo->is_in_editing_mode(); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } + else { + GLGizmoHollow* gizmo = dynamic_cast(get_current()); + is_rectangle_dragging = gizmo->is_selection_rectangle_dragging(); + } if (keyCode == WXK_SHIFT) { // shift has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::ShiftUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::ShiftUp) || (is_editing && is_rectangle_dragging)) processed = true; } else if (keyCode == WXK_ALT) { // alt has been just released - SLA gizmo might want to close rectangular selection. - if (gizmo_event(SLAGizmoEventType::AltUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging())) + if (gizmo_event(SLAGizmoEventType::AltUp) || (is_editing && is_rectangle_dragging)) processed = true; } } From ac8eab5fa8e6c38faf788349bcb4f0f707d6bbbe Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 8 Nov 2019 16:51:43 +0100 Subject: [PATCH 030/130] Enhanced hollowing scheme, closing distance working as expected. --- src/libslic3r/OpenVDBUtils.cpp | 109 +++---------------- src/libslic3r/OpenVDBUtils.hpp | 33 ++++-- src/libslic3r/PrintConfig.cpp | 10 +- src/libslic3r/PrintConfig.hpp | 9 +- src/libslic3r/SLA/Hollowing.cpp | 135 ++++++++++++++++++------ src/libslic3r/SLAPrint.cpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 9 +- src/slic3r/GUI/Preset.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 10 files changed, 160 insertions(+), 165 deletions(-) diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index 91ccdd5c4e..c76bad96c5 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -2,9 +2,9 @@ #include "OpenVDBUtils.hpp" #include #include -#include -#include -#include "MTUtils.hpp" +#include + +//#include "MTUtils.hpp" namespace Slic3r { @@ -58,7 +58,7 @@ void Contour3DDataAdapter::getIndexSpacePoint(size_t n, // docs say it should be called ones. It does a mutex lock-unlock sequence all // even if was called previously. -openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, +openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh, const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, @@ -70,7 +70,7 @@ openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh &mesh, interiorBandWidth, flags); } -static openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D &mesh, +openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh, const openvdb::math::Transform &tr, float exteriorBandWidth, float interiorBandWidth, @@ -82,13 +82,8 @@ static openvdb::FloatGrid::Ptr meshToVolume(const sla::Contour3D &mesh, flags); } -inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } -inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } -inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } -inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } - template -sla::Contour3D __volumeToMesh(const Grid &grid, +sla::Contour3D _volumeToMesh(const Grid &grid, double isovalue, double adaptivity, bool relaxDisorientedTriangles) @@ -114,97 +109,27 @@ sla::Contour3D __volumeToMesh(const Grid &grid, return ret; } -template inline -Mesh _volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue = 0.0, - double adaptivity = 0.0, - bool relaxDisorientedTriangles = true); - -template<> inline -TriangleMesh _volumeToMesh(const openvdb::FloatGrid &grid, +TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, double isovalue, double adaptivity, bool relaxDisorientedTriangles) { - return to_triangle_mesh(__volumeToMesh(grid, isovalue, adaptivity, - relaxDisorientedTriangles)); + return to_triangle_mesh( + _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles)); } -template<> inline -sla::Contour3D _volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { - return __volumeToMesh(grid, isovalue, adaptivity, - relaxDisorientedTriangles); + return _volumeToMesh(grid, isovalue, adaptivity, + relaxDisorientedTriangles); } -TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) +openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, double iso, double er, double ir) { - return _volumeToMesh(grid, isovalue, adaptivity, - relaxDisorientedTriangles); -} - -template> -inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } - -template> -inline void _scale(S s, sla::Contour3D &m) -{ - for (auto &p : m.points) p *= s; -} - -template -remove_cvref_t _hollowed_interior(Mesh &&mesh, - double min_thickness, - double quality, - HollowingFilter filt) -{ - using MMesh = remove_cvref_t; - MMesh imesh{std::forward(mesh)}; - - static const double QUALITY_COEFF = 7.; - - // I can't figure out how to increase the grid resolution through openvdb API - // so the model will be scaled up before conversion and the result scaled - // down. Voxels have a unit size. If I set voxelSize smaller, it scales - // the whole geometry down, and doesn't increase the number of voxels. - auto scale = (1.0 + QUALITY_COEFF * quality); // max 8x upscale, min is native voxel size - - _scale(scale, imesh); - - double offset = scale * min_thickness; - float range = float(std::max(2 * offset, scale)); - auto gridptr = meshToVolume(imesh, {}, 0.1f * float(offset), range); - - assert(gridptr); - - if (!gridptr) { - BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; - return MMesh{}; - } - - if (filt) filt(*gridptr, min_thickness, scale); - - double iso_surface = -offset; - double adaptivity = 0.; - auto omesh = _volumeToMesh(*gridptr, iso_surface, adaptivity); - - _scale(1. / scale, omesh); - - return omesh; -} - -TriangleMesh hollowed_interior(const TriangleMesh &mesh, - double min_thickness, - double quality, - HollowingFilter filt) -{ - return _hollowed_interior(mesh, min_thickness, quality, filt); + return openvdb::tools::levelSetRebuild(grid, float(iso), float(er), float(ir)); } } // namespace Slic3r diff --git a/src/libslic3r/OpenVDBUtils.hpp b/src/libslic3r/OpenVDBUtils.hpp index bd52e81eec..151647bfc4 100644 --- a/src/libslic3r/OpenVDBUtils.hpp +++ b/src/libslic3r/OpenVDBUtils.hpp @@ -7,26 +7,37 @@ namespace Slic3r { -openvdb::FloatGrid::Ptr meshToVolume(const TriangleMesh & mesh, +inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } +inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast(); } +inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } +inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } + +openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh, const openvdb::math::Transform &tr = {}, float exteriorBandWidth = 3.0f, float interiorBandWidth = 3.0f, int flags = 0); -TriangleMesh volumeToMesh(const openvdb::FloatGrid &grid, +openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D & mesh, + const openvdb::math::Transform &tr = {}, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0); + +sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles = true); + +TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, double isovalue = 0.0, double adaptivity = 0.0, bool relaxDisorientedTriangles = true); -using HollowingFilter = std::function; - -// Generate an interior for any solid geometry maintaining a given minimum -// wall thickness. The returned mesh has triangles with normals facing inside -// the mesh so the result can be directly merged with the input to finish the -// hollowing. -TriangleMesh hollowed_interior(const TriangleMesh &mesh, double min_thickness, - double quality = 0.5, - HollowingFilter filt = nullptr); +openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, + double iso, + double ext_range = 3., + double int_range = 3.); } // namespace Slic3r diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 96b84cf817..58212f1289 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2856,15 +2856,13 @@ void PrintConfigDef::init_sla_params() def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.5)); - def = this->add("hollowing_flatness", coFloat); - def->label = L("Hollowing smoothness"); + def = this->add("hollowing_closing_distance", coFloat); + def->label = L("Hollowing closing distance"); def->category = L("Hollowing"); - def->tooltip = L("The cavity shape is a smoothed version of the outside original shape. " - "Possible values span from 0 to 1 and control the amount of surface smoothing."); + def->tooltip = L(""); def->min = 0; - def->max = 1; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(0.5)); + def->set_default_value(new ConfigOptionFloat(2.0)); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 9ed63e5d4c..671a6ce8e1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1100,10 +1100,9 @@ public: // Indirectly controls the voxel size (resolution) used by openvdb ConfigOptionFloat hollowing_quality; - - // Indirectly controls the amount of filtering used to blur geometry - // features in the created cavity. - ConfigOptionFloat hollowing_flatness; + + // Indirectly controls the minimum size of created cavities. + ConfigOptionFloat hollowing_closing_distance; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -1144,7 +1143,7 @@ protected: OPT_PTR(hollowing_enable); OPT_PTR(hollowing_min_thickness); OPT_PTR(hollowing_quality); - OPT_PTR(hollowing_flatness); + OPT_PTR(hollowing_closing_distance); } }; diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 4309003134..c3993f2c89 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -1,55 +1,122 @@ -#include "Hollowing.hpp" -#include -#include -#include #include +//#include +#include + +#include "Hollowing.hpp" +#include + namespace Slic3r { namespace sla { -namespace { +//namespace { -void filter_grid_sla(openvdb::FloatGrid &grid, double scale, double /*thickness*/, double flatness) -{ - static const double ROUNDNESS_COEFF = 1.; +//void filter_grid_sla(openvdb::FloatGrid::Ptr &grid, double scale, double /*thickness*/, double flatness, double closing_dist) +//{ +// static const double ROUNDNESS_COEFF = 1.; - // Filtering: - if (flatness > 0.) { - double rounding = ROUNDNESS_COEFF * flatness; - int width = int(rounding * scale); - int count = 1; - openvdb::tools::Filter{grid}.gaussian(width, count); +// // Filtering: +// if (flatness > 0.) { +// double rounding = ROUNDNESS_COEFF * flatness; +// int width = int(rounding * scale); +// int count = 1; +// openvdb::tools::Filter{*grid}.gaussian(width, count); +// } +//} + +//} + +template> +inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } + +template> +inline void _scale(S s, sla::Contour3D &m) +{ + for (auto &p : m.points) p *= s; +} + +template +remove_cvref_t _grid_to_mesh(const openvdb::FloatGrid &grid, + double isosurf, + double adapt); + +template<> +TriangleMesh _grid_to_mesh(const openvdb::FloatGrid &grid, + double isosurf, + double adapt) +{ + return grid_to_mesh(grid, isosurf, adapt); +} + +template<> +sla::Contour3D _grid_to_mesh(const openvdb::FloatGrid &grid, + double isosurf, + double adapt) +{ + return grid_to_contour3d(grid, isosurf, adapt); +} + +template +remove_cvref_t _generate_interior(Mesh &&mesh, + double min_thickness, + double voxel_scale, + double closing_dist) +{ +// namespace plc = std::placeholders; +// auto filt = std::bind(redist_grid_sla, plc::_1, plc::_2, plc::_3, flatness, closing_dist); +// return hollowed_interior(mesh, min_thickness, quality, filt); + + using MMesh = remove_cvref_t; + MMesh imesh{std::forward(mesh)}; + + _scale(voxel_scale, imesh); + + double offset = voxel_scale * min_thickness; + double D = voxel_scale * closing_dist; + float out_range = 0.1f * float(offset); + float in_range = 1.1f * float(offset + D); + auto gridptr = mesh_to_grid(imesh, {}, out_range, in_range); + + assert(gridptr); + + if (!gridptr) { + BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; + return MMesh{}; } -} -// openvdb::tools::levelSetRebuild(grid, -float(thickness * 2)); -// filter_grid_sla(grid, scale, thickness, flatness); - -// openvdb::tools::levelSetRebuild(grid, float(thickness)); - - -void redist_grid_sla(openvdb::FloatGrid &grid, double scale, double thickness, double flatness) -{ -// openvdb::tools::levelSetRebuild(grid, -float(scale * thickness)); + if (closing_dist > .0) { + gridptr = redistance_grid(*gridptr, -(offset + D), double(in_range)); + } else { + D = -offset; + } - openvdb::tools::LevelSetFilter filt{grid}; +// openvdb::tools::Filter filt{*gridptr}; +// filt.offset(float(offset + D)); -// filt.gaussian(int(flatness * scale)); + double iso_surface = D; + double adaptivity = 0.; + auto omesh = _grid_to_mesh(*gridptr, iso_surface, adaptivity); -// openvdb::tools::levelSetRebuild(grid, float(scale * thickness)); - //grid = openvdb::tools::topologyToLevelSet(grid); -} - + _scale(1. / voxel_scale, omesh); + + return omesh; } TriangleMesh generate_interior(const TriangleMesh &mesh, double min_thickness, double quality, - double flatness) + double closing_dist) { - namespace plc = std::placeholders; - auto filt = std::bind(filter_grid_sla, plc::_1, plc::_2, plc::_3, flatness); - return hollowed_interior(mesh, min_thickness, quality, filt); + static const double MAX_OVERSAMPL = 7.; + + // I can't figure out how to increase the grid resolution through openvdb API + // so the model will be scaled up before conversion and the result scaled + // down. Voxels have a unit size. If I set voxelSize smaller, it scales + // the whole geometry down, and doesn't increase the number of voxels. + // + // max 8x upscale, min is native voxel size + auto voxel_scale = (1.0 + MAX_OVERSAMPL * quality); + return _generate_interior(mesh, min_thickness, voxel_scale, closing_dist); } }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index b150a4f768..3273cbcfed 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -776,10 +776,10 @@ void SLAPrint::process() po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); double thickness = po.m_config.hollowing_min_thickness.getFloat(); - double accuracy = po.m_config.hollowing_quality.getFloat(); - double blur = po.m_config.hollowing_flatness.getFloat(); + double quality = po.m_config.hollowing_quality.getFloat(); + double closing_d = po.m_config.hollowing_closing_distance.getFloat(); po.m_hollowing_data->interior = - generate_interior(po.transformed_mesh(), thickness, accuracy, blur); + generate_interior(po.transformed_mesh(), thickness, quality, closing_d); if (po.m_hollowing_data->interior.empty()) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; @@ -1755,7 +1755,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectortext(" "); // vertical gap - m_imgui->text("Offset: "); ImGui::SameLine(); ImGui::SliderFloat(" ", &m_offset, 0.f, 10.f, "%.1f"); @@ -763,10 +762,9 @@ RENDER_AGAIN: ImGui::SameLine(); ImGui::SliderFloat(" ", &m_accuracy, 0.f, 1.f, "%.1f"); - // TODO: only in expert mode: - m_imgui->text("Smoothness: "); + m_imgui->text("Closing distance: "); ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_smoothness, 0.f, 1.f, "%.1f"); + ImGui::SliderFloat(" ", &m_closing_d, 0.f, 20.f, "%.1f"); } else { // not in editing mode: m_imgui->text(m_desc.at("minimal_distance")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 0cf1e1ecb1..642f7f5a28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -109,18 +109,15 @@ private: std::vector m_normal_cache; // to restore after discarding changes or undo/redo float m_offset = 2.0f; - + float m_accuracy = 0.5f; + float m_closing_d = 2.f; + float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; - float m_accuracy = 0.5f; - // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; - - - float m_smoothness = 0.5f; GLSelectionRectangle m_selection_rectangle; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 6f511fdec4..3b5a2f9852 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -499,7 +499,7 @@ const std::vector& Preset::sla_print_options() "hollowing_enable", "hollowing_min_thickness", "hollowing_quality", - "hollowing_flatness", + "hollowing_closing_distance", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 333384802a..6f17abea44 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3569,7 +3569,7 @@ void TabSLAPrint::build() optgroup->append_single_option_line("hollowing_enable"); optgroup->append_single_option_line("hollowing_min_thickness"); optgroup->append_single_option_line("hollowing_quality"); - optgroup->append_single_option_line("hollowing_flatness"); + optgroup->append_single_option_line("hollowing_closing_distance"); page = add_options_page(_(L("Advanced")), "wrench"); optgroup = page->new_optgroup(_(L("Slicing"))); From b4795e12920ea8f20b796a1f9b3d068b53a03c6f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 8 Nov 2019 20:18:14 +0100 Subject: [PATCH 031/130] Unified igl calls in MeshRaycaster and EigenMesh3D MeshRaycaster is still aware of the clipping plane but it now uses EigenMesh3D internally Public interface of both classes is unchanged --- src/libslic3r/SLA/SLACommon.hpp | 3 + src/libslic3r/SLA/SLASupportTreeIGL.cpp | 25 ++++++ src/slic3r/GUI/MeshUtils.cpp | 105 ++++-------------------- src/slic3r/GUI/MeshUtils.hpp | 10 +-- 4 files changed, 51 insertions(+), 92 deletions(-) diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 6e56b6de75..0a3ee311b3 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -189,6 +189,9 @@ public: // Casting a ray on the mesh, returns the distance where the hit occures. hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + // Casts a ray on the mesh and returns all hits + std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; + class si_result { double m_value; int m_fidx; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 03d4a563c0..ea2be6be11 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -272,6 +272,31 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return ret; } +std::vector +EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const +{ + std::vector outs; + std::vector hits; + m_aabb->intersect_ray(m_V, m_F, s, dir, hits); + + // The sort is necessary, the hits are not always sorted. + std::sort(hits.begin(), hits.end(), + [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); + + // Convert the igl::Hit into hit_result + outs.reserve(hits.size()); + for (const igl::Hit& hit : hits) { + outs.emplace_back(EigenMesh3D::hit_result(*this)); + outs.back().m_t = double(hit.t); + outs.back().m_dir = dir; + outs.back().m_source = s; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) + outs.back().m_face_id = hit.id; + } + + return outs; +} + #ifdef SLIC3R_SLA_NEEDS_WINDTREE EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { double sign = 0; double sqdst = 0; int i = 0; Vec3d c; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 62a6813a66..8b37161e9d 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -5,10 +5,6 @@ #include "slic3r/GUI/Camera.hpp" -// There is an L function in igl that would be overridden by our localization macro. -#undef L -#include - #include @@ -99,59 +95,6 @@ void MeshClipper::recalculate_triangles() } -class MeshRaycaster::AABBWrapper { -public: - AABBWrapper(const TriangleMesh* mesh); - ~AABBWrapper() { m_AABB.deinit(); } - - typedef Eigen::Map> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - igl::AABB m_AABB; - - Vec3f get_hit_pos(const igl::Hit& hit) const; - Vec3f get_hit_normal(const igl::Hit& hit) const; - -private: - const TriangleMesh* m_mesh; -}; - -MeshRaycaster::AABBWrapper::AABBWrapper(const TriangleMesh* mesh) - : m_mesh(mesh) -{ - m_AABB.init( - MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3)); -} - - -MeshRaycaster::MeshRaycaster(const TriangleMesh& mesh) - : m_AABB_wrapper(new AABBWrapper(&mesh)), m_mesh(&mesh) -{ -} - -MeshRaycaster::~MeshRaycaster() -{ - delete m_AABB_wrapper; -} - -Vec3f MeshRaycaster::AABBWrapper::get_hit_pos(const igl::Hit& hit) const -{ - const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; - return Vec3f((1-hit.u-hit.v) * m_mesh->its.vertices[indices(0)] - + hit.u * m_mesh->its.vertices[indices(1)] - + hit.v * m_mesh->its.vertices[indices(2)]); -} - - -Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const -{ - const stl_triangle_vertex_indices& indices = m_mesh->its.indices[hit.id]; - Vec3f a(m_mesh->its.vertices[indices(1)] - m_mesh->its.vertices[indices(0)]); - Vec3f b(m_mesh->its.vertices[indices(2)] - m_mesh->its.vertices[indices(0)]); - return Vec3f(a.cross(b)); -} - - bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const { @@ -164,27 +107,20 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 0., model_mat.data(), proj_mat.data(), viewport.data(), &pt1(0), &pt1(1), &pt1(2)); ::gluUnProject(mouse_pos(0), viewport[3] - mouse_pos(1), 1., model_mat.data(), proj_mat.data(), viewport.data(), &pt2(0), &pt2(1), &pt2(2)); - std::vector hits; - Transform3d inv = trafo.inverse(); - pt1 = inv * pt1; pt2 = inv * pt2; - if (! m_AABB_wrapper->m_AABB.intersect_ray( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - pt1.cast(), (pt2-pt1).cast(), hits)) + std::vector hits = m_emesh.query_ray_hits(pt1, pt2-pt1); + if (hits.empty()) return false; // no intersection found - std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - unsigned i = 0; // Remove points that are obscured or cut by the clipping plane if (clipping_plane) { for (i=0; iis_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast())) + if (! clipping_plane->is_point_clipped(trafo * hits[i].position())) break; if (i==hits.size() || (hits.size()-i) % 2 != 0) { @@ -195,8 +131,8 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& } // Now stuff the points in the provided vector and calculate normals if asked about them: - position = m_AABB_wrapper->get_hit_pos(hits[i]); - normal = m_AABB_wrapper->get_hit_normal(hits[i]); + position = hits[i].position().cast(); + normal = hits[i].normal().cast(); return true; } @@ -220,24 +156,21 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo bool is_obscured = false; // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; + std::vector hits; // Offset the start of the ray by EPSILON to account for numerical inaccuracies. - if (m_AABB_wrapper->m_AABB.intersect_ray( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) { + hits = m_emesh.query_ray_hits((inverse_trafo * pt + direction_to_camera_mesh * EPSILON).cast(), + direction_to_camera.cast()); - std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; }); + if (! hits.empty()) { // 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: - if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f) + if (hits.front().normal().dot(direction_to_camera_mesh.cast()) > 0) is_obscured = true; // Eradicate all hits that the caller wants to ignore for (unsigned j=0; jis_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast())) { + if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * hits[j].position())) { hits.erase(hits.begin()+j); --j; } @@ -258,17 +191,15 @@ std::vector MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const { int idx = 0; - Eigen::Matrix closest_point; - m_AABB_wrapper->m_AABB.squared_distance( - AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3), - AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3), - point, idx, closest_point); + Vec3d closest_point; + m_emesh.squared_distance(point.cast(), idx, closest_point); if (normal) { - igl::Hit imag_hit; - imag_hit.id = idx; - *normal = m_AABB_wrapper->get_hit_normal(imag_hit); + const stl_triangle_vertex_indices& indices = m_mesh->its.indices[idx]; + Vec3f a(m_mesh->its.vertices[indices(1)] - m_mesh->its.vertices[indices(0)]); + Vec3f b(m_mesh->its.vertices[indices(2)] - m_mesh->its.vertices[indices(0)]); + *normal = Vec3f(a.cross(b)); } - return closest_point; + return closest_point.cast(); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index e4c4c20d22..8dae2a2f00 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -3,6 +3,7 @@ #include "libslic3r/Point.hpp" #include "libslic3r/Geometry.hpp" +#include "libslic3r/SLA/SLACommon.hpp" #include @@ -93,8 +94,9 @@ private: class MeshRaycaster { public: - MeshRaycaster(const TriangleMesh& mesh); - ~MeshRaycaster(); + MeshRaycaster(const TriangleMesh& mesh) + : m_mesh(&mesh), m_emesh(mesh) + {} void set_transformation(const Geometry::Transformation& trafo); void set_camera(const Camera& camera); @@ -107,9 +109,7 @@ public: Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; private: - // PIMPL wrapper around igl::AABB so I don't have to include the header-only IGL here - class AABBWrapper; - AABBWrapper* m_AABB_wrapper; + sla::EigenMesh3D m_emesh; const TriangleMesh* m_mesh = nullptr; }; From 35ba7a481cbc38f13fd5777c40412feb7fb4feaf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 11 Nov 2019 13:56:05 +0100 Subject: [PATCH 032/130] adding precompiled header should probably be the last statement. --- src/libslic3r/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c2f3115910..f6934ac8e2 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -216,10 +216,6 @@ add_library(libslic3r STATIC encoding_check(libslic3r) -if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) - add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) -endif () - target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNEST2D_INCLUDES} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(libslic3r @@ -252,3 +248,7 @@ endif() if(SLIC3R_PROFILE) target_link_libraries(slic3r Shiny) endif() + +if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) + add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) +endif () From 60650d0dfc0cc99e8ab7bef2ea469bc4825d242e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 11 Nov 2019 16:10:46 +0100 Subject: [PATCH 033/130] Progress indication for hollowing gizmo. --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 39 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 5 ++- src/slic3r/GUI/Plater.cpp | 49 ++++++++++++++++++++----- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index e48c6f8056..0516052361 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -571,30 +571,35 @@ std::pair GLGizmoHollow::get_hollowi return std::make_pair(m_mesh, sla::HollowingConfig{double(m_offset), double(m_accuracy), double(m_closing_d)}); } +void GLGizmoHollow::set_hollowing_result(std::unique_ptr mesh) +{ + // Called from Plater when the UI job finishes + m_cavity_mesh = std::move(mesh); + + m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); + m_object_clipper.reset(); + m_volume_with_cavity.reset(); + + if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside + Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); + volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); + + m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); + } +} + void GLGizmoHollow::hollow_mesh() { // Trigger a UI job to hollow the mesh. wxGetApp().plater()->hollow(); } -void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr mesh) +void GLGizmoHollow::update_hollowed_mesh() { - // Called from Plater when the UI job finishes - m_cavity_mesh = std::move(mesh); - - m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); - m_object_clipper.reset(); - m_volume_with_cavity.reset(); - - if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside - Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); - volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); - m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); - m_volume_with_cavity->finalize_geometry(true); - m_volume_with_cavity->set_volume_transformation(volume_trafo); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[m_active_instance]->get_transformation()); - } + if (m_volume_with_cavity) m_volume_with_cavity->finalize_geometry(true); m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 321be0dfbc..cea7a5245d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -76,8 +76,11 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(bool force = false); ClippingPlane get_sla_clipping_plane() const; - void update_hollowed_mesh(std::unique_ptr mesh); + + std::pair get_hollowing_parameters() const; + void set_hollowing_result(std::unique_ptr mesh); + void update_hollowed_mesh(); bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a92f9de5f9..d52f21bb24 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1714,6 +1714,9 @@ struct Plater::priv void process() override; void finalize() override; private: + GLGizmoHollow * get_gizmo(); + const GLGizmoHollow * get_gizmo() const; + std::unique_ptr m_output; const TriangleMesh* m_object_mesh = nullptr; sla::HollowingConfig m_cfg; @@ -2873,22 +2876,50 @@ void Plater::priv::HollowJob::prepare() void Plater::priv::HollowJob::process() { sla::JobController ctl; + ctl.stopcondition = [this]{ return was_canceled(); }; + ctl.statuscb = [this](unsigned st, const std::string &s) { + if (st < 100) update_status(int(st), s); + }; TriangleMesh omesh = sla::generate_interior(*m_object_mesh, m_cfg, ctl); - if (omesh.empty()) return; - - m_output.reset(new TriangleMesh{*m_object_mesh}); - m_output->merge(omesh); - m_output->require_shared_vertices(); + if (!omesh.empty()) { + m_output.reset(new TriangleMesh{*m_object_mesh}); + m_output->merge(omesh); + m_output->require_shared_vertices(); + + update_status(90, "Rendering hollowed object"); + + auto gizmo = get_gizmo(); + if (gizmo) gizmo->set_hollowing_result(std::move(m_output)); + + update_status(100, was_canceled() ? _(L("Hollowing cancelled.")) : + _(L("Hollowing done."))); + } else { + update_status(100, _(L("Hollowing failed."))); + } } void Plater::priv::HollowJob::finalize() +{ + auto gizmo = get_gizmo(); + if (gizmo) gizmo->update_hollowed_mesh(); +} + +GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() { const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); - GLGizmoHollow* gizmo_hollow = dynamic_cast(gizmo_manager.get_current()); - assert(gizmo_hollow); - gizmo_hollow->update_hollowed_mesh(std::move(m_output)); + auto ret = dynamic_cast(gizmo_manager.get_current()); + assert(ret); + return ret; +} + +const GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() const +{ + const GLGizmosManager& gizmo_manager = plater().q->canvas3D()->get_gizmos_manager(); + auto ret = dynamic_cast(gizmo_manager.get_current()); + assert(ret); + return ret; } void Plater::priv::split_object() @@ -2896,7 +2927,7 @@ void Plater::priv::split_object() int obj_idx = get_selected_object_idx(); if (obj_idx == -1) return; - + // we clone model object because split_object() adds the split volumes // into the same model object, thus causing duplicates when we call load_model_objects() Model new_model = model; From a69e80b9878b7eec13351f5166b29008525ae0fa Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 11 Nov 2019 17:27:32 +0100 Subject: [PATCH 034/130] Fix non thread-safe data flow between plater and hollowing gizmo. --- src/libslic3r/SLA/Hollowing.cpp | 4 --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 33 ++++++++++++------------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 4 +-- src/slic3r/GUI/Plater.cpp | 22 +++++++++-------- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 11fe0cb5b3..b224bc98c1 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -3,7 +3,6 @@ #include #include -//#include #include #include @@ -87,9 +86,6 @@ remove_cvref_t _generate_interior(Mesh &&mesh, if (ctl.stopcondition()) return {}; else ctl.statuscb(70, L("Hollowing")); -// openvdb::tools::Filter filt{*gridptr}; -// filt.offset(float(offset + D)); - double iso_surface = D; double adaptivity = 0.; auto omesh = _grid_to_mesh(*gridptr, iso_surface, adaptivity); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 0516052361..cbdadc4759 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -571,24 +571,11 @@ std::pair GLGizmoHollow::get_hollowi return std::make_pair(m_mesh, sla::HollowingConfig{double(m_offset), double(m_accuracy), double(m_closing_d)}); } -void GLGizmoHollow::set_hollowing_result(std::unique_ptr mesh) +void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) { - // Called from Plater when the UI job finishes - m_cavity_mesh = std::move(mesh); - - m_mesh_raycaster.reset(new MeshRaycaster(*m_cavity_mesh.get())); + m_mesh_raycaster = std::move(rc); m_object_clipper.reset(); m_volume_with_cavity.reset(); - - if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside - Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); - volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); - m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); - - m_volume_with_cavity->set_volume_transformation(volume_trafo); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); - } } void GLGizmoHollow::hollow_mesh() @@ -597,9 +584,21 @@ void GLGizmoHollow::hollow_mesh() wxGetApp().plater()->hollow(); } -void GLGizmoHollow::update_hollowed_mesh() +void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) { - if (m_volume_with_cavity) m_volume_with_cavity->finalize_geometry(true); + // Called from Plater when the UI job finishes + m_cavity_mesh = std::move(mesh); + + if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside + Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); + volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); + m_volume_with_cavity->finalize_geometry(true); + m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); + } + m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index cea7a5245d..290a793407 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -79,8 +79,8 @@ public: std::pair get_hollowing_parameters() const; - void set_hollowing_result(std::unique_ptr mesh); - void update_hollowed_mesh(); + void update_mesh_raycaster(std::unique_ptr &&rc); + void update_hollowed_mesh(std::unique_ptr &&mesh); bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d52f21bb24..1d4926d92b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1717,7 +1717,8 @@ struct Plater::priv GLGizmoHollow * get_gizmo(); const GLGizmoHollow * get_gizmo() const; - std::unique_ptr m_output; + std::unique_ptr m_output_mesh; + std::unique_ptr m_output_raycaster; const TriangleMesh* m_object_mesh = nullptr; sla::HollowingConfig m_cfg; }; @@ -2870,7 +2871,7 @@ void Plater::priv::HollowJob::prepare() auto hlw_data = gizmo_hollow->get_hollowing_parameters(); m_object_mesh = hlw_data.first; m_cfg = hlw_data.second; - m_output.reset(); + m_output_mesh.reset(); } void Plater::priv::HollowJob::process() @@ -2884,14 +2885,13 @@ void Plater::priv::HollowJob::process() TriangleMesh omesh = sla::generate_interior(*m_object_mesh, m_cfg, ctl); if (!omesh.empty()) { - m_output.reset(new TriangleMesh{*m_object_mesh}); - m_output->merge(omesh); - m_output->require_shared_vertices(); + m_output_mesh.reset(new TriangleMesh{*m_object_mesh}); + m_output_mesh->merge(omesh); + m_output_mesh->require_shared_vertices(); - update_status(90, "Rendering hollowed object"); + update_status(90, _(L("Indexing hollowed object"))); - auto gizmo = get_gizmo(); - if (gizmo) gizmo->set_hollowing_result(std::move(m_output)); + m_output_raycaster.reset(new MeshRaycaster(*m_output_mesh)); update_status(100, was_canceled() ? _(L("Hollowing cancelled.")) : _(L("Hollowing done."))); @@ -2902,8 +2902,10 @@ void Plater::priv::HollowJob::process() void Plater::priv::HollowJob::finalize() { - auto gizmo = get_gizmo(); - if (gizmo) gizmo->update_hollowed_mesh(); + if (auto gizmo = get_gizmo()) { + gizmo->update_mesh_raycaster(std::move(m_output_raycaster)); + gizmo->update_hollowed_mesh(std::move(m_output_mesh)); + } } GLGizmoHollow *Plater::priv::HollowJob::get_gizmo() From dfa4a58dc695a692adf987af5949d1ed3cb70d88 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 12 Nov 2019 10:28:00 +0100 Subject: [PATCH 035/130] Bump up C++ to 14 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7829fc9a9..4d23556a7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,7 +102,7 @@ list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) enable_testing () # Enable C++11 language standard. -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT WIN32) From b6edd5ddb9e7971a0334db6ce66c4ae3d37cfaf6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Nov 2019 11:48:12 +0100 Subject: [PATCH 036/130] SLA support points gizmo renders the holes and does not allow to place a support point in them --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 25 ++++----- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 58 +++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index cbdadc4759..909907a88f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -290,21 +290,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * drain_hole.m_normal.cast()); + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - - const double cylinder_radius = double(drain_hole.m_radius) * RenderPointScale; //0.25; // mm - const double stick_out_length = 1.; - const double cone_height = m_new_hole_height + stick_out_length; glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -cone_height+stick_out_length)); - ::gluCylinder(m_quadric, cylinder_radius, cylinder_radius, cone_height, 24, 1); - glsafe(::glTranslated(0., 0., cone_height)); - ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); - glsafe(::glTranslated(0., 0., -cone_height)); + glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.m_height)); + ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.m_height)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, cylinder_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); glsafe(::glPopMatrix()); if (vol->is_left_handed()) @@ -434,7 +430,8 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first, pos_and_normal.second, m_new_hole_radius, m_new_hole_height); + m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, + -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); m_selected.push_back(false); assert(m_selected.size == m_model_object->sla_drain_holes.size()); m_parent.set_as_dirty(); @@ -561,8 +558,8 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first; - m_model_object->sla_drain_holes[m_hover_id].m_normal = pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].m_normal = -pos_and_normal.second; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 290a793407..428a978c48 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -28,7 +28,7 @@ private: mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); - const float RenderPointScale = 1.f; + const float HoleStickOutLength = 1.f; GLUquadricObj* m_quadric; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 000ddf8c95..a7ad02cd8d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -327,6 +327,43 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive)); } + // Now render the drain holes: + render_color[0] = 0.7f; + render_color[1] = 0.7f; + render_color[2] = 0.7f; + render_color[3] = 0.7f; + glsafe(::glColor4fv(render_color)); + for (const sla::DrainHole& drain_hole : m_model_object->sla_drain_holes) { + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + + if (vol->is_left_handed()) + glFrontFace(GL_CW); + + // Matrices set, we can render the point mark now. + + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.m_height)); + ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + glsafe(::glPopMatrix()); + + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + + } + if (!picking) glsafe(::glDisable(GL_LIGHTING)); @@ -375,6 +412,24 @@ void GLGizmoSlaSupports::update_mesh() } +bool GLGizmoSlaSupports::is_point_in_hole(const Vec3f& pt) const +{ + auto squared_distance_from_line = [](const Vec3f pt, const Vec3f& line_pt, const Vec3f& dir) -> float { + Vec3f diff = line_pt - pt; + return (diff - diff.dot(dir) * dir).squaredNorm(); + }; + + + for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { + if ( hole.m_normal.dot(pt-hole.m_pos) < EPSILON + || hole.m_normal.dot(pt-(hole.m_pos+hole.m_height * hole.m_normal)) > 0.f) + continue; + if ( squared_distance_from_line(pt, hole.m_pos, hole.m_normal) < pow(hole.m_radius, 2.f)) + return true; + } + + return false; +} // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. @@ -393,7 +448,8 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get()) + && ! is_point_in_hole(hit)) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 85835f9d0e..e536aab40f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -126,6 +126,7 @@ private: std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; + bool is_point_in_hole(const Vec3f& pt) const; //void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, From 73ae73348434b110da1a09a83e9d152bb8afda8c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Nov 2019 15:04:40 +0100 Subject: [PATCH 037/130] New icon for the hollowing gizmo --- resources/icons/hollow.svg | 104 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 7 -- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/resources/icons/hollow.svg b/resources/icons/hollow.svg index 119fb6afcc..13e96ada96 100644 --- a/resources/icons/hollow.svg +++ b/resources/icons/hollow.svg @@ -1,42 +1,80 @@ - + - + - + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 909907a88f..c6c02947f1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -748,13 +748,6 @@ RENDER_AGAIN: if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); - if (m_imgui->button("?")) { - wxGetApp().CallAfter([]() { - SlaGizmoHelpDialog help_dlg; - help_dlg.ShowModal(); - }); - } - if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); force_refresh = true; From 4e067c42f03bac019066849401fb42f70cacda87 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 12 Nov 2019 16:53:47 +0100 Subject: [PATCH 038/130] SLAPrint steps moved to separate module. * Lambdas replaced with class methods --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/SLA/Common.cpp | 235 +++---- src/libslic3r/SLA/Hollowing.cpp | 26 +- src/libslic3r/SLA/Hollowing.hpp | 21 +- src/libslic3r/SLA/RasterWriter.cpp | 2 + src/libslic3r/SLA/RasterWriter.hpp | 8 +- src/libslic3r/SLAPrint.cpp | 1017 +++------------------------- src/libslic3r/SLAPrint.hpp | 50 +- src/libslic3r/SLAPrintSteps.cpp | 848 +++++++++++++++++++++++ src/libslic3r/SLAPrintSteps.hpp | 68 ++ src/slic3r/GUI/Plater.cpp | 7 +- tests/libslic3r/test_hollowing.cpp | 6 +- 12 files changed, 1214 insertions(+), 1076 deletions(-) create mode 100644 src/libslic3r/SLAPrintSteps.cpp create mode 100644 src/libslic3r/SLAPrintSteps.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index f6934ac8e2..0d483d9979 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -150,6 +150,8 @@ add_library(libslic3r STATIC ShortestPath.cpp ShortestPath.hpp SLAPrint.cpp + SLAPrintSteps.cpp + SLAPrintSteps.hpp SLAPrint.hpp Slicing.cpp Slicing.hpp diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index f85731603f..caabdd7554 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -1,11 +1,13 @@ #include #include +#include #include #include #include #include #include + // Workaround: IGL signed_distance.h will define PI in the igl namespace. #undef PI @@ -351,128 +353,131 @@ PointSet normals(const PointSet& points, std::function thr, // throw on cancel const std::vector& pt_indices) { - if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) + if (points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) return {}; - + std::vector range = pt_indices; - if(range.empty()) { + if (range.empty()) { range.resize(size_t(points.rows()), 0); std::iota(range.begin(), range.end(), 0); } - - PointSet ret(range.size(), 3); - + + PointSet ret(range.size(), 3); + // for (size_t ridx = 0; ridx < range.size(); ++ridx) - tbb::parallel_for(size_t(0), range.size(), - [&ret, &range, &mesh, &points, thr, eps](size_t ridx) - { - thr(); - auto eidx = Eigen::Index(range[ridx]); - int faceid = 0; - Vec3d p; - - mesh.squared_distance(points.row(eidx), faceid, p); - - auto trindex = mesh.F().row(faceid); - - const Vec3d& p1 = mesh.V().row(trindex(0)); - const Vec3d& p2 = mesh.V().row(trindex(1)); - const Vec3d& p3 = mesh.V().row(trindex(2)); - - // We should check if the point lies on an edge of the hosting triangle. - // If it does then all the other triangles using the same two points - // have to be searched and the final normal should be some kind of - // aggregation of the participating triangle normals. We should also - // consider the cases where the support point lies right on a vertex - // of its triangle. The procedure is the same, get the neighbor - // triangles and calculate an average normal. - - // mark the vertex indices of the edge. ia and ib marks and edge ic - // will mark a single vertex. - int ia = -1, ib = -1, ic = -1; - - if(std::abs(distance(p, p1)) < eps) { - ic = trindex(0); - } - else if(std::abs(distance(p, p2)) < eps) { - ic = trindex(1); - } - else if(std::abs(distance(p, p3)) < eps) { - ic = trindex(2); - } - else if(point_on_edge(p, p1, p2, eps)) { - ia = trindex(0); ib = trindex(1); - } - else if(point_on_edge(p, p2, p3, eps)) { - ia = trindex(1); ib = trindex(2); - } - else if(point_on_edge(p, p1, p3, eps)) { - ia = trindex(0); ib = trindex(2); - } - - // vector for the neigboring triangles including the detected one. - std::vector neigh; - if(ic >= 0) { // The point is right on a vertex of the triangle - for(int n = 0; n < mesh.F().rows(); ++n) { - thr(); - Vec3i ni = mesh.F().row(n); - if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) - neigh.emplace_back(ni); - } - } - else if(ia >= 0 && ib >= 0) { // the point is on and edge - // now get all the neigboring triangles - for(int n = 0; n < mesh.F().rows(); ++n) { - thr(); - Vec3i ni = mesh.F().row(n); - if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && - (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) - neigh.emplace_back(ni); - } - } - - // Calculate the normals for the neighboring triangles - std::vector neighnorms; neighnorms.reserve(neigh.size()); - for(const Vec3i& tri : neigh) { - const Vec3d& pt1 = mesh.V().row(tri(0)); - const Vec3d& pt2 = mesh.V().row(tri(1)); - const Vec3d& pt3 = mesh.V().row(tri(2)); - Eigen::Vector3d U = pt2 - pt1; - Eigen::Vector3d V = pt3 - pt1; - neighnorms.emplace_back(U.cross(V).normalized()); - } - - // Throw out duplicates. They would cause trouble with summing. We will - // use std::unique which works on sorted ranges. We will sort by the - // coefficient-wise sum of the normals. It should force the same - // elements to be consecutive. - std::sort(neighnorms.begin(), neighnorms.end(), - [](const Vec3d& v1, const Vec3d& v2){ - return v1.sum() < v2.sum(); - }); - - auto lend = std::unique(neighnorms.begin(), neighnorms.end(), - [](const Vec3d& n1, const Vec3d& n2) { - // Compare normals for equivalence. This is controvers stuff. - auto deq = [](double a, double b) { return std::abs(a-b) < 1e-3; }; - return deq(n1(X), n2(X)) && deq(n1(Y), n2(Y)) && deq(n1(Z), n2(Z)); - }); - - if(!neighnorms.empty()) { // there were neighbors to count with - // sum up the normals and then normalize the result again. - // This unification seems to be enough. - Vec3d sumnorm(0, 0, 0); - sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); - sumnorm.normalize(); - ret.row(long(ridx)) = sumnorm; - } - else { // point lies safely within its triangle - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - ret.row(long(ridx)) = U.cross(V).normalized(); - } + ccr::enumerate( + range.begin(), range.end(), + [&ret, &mesh, &points, thr, eps](unsigned el, size_t ridx) { + thr(); + auto eidx = Eigen::Index(el); + int faceid = 0; + Vec3d p; + + mesh.squared_distance(points.row(eidx), faceid, p); + + auto trindex = mesh.F().row(faceid); + + const Vec3d &p1 = mesh.V().row(trindex(0)); + const Vec3d &p2 = mesh.V().row(trindex(1)); + const Vec3d &p3 = mesh.V().row(trindex(2)); + + // We should check if the point lies on an edge of the hosting + // triangle. If it does then all the other triangles using the + // same two points have to be searched and the final normal should + // be some kind of aggregation of the participating triangle + // normals. We should also consider the cases where the support + // point lies right on a vertex of its triangle. The procedure is + // the same, get the neighbor triangles and calculate an average + // normal. + + // mark the vertex indices of the edge. ia and ib marks and edge + // ic will mark a single vertex. + int ia = -1, ib = -1, ic = -1; + + if (std::abs(distance(p, p1)) < eps) { + ic = trindex(0); + } else if (std::abs(distance(p, p2)) < eps) { + ic = trindex(1); + } else if (std::abs(distance(p, p3)) < eps) { + ic = trindex(2); + } else if (point_on_edge(p, p1, p2, eps)) { + ia = trindex(0); + ib = trindex(1); + } else if (point_on_edge(p, p2, p3, eps)) { + ia = trindex(1); + ib = trindex(2); + } else if (point_on_edge(p, p1, p3, eps)) { + ia = trindex(0); + ib = trindex(2); + } + + // vector for the neigboring triangles including the detected one. + std::vector neigh; + if (ic >= 0) { // The point is right on a vertex of the triangle + for (int n = 0; n < mesh.F().rows(); ++n) { + thr(); + Vec3i ni = mesh.F().row(n); + if ((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) + neigh.emplace_back(ni); + } + } else if (ia >= 0 && ib >= 0) { // the point is on and edge + // now get all the neigboring triangles + for (int n = 0; n < mesh.F().rows(); ++n) { + thr(); + Vec3i ni = mesh.F().row(n); + if ((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && + (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) + neigh.emplace_back(ni); + } + } + + // Calculate the normals for the neighboring triangles + std::vector neighnorms; + neighnorms.reserve(neigh.size()); + for (const Vec3i &tri : neigh) { + const Vec3d & pt1 = mesh.V().row(tri(0)); + const Vec3d & pt2 = mesh.V().row(tri(1)); + const Vec3d & pt3 = mesh.V().row(tri(2)); + Eigen::Vector3d U = pt2 - pt1; + Eigen::Vector3d V = pt3 - pt1; + neighnorms.emplace_back(U.cross(V).normalized()); + } + + // Throw out duplicates. They would cause trouble with summing. We + // will use std::unique which works on sorted ranges. We will sort + // by the coefficient-wise sum of the normals. It should force the + // same elements to be consecutive. + std::sort(neighnorms.begin(), neighnorms.end(), + [](const Vec3d &v1, const Vec3d &v2) { + return v1.sum() < v2.sum(); }); - + + auto lend = std::unique(neighnorms.begin(), neighnorms.end(), + [](const Vec3d &n1, const Vec3d &n2) { + // Compare normals for equivalence. + // This is controvers stuff. + auto deq = [](double a, double b) { + return std::abs(a - b) < 1e-3; + }; + return deq(n1(X), n2(X)) && + deq(n1(Y), n2(Y)) && + deq(n1(Z), n2(Z)); + }); + + if (!neighnorms.empty()) { // there were neighbors to count with + // sum up the normals and then normalize the result again. + // This unification seems to be enough. + Vec3d sumnorm(0, 0, 0); + sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); + sumnorm.normalize(); + ret.row(long(ridx)) = sumnorm; + } else { // point lies safely within its triangle + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(long(ridx)) = U.cross(V).normalized(); + } + }); + return ret; } diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index b224bc98c1..c3763dbc9b 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include #include @@ -98,18 +100,30 @@ remove_cvref_t _generate_interior(Mesh &&mesh, return omesh; } -TriangleMesh generate_interior(const TriangleMesh &mesh, const HollowingConfig &hc, const JobController &ctl) +std::unique_ptr generate_interior(const TriangleMesh & mesh, + const HollowingConfig &hc, + const JobController & ctl) { static const double MAX_OVERSAMPL = 7.; - // I can't figure out how to increase the grid resolution through openvdb API - // so the model will be scaled up before conversion and the result scaled - // down. Voxels have a unit size. If I set voxelSize smaller, it scales - // the whole geometry down, and doesn't increase the number of voxels. + // I can't figure out how to increase the grid resolution through openvdb + // API so the model will be scaled up before conversion and the result + // scaled down. Voxels have a unit size. If I set voxelSize smaller, it + // scales the whole geometry down, and doesn't increase the number of + // voxels. // // max 8x upscale, min is native voxel size auto voxel_scale = (1.0 + MAX_OVERSAMPL * hc.quality); - return _generate_interior(mesh, ctl, hc.min_thickness, voxel_scale, hc.closing_distance); + return std::make_unique( + _generate_interior(mesh, ctl, hc.min_thickness, voxel_scale, + hc.closing_distance)); +} + +bool DrainHole::operator==(const DrainHole &sp) const +{ + return (m_pos == sp.m_pos) && (m_normal == sp.m_normal) && + is_approx(m_radius, sp.m_radius) && + is_approx(m_height, sp.m_height); } }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 93a1f90fde..cb5c2bd15e 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -1,10 +1,14 @@ #ifndef SLA_HOLLOWING_HPP #define SLA_HOLLOWING_HPP -#include +#include +#include #include namespace Slic3r { + +class TriangleMesh; + namespace sla { struct HollowingConfig @@ -33,24 +37,19 @@ struct DrainHole , m_height(height) {} - bool operator==(const DrainHole &sp) const - { - return (m_pos == sp.m_pos) && (m_normal == sp.m_normal) && - is_approx(m_radius, sp.m_radius) && - is_approx(m_height, sp.m_height); - } + bool operator==(const DrainHole &sp) const; bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } - template void serialize(Archive &ar) + template inline void serialize(Archive &ar) { ar(m_pos, m_normal, m_radius, m_height); } }; -TriangleMesh generate_interior(const TriangleMesh &mesh, - const HollowingConfig & = {}, - const JobController &ctl = {}); +std::unique_ptr generate_interior(const TriangleMesh &mesh, + const HollowingConfig & = {}, + const JobController &ctl = {}); } } diff --git a/src/libslic3r/SLA/RasterWriter.cpp b/src/libslic3r/SLA/RasterWriter.cpp index b3da0d2a5a..0d55b769d5 100644 --- a/src/libslic3r/SLA/RasterWriter.cpp +++ b/src/libslic3r/SLA/RasterWriter.cpp @@ -1,4 +1,6 @@ #include + +#include "libslic3r/PrintConfig.hpp" #include #include diff --git a/src/libslic3r/SLA/RasterWriter.hpp b/src/libslic3r/SLA/RasterWriter.hpp index a7792d55da..62ed44ca85 100644 --- a/src/libslic3r/SLA/RasterWriter.hpp +++ b/src/libslic3r/SLA/RasterWriter.hpp @@ -9,11 +9,13 @@ #include #include -#include "libslic3r/PrintConfig.hpp" - #include -namespace Slic3r { namespace sla { +namespace Slic3r { + +class DynamicPrintConfig; + +namespace sla { // API to write the zipped sla output layers and metadata. // Implementation uses PNG raster output. diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 05440ad528..53a0e82233 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,8 +1,6 @@ #include "SLAPrint.hpp" -#include "SLA/SupportTree.hpp" -#include "SLA/Pad.hpp" -#include "SLA/SupportPointGenerator.hpp" -#include "SLA/Hollowing.hpp" +#include "SLAPrintSteps.hpp" + #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "MTUtils.hpp" @@ -14,9 +12,6 @@ #include #include -// For geometry algorithms with native Clipper types (no copies and conversions) -#include - // #define SLAPRINT_DO_BENCHMARK #ifdef SLAPRINT_DO_BENCHMARK @@ -33,78 +28,86 @@ namespace Slic3r { -class SLAPrintObject::SupportData : public sla::SupportableMesh + +bool is_zero_elevation(const SLAPrintObjectConfig &c) { -public: - sla::SupportTree::UPtr support_tree_ptr; // the supports - std::vector support_slices; // sliced supports + return c.pad_enable.getBool() && c.pad_around_object.getBool(); +} + +// Compile the argument for support creation from the static print config. +sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) +{ + sla::SupportConfig scfg; - inline SupportData(const TriangleMesh &t): sla::SupportableMesh{t, {}, {}} {} + scfg.enabled = c.supports_enable.getBool(); + scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); + scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat(); + scfg.head_penetration_mm = c.support_head_penetration.getFloat(); + scfg.head_width_mm = c.support_head_width.getFloat(); + scfg.object_elevation_mm = is_zero_elevation(c) ? + 0. : c.support_object_elevation.getFloat(); + scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ; + scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); + scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); + switch(c.support_pillar_connection_mode.getInt()) { + case slapcmZigZag: + scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break; + case slapcmCross: + scfg.pillar_connection_mode = sla::PillarConnectionMode::cross; break; + case slapcmDynamic: + scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break; + } + scfg.ground_facing_only = c.support_buildplate_only.getBool(); + scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); + scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); + scfg.base_height_mm = c.support_base_height.getFloat(); + scfg.pillar_base_safety_distance_mm = + c.support_base_safety_distance.getFloat() < EPSILON ? + scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); - sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl) - { - support_tree_ptr = sla::SupportTree::create(*this, ctl); - return support_tree_ptr; - } -}; + return scfg; +} -class SLAPrintObject::HollowingData +sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { -public: + sla::PadConfig::EmbedObject ret; - TriangleMesh interior; - // std::vector -}; - -namespace { - -// should add up to 100 (%) -const std::array OBJ_STEP_LEVELS = -{ - 5, // slaposHollowing, - 20, // slaposObjectSlice, - 5, // slaposDrillHolesIfHollowed - 20, // slaposSupportPoints, - 10, // slaposSupportTree, - 10, // slaposPad, - 30, // slaposSliceSupports, -}; - -// Object step to status label. The labels are localized at the time of calling, thus supporting language switching. -std::string OBJ_STEP_LABELS(size_t idx) -{ - switch (idx) { - case slaposHollowing: return L("Hollowing out the model"); - case slaposObjectSlice: return L("Slicing model"); - case slaposDrillHolesIfHollowed: return L("Drilling holes into hollowed model."); - case slaposSupportPoints: return L("Generating support points"); - case slaposSupportTree: return L("Generating support tree"); - case slaposPad: return L("Generating pad"); - case slaposSliceSupports: return L("Slicing supports"); - default:; + ret.enabled = is_zero_elevation(c); + + if(ret.enabled) { + ret.everywhere = c.pad_around_object_everywhere.getBool(); + ret.object_gap_mm = c.pad_object_gap.getFloat(); + ret.stick_width_mm = c.pad_object_connector_width.getFloat(); + ret.stick_stride_mm = c.pad_object_connector_stride.getFloat(); + ret.stick_penetration_mm = c.pad_object_connector_penetration + .getFloat(); } - assert(false); - return "Out of bounds!"; -}; + + return ret; +} -// Should also add up to 100 (%) -const std::array PRINT_STEP_LEVELS = +sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { - 10, // slapsMergeSlicesAndEval - 90, // slapsRasterize -}; + sla::PadConfig pcfg; + + pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); + pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; + + pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat(); + pcfg.wall_height_mm = c.pad_wall_height.getFloat(); + pcfg.brim_size_mm = c.pad_brim_size.getFloat(); + + // set builtin pad implicitly ON + pcfg.embed_object = builtin_pad_cfg(c); + + return pcfg; +} -// Print step to status label. The labels are localized at the time of calling, thus supporting language switching. -std::string PRINT_STEP_LABELS(size_t idx) +bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg) { - switch (idx) { - case slapsMergeSlicesAndEval: return L("Merging slices and calculating statistics"); - case slapsRasterize: return L("Rasterizing layers"); - default:; - } - assert(false); return "Out of bounds!"; -}; - + // An empty pad can only be created if embed_object mode is enabled + // and the pad is not forced everywhere + return !pad.empty() || (pcfg.embed_object.enabled && !pcfg.embed_object.everywhere); } void SLAPrint::clear() @@ -590,87 +593,6 @@ std::string SLAPrint::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config); } -namespace { - -bool is_zero_elevation(const SLAPrintObjectConfig &c) { - return c.pad_enable.getBool() && c.pad_around_object.getBool(); -} - -// Compile the argument for support creation from the static print config. -sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) { - sla::SupportConfig scfg; - - scfg.enabled = c.supports_enable.getBool(); - scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); - scfg.head_back_radius_mm = 0.5*c.support_pillar_diameter.getFloat(); - scfg.head_penetration_mm = c.support_head_penetration.getFloat(); - scfg.head_width_mm = c.support_head_width.getFloat(); - scfg.object_elevation_mm = is_zero_elevation(c) ? - 0. : c.support_object_elevation.getFloat(); - scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ; - scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); - scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); - switch(c.support_pillar_connection_mode.getInt()) { - case slapcmZigZag: - scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break; - case slapcmCross: - scfg.pillar_connection_mode = sla::PillarConnectionMode::cross; break; - case slapcmDynamic: - scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break; - } - scfg.ground_facing_only = c.support_buildplate_only.getBool(); - scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); - scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); - scfg.base_height_mm = c.support_base_height.getFloat(); - scfg.pillar_base_safety_distance_mm = - c.support_base_safety_distance.getFloat() < EPSILON ? - scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); - - return scfg; -} - -sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { - sla::PadConfig::EmbedObject ret; - - ret.enabled = is_zero_elevation(c); - - if(ret.enabled) { - ret.everywhere = c.pad_around_object_everywhere.getBool(); - ret.object_gap_mm = c.pad_object_gap.getFloat(); - ret.stick_width_mm = c.pad_object_connector_width.getFloat(); - ret.stick_stride_mm = c.pad_object_connector_stride.getFloat(); - ret.stick_penetration_mm = c.pad_object_connector_penetration - .getFloat(); - } - - return ret; -} - -sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { - sla::PadConfig pcfg; - - pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); - pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; - - pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat(); - pcfg.wall_height_mm = c.pad_wall_height.getFloat(); - pcfg.brim_size_mm = c.pad_brim_size.getFloat(); - - // set builtin pad implicitly ON - pcfg.embed_object = builtin_pad_cfg(c); - - return pcfg; -} - -bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg) -{ - // An empty pad can only be created if embed_object mode is enabled - // and the pad is not forced everywhere - return !pad.empty() || (pcfg.embed_object.enabled && !pcfg.embed_object.everywhere); -} - -} - std::string SLAPrint::validate() const { for(SLAPrintObject * po : m_objects) { @@ -740,775 +662,12 @@ bool SLAPrint::invalidate_step(SLAPrintStep step) void SLAPrint::process() { - using namespace sla; - using ExPolygon = Slic3r::ExPolygon; - if(m_objects.empty()) return; // Assumption: at this point the print objects should be populated only with // the model objects we have to process and the instances are also filtered - - // shortcut to initial layer height - double ilhd = m_material_config.initial_layer_height.getFloat(); - auto ilh = float(ilhd); - - coord_t ilhs = scaled(ilhd); - const size_t objcount = m_objects.size(); - - 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 - const double ostepd = (max_objstatus - min_objstatus) / (objcount * 100.0); - auto hollow_model = [](SLAPrintObject &po) { - - if (!po.m_config.hollowing_enable.getBool()) { - BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; - po.m_hollowing_data.reset(); - return; - } else { - BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; - } - - if (!po.m_hollowing_data) - po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); - - double thickness = po.m_config.hollowing_min_thickness.getFloat(); - double quality = po.m_config.hollowing_quality.getFloat(); - double closing_d = po.m_config.hollowing_closing_distance.getFloat(); - po.m_hollowing_data->interior = - generate_interior(po.transformed_mesh(), {thickness, quality, closing_d}); - - if (po.m_hollowing_data->interior.empty()) - BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; - }; - - // The slicing will be performed on an imaginary 1D grid which starts from - // the bottom of the bounding box created around the supported model. So - // the first layer which is usually thicker will be part of the supports - // not the model geometry. Exception is when the model is not in the air - // (elevation is zero) and no pad creation was requested. In this case the - // model geometry starts on the ground level and the initial layer is part - // of it. In any case, the model and the supports have to be sliced in the - // same imaginary grid (the height vector argument to TriangleMeshSlicer). - - // 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](SLAPrintObject& po) { - - TriangleMesh hollowed_mesh; - - bool is_hollowing = po.m_config.hollowing_enable.getBool() && - po.m_hollowing_data; - - if (is_hollowing) { - hollowed_mesh = po.transformed_mesh(); - hollowed_mesh.merge(po.m_hollowing_data->interior); - hollowed_mesh.require_shared_vertices(); - } - - const TriangleMesh &mesh = is_hollowing ? hollowed_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); - coord_t lhs = scaled(lhd); - auto && bb3d = mesh.bounding_box(); - double minZ = bb3d.min(Z) - po.get_elevation(); - double maxZ = bb3d.max(Z); - auto minZf = float(minZ); - coord_t minZs = scaled(minZ); - coord_t 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, minZf + ilh / 2.f, ilh); - - for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) - po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); - - // Just get the first record that is from the model: - auto slindex_it = - po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); - - if(slindex_it == po.m_slice_index.end()) - //TRN To be shown at the status bar on SLA slicing error. - throw std::runtime_error( - L("Slicing had to be stopped due to an internal error: " - "Inconsistent slice index.")); - - po.m_model_height_levels.clear(); - po.m_model_height_levels.reserve(po.m_slice_index.size()); - for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) - po.m_model_height_levels.emplace_back(it->slice_level()); - - TriangleMeshSlicer slicer(&mesh); - - po.m_model_slices.clear(); - slicer.slice(po.m_model_height_levels, - float(po.config().slice_closing_radius.value), - &po.m_model_slices, - [this](){ throw_if_canceled(); }); - - auto mit = slindex_it; - double doffs = m_printer_config.absolute_correction.getFloat(); - coord_t clpr_offs = scaled(doffs); - for(size_t id = 0; - id < po.m_model_slices.size() && mit != po.m_slice_index.end(); - id++) - { - // We apply the printer correction offset here. - if(clpr_offs != 0) - po.m_model_slices[id] = - offset_ex(po.m_model_slices[id], float(clpr_offs)); - - mit->set_model_slice_idx(po, id); ++mit; - } - - if(po.m_config.supports_enable.getBool() || - po.m_config.pad_enable.getBool()) - { - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh()) ); - } - }; - - // In this step we check the slices, identify island and cover them with - // support points. Then we sprinkle the rest of the mesh. - auto support_points = [this, ostepd](SLAPrintObject& po) { - // If supports are disabled, we can skip the model scan. - if(!po.m_config.supports_enable.getBool()) return; - - if (!po.m_supportdata) - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh())); - - const ModelObject& mo = *po.m_model_object; - - BOOST_LOG_TRIVIAL(debug) << "Support point count " - << mo.sla_support_points.size(); - - // Unless the user modified the points or we already did the calculation, we will do - // the autoplacement. Otherwise we will just blindly copy the frontend data - // into the backend cache. - if (mo.sla_points_status != sla::PointsStatus::UserModified) { - - // Hypothetical use of the slice index: - // auto bb = po.transformed_mesh().bounding_box(); - // auto range = po.get_slice_records(bb.min(Z)); - // std::vector heights; heights.reserve(range.size()); - // for(auto& record : range) heights.emplace_back(record.slice_level()); - - // calculate heights of slices (slices are calculated already) - const std::vector& heights = po.m_model_height_levels; - - this->throw_if_canceled(); - SupportPointGenerator::Config config; - const SLAPrintObjectConfig& cfg = po.config(); - - // the density config value is in percents: - config.density_relative = float(cfg.support_points_density_relative / 100.f); - config.minimal_distance = float(cfg.support_points_minimal_distance); - config.head_diameter = float(cfg.support_head_front_diameter); - - // scaling for the sub operations - double d = ostepd * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; - double init = m_report_status.status(); - - auto statuscb = [this, d, init](unsigned st) - { - double current = init + st * d; - if(std::round(m_report_status.status()) < std::round(current)) - m_report_status(*this, current, - OBJ_STEP_LABELS(slaposSupportPoints)); - - }; - - // Construction of this object does the calculation. - this->throw_if_canceled(); - SupportPointGenerator auto_supports( - po.m_supportdata->emesh, po.get_model_slices(), heights, - config, [this]() { throw_if_canceled(); }, statuscb); - - // Now let's extract the result. - const std::vector& points = auto_supports.output(); - this->throw_if_canceled(); - po.m_supportdata->pts = points; - - BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " - << po.m_supportdata->pts.size(); - - // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass - // the update status to GLGizmoSlaSupports - m_report_status(*this, - -1, - L("Generating support points"), - SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); - } - else { - // There are either some points on the front-end, or the user - // removed them on purpose. No calculation will be done. - po.m_supportdata->pts = po.transformed_support_points(); - } - - // If the zero elevation mode is engaged, we have to filter out all the - // points that are on the bottom of the object - if (is_zero_elevation(po.config())) { - double tolerance = po.config().pad_enable.getBool() - ? po.m_config.pad_wall_thickness.getFloat() - : po.m_config.support_base_height.getFloat(); - - remove_bottom_points(po.m_supportdata->pts, - po.m_supportdata->emesh.ground_level(), - tolerance); - } - }; - - // In this step we create the supports - auto support_tree = [this, ostepd](SLAPrintObject& po) - { - if(!po.m_supportdata) return; - - sla::PadConfig pcfg = make_pad_cfg(po.m_config); - - if (pcfg.embed_object) - po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); - - po.m_supportdata->cfg = make_support_cfg(po.m_config); - - // scaling for the sub operations - double d = ostepd * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; - double init = m_report_status.status(); - JobController ctl; - - ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { - double current = init + st * d; - if (std::round(m_report_status.status()) < std::round(current)) - m_report_status(*this, current, - OBJ_STEP_LABELS(slaposSupportTree), - SlicingStatus::DEFAULT, logmsg); - }; - ctl.stopcondition = [this]() { return canceled(); }; - ctl.cancelfn = [this]() { throw_if_canceled(); }; - - po.m_supportdata->create_support_tree(ctl); - - if (!po.m_config.supports_enable.getBool()) return; - - throw_if_canceled(); - - // Create the unified mesh - auto rc = SlicingStatus::RELOAD_SCENE; - - // This is to prevent "Done." being displayed during merged_mesh() - m_report_status(*this, -1, L("Visualizing supports")); - - BOOST_LOG_TRIVIAL(debug) << "Processed support point count " - << po.m_supportdata->pts.size(); - - // Check the mesh for later troubleshooting. - if(po.support_mesh().empty()) - BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; - - m_report_status(*this, -1, L("Visualizing supports"), rc); - }; - - // This step generates the sla base pad - auto generate_pad = [this](SLAPrintObject& po) { - // this step can only go after the support tree has been created - // and before the supports had been sliced. (or the slicing has to be - // repeated) - - if(po.m_config.pad_enable.getBool()) - { - // Get the distilled pad configuration from the config - sla::PadConfig pcfg = make_pad_cfg(po.m_config); - - ExPolygons bp; // This will store the base plate of the pad. - double pad_h = pcfg.full_height(); - const TriangleMesh &trmesh = po.transformed_mesh(); - - // This call can get pretty time consuming - auto thrfn = [this](){ throw_if_canceled(); }; - - if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { - // No support (thus no elevation) or zero elevation mode - // we sometimes call it "builtin pad" is enabled so we will - // get a sample from the bottom of the mesh and use it for pad - // creation. - sla::pad_blueprint(trmesh, bp, float(pad_h), - float(po.m_config.layer_height.getFloat()), - thrfn); - } - - po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); - auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(MeshType::Pad); - - if (!validate_pad(pad_mesh, pcfg)) - throw std::runtime_error( - L("No pad can be generated for this model with the " - "current configuration")); - - } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { - po.m_supportdata->support_tree_ptr->remove_pad(); - } - - po.throw_if_canceled(); - auto rc = SlicingStatus::RELOAD_SCENE; - m_report_status(*this, -1, L("Visualizing supports"), rc); - }; - - // Slicing the support geometries similarly to the model slicing procedure. - // If the pad had been added previously (see step "base_pool" than it will - // be part of the slices) - auto slice_supports = [this](SLAPrintObject& po) { - auto& sd = po.m_supportdata; - - if(sd) sd->support_slices.clear(); - - // Don't bother if no supports and no pad is present. - if (!po.m_config.supports_enable.getBool() && - !po.m_config.pad_enable.getBool()) - return; - - if(sd && sd->support_tree_ptr) { - - std::vector heights; heights.reserve(po.m_slice_index.size()); - - for(auto& rec : po.m_slice_index) { - heights.emplace_back(rec.slice_level()); - } - - sd->support_slices = sd->support_tree_ptr->slice( - heights, float(po.config().slice_closing_radius.value)); - } - - double doffs = m_printer_config.absolute_correction.getFloat(); - 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], float(clpr_offs)); - - po.m_slice_index[i].set_support_slice_idx(po, i); - } - - // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update - // status to the 3D preview to load the SLA slices. - m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); - }; - - // Merging the slices from all the print objects into one slice grid and - // calculating print statistics from the merge result. - auto merge_slices_and_eval_stats = [this, ilhs]() { - - // clear the rasterizer input - m_printer_input.clear(); - - size_t mx = 0; - for(SLAPrintObject * o : m_objects) { - if(auto m = o->get_slice_index().size() > mx) mx = m; - } - - m_printer_input.reserve(mx); - - auto eps = coord_t(SCALED_EPSILON); - - for(SLAPrintObject * o : m_objects) { - coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; - - for(const SliceRecord& slicerecord : o->get_slice_index()) { - coord_t lvlid = slicerecord.print_level() - gndlvl; - - // Neat trick to round the layer levels to the grid. - lvlid = eps * (lvlid / eps); - - auto it = std::lower_bound(m_printer_input.begin(), - m_printer_input.end(), - PrintLayer(lvlid)); - - if(it == m_printer_input.end() || it->level() != lvlid) - it = m_printer_input.insert(it, PrintLayer(lvlid)); - - - it->add(slicerecord); - } - } - - m_print_statistics.clear(); - - using ClipperPoint = ClipperLib::IntPoint; - using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d - using ClipperPolygons = std::vector; - namespace sl = libnest2d::shapelike; // For algorithms - - // Set up custom union and diff functions for clipper polygons - auto polyunion = [] (const ClipperPolygons& subjects) - { - ClipperLib::Clipper clipper; - - bool closed = true; - - for(auto& path : subjects) { - clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); - } - - auto mode = ClipperLib::pftPositive; - - return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); - }; - - auto polydiff = [](const ClipperPolygons& subjects, const ClipperPolygons& clips) - { - ClipperLib::Clipper clipper; - - bool closed = true; - - for(auto& path : subjects) { - clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); - } - - for(auto& path : clips) { - clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); - clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); - } - - auto mode = ClipperLib::pftPositive; - - return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); - }; - - // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise - auto areafn = [](const ClipperPolygon& poly) { return - sl::area(poly); }; - - const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); - const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; - const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0; - - const double init_exp_time = m_material_config.initial_exposure_time.getFloat(); - const double exp_time = m_material_config.exposure_time.getFloat(); - - const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] - - const auto width = scaled(m_printer_config.display_width.getFloat()); - const auto height = scaled(m_printer_config.display_height.getFloat()); - const double display_area = width*height; - - // get polygons for all instances in the object - auto get_all_polygons = - [](const ExPolygons& input_polygons, - const std::vector& instances, - bool is_lefthanded) - { - ClipperPolygons polygons; - polygons.reserve(input_polygons.size() * instances.size()); - - for (const ExPolygon& polygon : input_polygons) { - if(polygon.contour.empty()) continue; - - for (size_t i = 0; i < instances.size(); ++i) - { - ClipperPolygon poly; - - // We need to reverse if is_lefthanded is true but - bool needreverse = is_lefthanded; - - // should be a move - poly.Contour.reserve(polygon.contour.size() + 1); - - auto& cntr = polygon.contour.points; - if(needreverse) - for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) - poly.Contour.emplace_back(it->x(), it->y()); - else - for(auto& p : cntr) - poly.Contour.emplace_back(p.x(), p.y()); - - for(auto& h : polygon.holes) { - poly.Holes.emplace_back(); - auto& hole = poly.Holes.back(); - hole.reserve(h.points.size() + 1); - - if(needreverse) - for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) - hole.emplace_back(it->x(), it->y()); - else - for(auto& p : h.points) - hole.emplace_back(p.x(), p.y()); - } - - if(is_lefthanded) { - for(auto& p : poly.Contour) p.X = -p.X; - for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; - } - - sl::rotate(poly, double(instances[i].rotation)); - sl::translate(poly, ClipperPoint{instances[i].shift(X), - instances[i].shift(Y)}); - - polygons.emplace_back(std::move(poly)); - } - } - return polygons; - }; - - double supports_volume(0.0); - double models_volume(0.0); - - double estim_time(0.0); - - size_t slow_layers = 0; - size_t fast_layers = 0; - - const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); - double fade_layer_time = init_exp_time; - - SpinMutex mutex; - using Lock = std::lock_guard; - - // Going to parallel: - auto printlayerfn = [this, - // functions and read only vars - get_all_polygons, polyunion, polydiff, areafn, - area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, - - // write vars - &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, - &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) - { - PrintLayer& layer = m_printer_input[sliced_layer_cnt]; - - // vector of slice record references - auto& slicerecord_references = layer.slices(); - - if(slicerecord_references.empty()) return; - - // Layer height should match for all object slices for a given level. - const auto l_height = double(slicerecord_references.front().get().layer_height()); - - // Calculation of the consumed material - - ClipperPolygons model_polygons; - ClipperPolygons supports_polygons; - - 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(), - size_t(0), - [](size_t a, const SliceRecord &sr) { - return a + sr.get_slice(soModel).size(); - }); - - supports_polygons.reserve(c); - - for(const SliceRecord& record : layer.slices()) { - const SLAPrintObject *po = record.print_obj(); - - const ExPolygons &modelslices = record.get_slice(soModel); - - bool is_lefth = record.print_obj()->is_left_handed(); - if (!modelslices.empty()) { - ClipperPolygons v = get_all_polygons(modelslices, po->instances(), is_lefth); - for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp)); - } - - const ExPolygons &supportslices = record.get_slice(soSupport); - - if (!supportslices.empty()) { - ClipperPolygons v = get_all_polygons(supportslices, po->instances(), is_lefth); - for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp)); - } - } - - model_polygons = polyunion(model_polygons); - double layer_model_area = 0; - for (const ClipperPolygon& polygon : model_polygons) - layer_model_area += areafn(polygon); - - if (layer_model_area < 0 || layer_model_area > 0) { - Lock lck(mutex); models_volume += layer_model_area * l_height; - } - - if(!supports_polygons.empty()) { - if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); - else supports_polygons = polydiff(supports_polygons, model_polygons); - // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType - } - - double layer_support_area = 0; - for (const ClipperPolygon& polygon : supports_polygons) - layer_support_area += areafn(polygon); - - if (layer_support_area < 0 || layer_support_area > 0) { - Lock lck(mutex); supports_volume += layer_support_area * l_height; - } - - // Here we can save the expensively calculated polygons for printing - ClipperPolygons trslices; - trslices.reserve(model_polygons.size() + supports_polygons.size()); - for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); - for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); - - layer.transformed_slices(polyunion(trslices)); - - // Calculation of the slow and fast layers to the future controlling those values on FW - - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; - const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - - { Lock lck(mutex); - if (is_fast_layer) - fast_layers++; - else - slow_layers++; - - - // Calculation of the printing time - - if (sliced_layer_cnt < 3) - estim_time += init_exp_time; - else if (fade_layer_time > exp_time) - { - fade_layer_time -= delta_fade_time; - estim_time += fade_layer_time; - } - else - estim_time += exp_time; - - estim_time += tilt_time; - } - }; - - // sequential version for debugging: - // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); - tbb::parallel_for(0, m_printer_input.size(), printlayerfn); - - 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 - if (m_printer_input.size() == 0) - m_print_statistics.estimated_print_time = std::nan(""); - else - m_print_statistics.estimated_print_time = estim_time; - - m_print_statistics.fast_layers_count = fast_layers; - m_print_statistics.slow_layers_count = slow_layers; - - m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); - }; - - // Rasterizing the model objects, and their supports - auto rasterize = [this]() { - if(canceled()) return; - - // Set up the printer, allocate space for all the layers - sla::RasterWriter &printer = init_printer(); - - auto lvlcnt = unsigned(m_printer_input.size()); - printer.layers(lvlcnt); - - // coefficient to map the rasterization state (0-99) to the allocated - // portion (slot) of the process state - double sd = (100 - max_objstatus) / 100.0; - - // slot is the portion of 100% that is realted to rasterization - unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; - - // pst: previous state - double pst = m_report_status.status(); - - double increment = (slot * sd) / m_printer_input.size(); - double dstatus = m_report_status.status(); - - SpinMutex slck; - - // procedure to process one height level. This will run in parallel - auto lvlfn = - [this, &slck, &printer, increment, &dstatus, &pst] - (unsigned level_id) - { - if(canceled()) return; - - PrintLayer& printlayer = m_printer_input[level_id]; - - // Switch to the appropriate layer in the printer - printer.begin_layer(level_id); - - for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) - printer.draw_polygon(poly, level_id); - - // Finish the layer for later saving it. - printer.finish_layer(level_id); - - // Status indication guarded with the spinlock - { - std::lock_guard lck(slck); - dstatus += increment; - double st = std::round(dstatus); - if(st > pst) { - m_report_status(*this, st, - PRINT_STEP_LABELS(slapsRasterize)); - pst = st; - } - } - }; - - // last minute escape - if(canceled()) return; - - // Sequential version (for testing) - // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l); - - // Print all the layers in parallel - tbb::parallel_for(0, lvlcnt, lvlfn); - - // Set statistics values to the printer - sla::RasterWriter::PrintStatistics stats; - stats.used_material = (m_print_statistics.objects_used_material + - m_print_statistics.support_used_material) / - 1000; - - int num_fade = m_default_object_config.faded_layers.getInt(); - stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0); - stats.num_fast = m_print_statistics.fast_layers_count; - stats.num_slow = m_print_statistics.slow_layers_count; - stats.estimated_print_time_s = m_print_statistics.estimated_print_time; - - m_printer->set_statistics(stats); - }; - - using slaposFn = std::function; - using slapsFn = std::function; - - slaposFn pobj_program[] = - { - hollow_model, slice_model, [](SLAPrintObject&){}, support_points, support_tree, generate_pad, slice_supports - }; + Steps printsteps{this}; // We want to first process all objects... std::vector level1_obj_steps = { @@ -1520,10 +679,9 @@ void SLAPrint::process() slaposSliceSupports }; - slapsFn print_program[] = { merge_slices_and_eval_stats, rasterize }; SLAPrintStep print_steps[] = { slapsMergeSlicesAndEval, slapsRasterize }; - - double st = min_objstatus; + + double st = Steps::min_objstatus; BOOST_LOG_TRIVIAL(info) << "Start slicing process."; @@ -1538,10 +696,10 @@ void SLAPrint::process() std::array step_times {}; auto apply_steps_on_objects = - [this, &st, ostepd, &pobj_program, &step_times, &bench] + [this, &st, &printsteps, &step_times, &bench] (const std::vector &steps) { - unsigned incr = 0; + double incr = 0; for (SLAPrintObject *po : m_objects) { for (SLAPrintObjectStep step : steps) { @@ -1551,19 +709,19 @@ void SLAPrint::process() // throws the canceled signal. throw_if_canceled(); - st += incr * ostepd; + st += incr; if (po->m_stepmask[step] && po->set_started(step)) { - m_report_status(*this, st, OBJ_STEP_LABELS(step)); + m_report_status(*this, st, printsteps.label(step)); bench.start(); - pobj_program[step](*po); + printsteps.execute(step, *po); bench.stop(); step_times[step] += bench.getElapsedSec(); throw_if_canceled(); po->set_done(step); } - incr = OBJ_STEP_LEVELS[step]; + incr = printsteps.progressrange(step); } } }; @@ -1573,23 +731,22 @@ void SLAPrint::process() // this would disable the rasterization step // std::fill(m_stepmask.begin(), m_stepmask.end(), false); - - double pstd = (100 - max_objstatus) / 100.0; - st = max_objstatus; + + st = Steps::max_objstatus; for(SLAPrintStep currentstep : print_steps) { throw_if_canceled(); if (m_stepmask[currentstep] && set_started(currentstep)) { - m_report_status(*this, st, PRINT_STEP_LABELS(currentstep)); + m_report_status(*this, st, printsteps.label(currentstep)); bench.start(); - print_program[currentstep](); + printsteps.execute(currentstep); bench.stop(); step_times[slaposCount + currentstep] += bench.getElapsedSec(); throw_if_canceled(); set_done(currentstep); } - st += PRINT_STEP_LEVELS[currentstep] * pstd; + st += printsteps.progressrange(currentstep); } // If everything vent well @@ -1598,10 +755,10 @@ void SLAPrint::process() #ifdef SLAPRINT_DO_BENCHMARK std::string csvbenchstr; for (size_t i = 0; i < size_t(slaposCount); ++i) - csvbenchstr += OBJ_STEP_LABELS(i) + ";"; + csvbenchstr += printsteps.label(SLAPrintObjectStep(i)) + ";"; for (size_t i = 0; i < size_t(slapsCount); ++i) - csvbenchstr += PRINT_STEP_LABELS(i) + ";"; + csvbenchstr += printsteps.label(SLAPrintStep(i)) + ";"; csvbenchstr += "\n"; for (double t : step_times) csvbenchstr += std::to_string(t) + ";"; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 6c8a6e26b9..2edede1099 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,8 +3,8 @@ #include #include "PrintBase.hpp" -//#include "PrintExport.hpp" #include "SLA/RasterWriter.hpp" +#include "SLA/SupportTree.hpp" #include "Point.hpp" #include "MTUtils.hpp" #include @@ -292,10 +292,33 @@ private: // Caching the transformed (m_trafo) raw mesh of the object mutable CachedObject m_transformed_rmesh; - class SupportData; + class SupportData : public sla::SupportableMesh + { + public: + sla::SupportTree::UPtr support_tree_ptr; // the supports + std::vector support_slices; // sliced supports + + inline SupportData(const TriangleMesh &t) + : sla::SupportableMesh{t, {}, {}} + {} + + sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl) + { + support_tree_ptr = sla::SupportTree::create(*this, ctl); + return support_tree_ptr; + } + }; + std::unique_ptr m_supportdata; - class HollowingData; + class HollowingData + { + public: + + TriangleMesh interior; + // std::vector + }; + std::unique_ptr m_hollowing_data; }; @@ -346,7 +369,9 @@ class SLAPrint : public PrintBaseWithState { private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; - + + class Steps; // See SLAPrintSteps.cpp + public: SLAPrint(): m_stepmask(slapsCount, true) {} @@ -402,8 +427,8 @@ public: template void transformed_slices(Container&& c) { m_transformed_slices = std::forward(c); } - - friend void SLAPrint::process(); + + friend class SLAPrint::Steps; public: @@ -479,6 +504,19 @@ private: friend SLAPrintObject; }; +// Helper functions: + +bool is_zero_elevation(const SLAPrintObjectConfig &c); + +sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c); + +sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c); + +sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c); + +bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg); + + } // namespace Slic3r #endif /* slic3r_SLAPrint_hpp_ */ diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp new file mode 100644 index 0000000000..61d00dff8d --- /dev/null +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -0,0 +1,848 @@ +#include + +#include +#include +#include + +#include + +// For geometry algorithms with native Clipper types (no copies and conversions) +#include + +#include + +#include "I18N.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + +namespace Slic3r { + +namespace { + +const std::array OBJ_STEP_LEVELS = { + 5, // slaposHollowing, + 20, // slaposObjectSlice, + 5, // slaposDrillHolesIfHollowed + 20, // slaposSupportPoints, + 10, // slaposSupportTree, + 10, // slaposPad, + 30, // slaposSliceSupports, +}; + +std::string OBJ_STEP_LABELS(size_t idx) +{ + switch (idx) { + case slaposHollowing: return L("Hollowing out the model"); + case slaposObjectSlice: return L("Slicing model"); + case slaposDrillHolesIfHollowed: return L("Drilling holes into hollowed model."); + case slaposSupportPoints: return L("Generating support points"); + case slaposSupportTree: return L("Generating support tree"); + case slaposPad: return L("Generating pad"); + case slaposSliceSupports: return L("Slicing supports"); + default:; + } + assert(false); + return "Out of bounds!"; +}; + +const std::array PRINT_STEP_LEVELS = { + 10, // slapsMergeSlicesAndEval + 90, // slapsRasterize +}; + +std::string PRINT_STEP_LABELS(size_t idx) +{ + switch (idx) { + case slapsMergeSlicesAndEval: return L("Merging slices and calculating statistics"); + case slapsRasterize: return L("Rasterizing layers"); + default:; + } + assert(false); return "Out of bounds!"; +}; + +} + +SLAPrint::Steps::Steps(SLAPrint *print) + : m_print{print} + , objcount{m_print->m_objects.size()} + , ilhd{m_print->m_material_config.initial_layer_height.getFloat()} + , ilh{float(ilhd)} + , ilhs{scaled(ilhd)} + , objectstep_scale{(max_objstatus - min_objstatus) / (objcount * 100.0)} +{} + +void SLAPrint::Steps::hollow_model(SLAPrintObject &po) +{ + + if (!po.m_config.hollowing_enable.getBool()) { + BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; + po.m_hollowing_data.reset(); + return; + } else { + BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; + } + + if (!po.m_hollowing_data) + po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + + double thickness = po.m_config.hollowing_min_thickness.getFloat(); + double quality = po.m_config.hollowing_quality.getFloat(); + double closing_d = po.m_config.hollowing_closing_distance.getFloat(); + sla::HollowingConfig hlwcfg{thickness, quality, closing_d}; + auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg); + if (meshptr) po.m_hollowing_data->interior = *meshptr; + + if (po.m_hollowing_data->interior.empty()) + BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; +} + +// The slicing will be performed on an imaginary 1D grid which starts from +// the bottom of the bounding box created around the supported model. So +// the first layer which is usually thicker will be part of the supports +// not the model geometry. Exception is when the model is not in the air +// (elevation is zero) and no pad creation was requested. In this case the +// model geometry starts on the ground level and the initial layer is part +// of it. In any case, the model and the supports have to be sliced in the +// same imaginary grid (the height vector argument to TriangleMeshSlicer). +void SLAPrint::Steps::slice_model(SLAPrintObject &po) +{ + + TriangleMesh hollowed_mesh; + + bool is_hollowing = po.m_config.hollowing_enable.getBool() && + po.m_hollowing_data; + + if (is_hollowing) { + hollowed_mesh = po.transformed_mesh(); + hollowed_mesh.merge(po.m_hollowing_data->interior); + hollowed_mesh.require_shared_vertices(); + } + + const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : + po.transformed_mesh(); + + // We need to prepare the slice index... + + double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); + float lh = float(lhd); + coord_t lhs = scaled(lhd); + auto && bb3d = mesh.bounding_box(); + double minZ = bb3d.min(Z) - po.get_elevation(); + double maxZ = bb3d.max(Z); + auto minZf = float(minZ); + coord_t minZs = scaled(minZ); + coord_t 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, minZf + ilh / 2.f, ilh); + + for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) + po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); + + // Just get the first record that is from the model: + auto slindex_it = + po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); + + if(slindex_it == po.m_slice_index.end()) + //TRN To be shown at the status bar on SLA slicing error. + throw std::runtime_error( + L("Slicing had to be stopped due to an internal error: " + "Inconsistent slice index.")); + + po.m_model_height_levels.clear(); + po.m_model_height_levels.reserve(po.m_slice_index.size()); + for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) + po.m_model_height_levels.emplace_back(it->slice_level()); + + TriangleMeshSlicer slicer(&mesh); + + po.m_model_slices.clear(); + slicer.slice(po.m_model_height_levels, + float(po.config().slice_closing_radius.value), + &po.m_model_slices, + [this](){ m_print->throw_if_canceled(); }); + + auto mit = slindex_it; + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); + coord_t clpr_offs = scaled(doffs); + for(size_t id = 0; + id < po.m_model_slices.size() && mit != po.m_slice_index.end(); + id++) + { + // We apply the printer correction offset here. + if(clpr_offs != 0) + po.m_model_slices[id] = + offset_ex(po.m_model_slices[id], float(clpr_offs)); + + mit->set_model_slice_idx(po, id); ++mit; + } + + if(po.m_config.supports_enable.getBool() || + po.m_config.pad_enable.getBool()) + { + po.m_supportdata.reset( + new SLAPrintObject::SupportData(po.transformed_mesh()) ); + } +} + +// In this step we check the slices, identify island and cover them with +// support points. Then we sprinkle the rest of the mesh. +void SLAPrint::Steps::support_points(SLAPrintObject &po) +{ + // If supports are disabled, we can skip the model scan. + if(!po.m_config.supports_enable.getBool()) return; + + if (!po.m_supportdata) + po.m_supportdata.reset( + new SLAPrintObject::SupportData(po.transformed_mesh())); + + const ModelObject& mo = *po.m_model_object; + + BOOST_LOG_TRIVIAL(debug) << "Support point count " + << mo.sla_support_points.size(); + + // Unless the user modified the points or we already did the calculation, + // we will do the autoplacement. Otherwise we will just blindly copy the + // frontend data into the backend cache. + if (mo.sla_points_status != sla::PointsStatus::UserModified) { + + // calculate heights of slices (slices are calculated already) + const std::vector& heights = po.m_model_height_levels; + + throw_if_canceled(); + sla::SupportPointGenerator::Config config; + const SLAPrintObjectConfig& cfg = po.config(); + + // the density config value is in percents: + config.density_relative = float(cfg.support_points_density_relative / 100.f); + config.minimal_distance = float(cfg.support_points_minimal_distance); + config.head_diameter = float(cfg.support_head_front_diameter); + + // scaling for the sub operations + double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; + double init = current_status(); + + auto statuscb = [this, d, init](unsigned st) + { + double current = init + st * d; + if(std::round(current_status()) < std::round(current)) + report_status(current, OBJ_STEP_LABELS(slaposSupportPoints)); + }; + + // Construction of this object does the calculation. + throw_if_canceled(); + sla::SupportPointGenerator auto_supports( + po.m_supportdata->emesh, po.get_model_slices(), heights, config, + [this]() { throw_if_canceled(); }, statuscb); + + // Now let's extract the result. + const std::vector& points = auto_supports.output(); + throw_if_canceled(); + po.m_supportdata->pts = points; + + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " + << po.m_supportdata->pts.size(); + + // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass + // the update status to GLGizmoSlaSupports + report_status(-1, L("Generating support points"), + SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); + } else { + // There are either some points on the front-end, or the user + // removed them on purpose. No calculation will be done. + po.m_supportdata->pts = po.transformed_support_points(); + } + + // If the zero elevation mode is engaged, we have to filter out all the + // points that are on the bottom of the object + if (is_zero_elevation(po.config())) { + double tolerance = po.config().pad_enable.getBool() ? + po.m_config.pad_wall_thickness.getFloat() : + po.m_config.support_base_height.getFloat(); + + remove_bottom_points(po.m_supportdata->pts, + po.m_supportdata->emesh.ground_level(), + tolerance); + } +} + +void SLAPrint::Steps::support_tree(SLAPrintObject &po) +{ + if(!po.m_supportdata) return; + + sla::PadConfig pcfg = make_pad_cfg(po.m_config); + + if (pcfg.embed_object) + po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); + + po.m_supportdata->cfg = make_support_cfg(po.m_config); + + // scaling for the sub operations + double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; + double init = current_status(); + sla::JobController ctl; + + ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { + double current = init + st * d; + if (std::round(current_status()) < std::round(current)) + report_status(current, OBJ_STEP_LABELS(slaposSupportTree), + SlicingStatus::DEFAULT, logmsg); + }; + ctl.stopcondition = [this]() { return canceled(); }; + ctl.cancelfn = [this]() { throw_if_canceled(); }; + + po.m_supportdata->create_support_tree(ctl); + + if (!po.m_config.supports_enable.getBool()) return; + + throw_if_canceled(); + + // Create the unified mesh + auto rc = SlicingStatus::RELOAD_SCENE; + + // This is to prevent "Done." being displayed during merged_mesh() + report_status(-1, L("Visualizing supports")); + + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " + << po.m_supportdata->pts.size(); + + // Check the mesh for later troubleshooting. + if(po.support_mesh().empty()) + BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; + + report_status(-1, L("Visualizing supports"), rc); +} + +void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { + // this step can only go after the support tree has been created + // and before the supports had been sliced. (or the slicing has to be + // repeated) + + if(po.m_config.pad_enable.getBool()) + { + // Get the distilled pad configuration from the config + sla::PadConfig pcfg = make_pad_cfg(po.m_config); + + ExPolygons bp; // This will store the base plate of the pad. + double pad_h = pcfg.full_height(); + const TriangleMesh &trmesh = po.transformed_mesh(); + + if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { + // No support (thus no elevation) or zero elevation mode + // we sometimes call it "builtin pad" is enabled so we will + // get a sample from the bottom of the mesh and use it for pad + // creation. + sla::pad_blueprint(trmesh, bp, float(pad_h), + float(po.m_config.layer_height.getFloat()), + [this](){ throw_if_canceled(); }); + } + + po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); + auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); + + if (!validate_pad(pad_mesh, pcfg)) + throw std::runtime_error( + L("No pad can be generated for this model with the " + "current configuration")); + + } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { + po.m_supportdata->support_tree_ptr->remove_pad(); + } + + throw_if_canceled(); + report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); +} + +// Slicing the support geometries similarly to the model slicing procedure. +// If the pad had been added previously (see step "base_pool" than it will +// be part of the slices) +void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { + auto& sd = po.m_supportdata; + + if(sd) sd->support_slices.clear(); + + // Don't bother if no supports and no pad is present. + if (!po.m_config.supports_enable.getBool() && + !po.m_config.pad_enable.getBool()) + return; + + if(sd && sd->support_tree_ptr) { + + std::vector heights; heights.reserve(po.m_slice_index.size()); + + for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); + + sd->support_slices = sd->support_tree_ptr->slice( + heights, float(po.config().slice_closing_radius.value)); + } + + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); + 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], float(clpr_offs)); + + po.m_slice_index[i].set_support_slice_idx(po, i); + } + + // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update + // status to the 3D preview to load the SLA slices. + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); +} + +using ClipperPoint = ClipperLib::IntPoint; +using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d +using ClipperPolygons = std::vector; + +static ClipperPolygons polyunion(const ClipperPolygons &subjects) +{ + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); +} + +static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) +{ + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + for(auto& path : clips) { + clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); +} + +// get polygons for all instances in the object +static ClipperPolygons get_all_polygons( + const ExPolygons & input_polygons, + const std::vector &instances, + bool is_lefthanded) +{ + namespace sl = libnest2d::sl; + + ClipperPolygons polygons; + polygons.reserve(input_polygons.size() * instances.size()); + + for (const ExPolygon& polygon : input_polygons) { + if(polygon.contour.empty()) continue; + + for (size_t i = 0; i < instances.size(); ++i) + { + ClipperPolygon poly; + + // We need to reverse if is_lefthanded is true but + bool needreverse = is_lefthanded; + + // should be a move + poly.Contour.reserve(polygon.contour.size() + 1); + + auto& cntr = polygon.contour.points; + if(needreverse) + for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) + poly.Contour.emplace_back(it->x(), it->y()); + else + for(auto& p : cntr) + poly.Contour.emplace_back(p.x(), p.y()); + + for(auto& h : polygon.holes) { + poly.Holes.emplace_back(); + auto& hole = poly.Holes.back(); + hole.reserve(h.points.size() + 1); + + if(needreverse) + for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) + hole.emplace_back(it->x(), it->y()); + else + for(auto& p : h.points) + hole.emplace_back(p.x(), p.y()); + } + + if(is_lefthanded) { + for(auto& p : poly.Contour) p.X = -p.X; + for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; + } + + sl::rotate(poly, double(instances[i].rotation)); + sl::translate(poly, ClipperPoint{instances[i].shift(X), + instances[i].shift(Y)}); + + polygons.emplace_back(std::move(poly)); + } + } + + return polygons; +} + +void SLAPrint::Steps::initialize_printer_input() +{ + auto &printer_input = m_print->m_printer_input; + + // clear the rasterizer input + printer_input.clear(); + + size_t mx = 0; + for(SLAPrintObject * o : m_print->m_objects) { + if(auto m = o->get_slice_index().size() > mx) mx = m; + } + + printer_input.reserve(mx); + + auto eps = coord_t(SCALED_EPSILON); + + for(SLAPrintObject * o : m_print->m_objects) { + coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; + + for(const SliceRecord& slicerecord : o->get_slice_index()) { + coord_t lvlid = slicerecord.print_level() - gndlvl; + + // Neat trick to round the layer levels to the grid. + lvlid = eps * (lvlid / eps); + + auto it = std::lower_bound(printer_input.begin(), + printer_input.end(), + PrintLayer(lvlid)); + + if(it == printer_input.end() || it->level() != lvlid) + it = printer_input.insert(it, PrintLayer(lvlid)); + + + it->add(slicerecord); + } + } +} + +// Merging the slices from all the print objects into one slice grid and +// calculating print statistics from the merge result. +void SLAPrint::Steps::merge_slices_and_eval_stats() { + + initialize_printer_input(); + + auto &print_statistics = m_print->m_print_statistics; + auto &printer_config = m_print->m_printer_config; + auto &material_config = m_print->m_material_config; + auto &printer_input = m_print->m_printer_input; + + print_statistics.clear(); + + // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise + auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); }; + + const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); + const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; + const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; + + const double init_exp_time = material_config.initial_exposure_time.getFloat(); + const double exp_time = material_config.exposure_time.getFloat(); + + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] + + const auto width = scaled(printer_config.display_width.getFloat()); + const auto height = scaled(printer_config.display_height.getFloat()); + const double display_area = width*height; + + double supports_volume(0.0); + double models_volume(0.0); + + double estim_time(0.0); + + size_t slow_layers = 0; + size_t fast_layers = 0; + + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); + double fade_layer_time = init_exp_time; + + SpinMutex mutex; + using Lock = std::lock_guard; + + // Going to parallel: + auto printlayerfn = [ + // functions and read only vars + areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, + + // write vars + &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, + &fast_layers, &fade_layer_time](PrintLayer& layer, size_t sliced_layer_cnt) + { + // vector of slice record references + auto& slicerecord_references = layer.slices(); + + if(slicerecord_references.empty()) return; + + // Layer height should match for all object slices for a given level. + const auto l_height = double(slicerecord_references.front().get().layer_height()); + + // Calculation of the consumed material + + ClipperPolygons model_polygons; + ClipperPolygons supports_polygons; + + 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(), + size_t(0), + [](size_t a, const SliceRecord &sr) { + return a + sr.get_slice(soModel).size(); + }); + + supports_polygons.reserve(c); + + for(const SliceRecord& record : layer.slices()) { + const SLAPrintObject *po = record.print_obj(); + + const ExPolygons &modelslices = record.get_slice(soModel); + + bool is_lefth = record.print_obj()->is_left_handed(); + if (!modelslices.empty()) { + ClipperPolygons v = get_all_polygons(modelslices, po->instances(), is_lefth); + for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp)); + } + + const ExPolygons &supportslices = record.get_slice(soSupport); + + if (!supportslices.empty()) { + ClipperPolygons v = get_all_polygons(supportslices, po->instances(), is_lefth); + for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp)); + } + } + + model_polygons = polyunion(model_polygons); + double layer_model_area = 0; + for (const ClipperPolygon& polygon : model_polygons) + layer_model_area += areafn(polygon); + + if (layer_model_area < 0 || layer_model_area > 0) { + Lock lck(mutex); models_volume += layer_model_area * l_height; + } + + if(!supports_polygons.empty()) { + if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); + else supports_polygons = polydiff(supports_polygons, model_polygons); + // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType + } + + double layer_support_area = 0; + for (const ClipperPolygon& polygon : supports_polygons) + layer_support_area += areafn(polygon); + + if (layer_support_area < 0 || layer_support_area > 0) { + Lock lck(mutex); supports_volume += layer_support_area * l_height; + } + + // Here we can save the expensively calculated polygons for printing + ClipperPolygons trslices; + trslices.reserve(model_polygons.size() + supports_polygons.size()); + for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); + for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); + + layer.transformed_slices(polyunion(trslices)); + + // Calculation of the slow and fast layers to the future controlling those values on FW + + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; + const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; + + { Lock lck(mutex); + if (is_fast_layer) + fast_layers++; + else + slow_layers++; + + + // Calculation of the printing time + + if (sliced_layer_cnt < 3) + estim_time += init_exp_time; + else if (fade_layer_time > exp_time) + { + fade_layer_time -= delta_fade_time; + estim_time += fade_layer_time; + } + else + estim_time += exp_time; + + estim_time += tilt_time; + } + }; + + // sequential version for debugging: + // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); + sla::ccr::enumerate(printer_input.begin(), printer_input.end(), printlayerfn); + + auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; + print_statistics.support_used_material = supports_volume * SCALING2; + print_statistics.objects_used_material = models_volume * SCALING2; + + // Estimated printing time + // A layers count o the highest object + if (printer_input.size() == 0) + print_statistics.estimated_print_time = std::nan(""); + else + print_statistics.estimated_print_time = estim_time; + + print_statistics.fast_layers_count = fast_layers; + print_statistics.slow_layers_count = slow_layers; + + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); +} + +// Rasterizing the model objects, and their supports +void SLAPrint::Steps::rasterize() +{ + if(canceled()) return; + + auto &print_statistics = m_print->m_print_statistics; + auto &printer_input = m_print->m_printer_input; + + // Set up the printer, allocate space for all the layers + sla::RasterWriter &printer = m_print->init_printer(); + + auto lvlcnt = unsigned(printer_input.size()); + printer.layers(lvlcnt); + + // coefficient to map the rasterization state (0-99) to the allocated + // portion (slot) of the process state + double sd = (100 - max_objstatus) / 100.0; + + // slot is the portion of 100% that is realted to rasterization + unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; + + // pst: previous state + double pst = current_status(); + + double increment = (slot * sd) / printer_input.size(); + double dstatus = current_status(); + + SpinMutex slck; + + // procedure to process one height level. This will run in parallel + auto lvlfn = [this, &slck, &printer, increment, &dstatus, &pst] (unsigned level_id) + { + if(canceled()) return; + + PrintLayer& printlayer = m_print->m_printer_input[level_id]; + + // Switch to the appropriate layer in the printer + printer.begin_layer(level_id); + + for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) + printer.draw_polygon(poly, level_id); + + // Finish the layer for later saving it. + printer.finish_layer(level_id); + + // Status indication guarded with the spinlock + { + std::lock_guard lck(slck); + dstatus += increment; + double st = std::round(dstatus); + if(st > pst) { + report_status(st, PRINT_STEP_LABELS(slapsRasterize)); + pst = st; + } + } + }; + + // last minute escape + if(canceled()) return; + + // Sequential version (for testing) + // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l); + + // Print all the layers in parallel + tbb::parallel_for(0, lvlcnt, lvlfn); + + // Set statistics values to the printer + sla::RasterWriter::PrintStatistics stats; + stats.used_material = (print_statistics.objects_used_material + + print_statistics.support_used_material) / + 1000; + + int num_fade = m_print->m_default_object_config.faded_layers.getInt(); + stats.num_fade = num_fade >= 0 ? size_t(num_fade) : size_t(0); + stats.num_fast = print_statistics.fast_layers_count; + stats.num_slow = print_statistics.slow_layers_count; + stats.estimated_print_time_s = print_statistics.estimated_print_time; + + printer.set_statistics(stats); +} + +std::string SLAPrint::Steps::label(SLAPrintObjectStep step) +{ + return OBJ_STEP_LABELS(step); +} + +std::string SLAPrint::Steps::label(SLAPrintStep step) +{ + return PRINT_STEP_LABELS(step); +} + +double SLAPrint::Steps::progressrange(SLAPrintObjectStep step) const +{ + return OBJ_STEP_LEVELS[step] * objectstep_scale; +} + +double SLAPrint::Steps::progressrange(SLAPrintStep step) const +{ + return PRINT_STEP_LEVELS[step] * (100 - max_objstatus) / 100.0; +} + +void SLAPrint::Steps::execute(SLAPrintObjectStep step, SLAPrintObject &obj) +{ + switch(step) { + case slaposHollowing: hollow_model(obj); break; + case slaposObjectSlice: slice_model(obj); break; + case slaposDrillHolesIfHollowed: break; + case slaposSupportPoints: support_points(obj); break; + case slaposSupportTree: support_tree(obj); break; + case slaposPad: generate_pad(obj); break; + case slaposSliceSupports: slice_supports(obj); break; + case slaposCount: assert(false); + } +} + +void SLAPrint::Steps::execute(SLAPrintStep step) +{ + switch (step) { + case slapsMergeSlicesAndEval: merge_slices_and_eval_stats(); break; + case slapsRasterize: rasterize(); break; + case slapsCount: assert(false); + } +} + +} diff --git a/src/libslic3r/SLAPrintSteps.hpp b/src/libslic3r/SLAPrintSteps.hpp new file mode 100644 index 0000000000..c62558671c --- /dev/null +++ b/src/libslic3r/SLAPrintSteps.hpp @@ -0,0 +1,68 @@ +#ifndef SLAPRINTSTEPS_HPP +#define SLAPRINTSTEPS_HPP + +#include + +#include +#include + +namespace Slic3r { + +class SLAPrint::Steps +{ +private: + SLAPrint *m_print = nullptr; + +public: + // where the per object operations start and end + static const constexpr unsigned min_objstatus = 0; + static const constexpr unsigned max_objstatus = 50; + +private: + const size_t objcount; + + // shortcut to initial layer height + const double ilhd; + const float ilh; + const coord_t ilhs; + + // 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 + const double objectstep_scale; + + template void report_status(Args&&...args) + { + m_print->m_report_status(*m_print, std::forward(args)...); + } + + double current_status() const { return m_print->m_report_status.status(); } + void throw_if_canceled() const { m_print->throw_if_canceled(); } + bool canceled() const { return m_print->canceled(); } + void initialize_printer_input(); + +public: + Steps(SLAPrint *print); + + void hollow_model(SLAPrintObject &po); + void slice_model(SLAPrintObject& po); + void support_points(SLAPrintObject& po); + void support_tree(SLAPrintObject& po); + void generate_pad(SLAPrintObject& po); + void slice_supports(SLAPrintObject& po); + + void merge_slices_and_eval_stats(); + void rasterize(); + + void execute(SLAPrintObjectStep step, SLAPrintObject &obj); + void execute(SLAPrintStep step); + + static std::string label(SLAPrintObjectStep step); + static std::string label(SLAPrintStep step); + + double progressrange(SLAPrintObjectStep step) const; + double progressrange(SLAPrintStep step) const; +}; + +} + +#endif // SLAPRINTSTEPS_HPP diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1d4926d92b..615d971b49 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2882,11 +2882,12 @@ void Plater::priv::HollowJob::process() if (st < 100) update_status(int(st), s); }; - TriangleMesh omesh = sla::generate_interior(*m_object_mesh, m_cfg, ctl); + std::unique_ptr omesh = + sla::generate_interior(*m_object_mesh, m_cfg, ctl); - if (!omesh.empty()) { + if (omesh && !omesh->empty()) { m_output_mesh.reset(new TriangleMesh{*m_object_mesh}); - m_output_mesh->merge(omesh); + m_output_mesh->merge(*omesh); m_output_mesh->require_shared_vertices(); update_status(90, _(L("Indexing hollowed object"))); diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 5e7f1a568d..0cb1ac343c 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "libslic3r/SLA/Hollowing.hpp" #include #include "libslic3r/Format/OBJ.hpp" @@ -28,13 +29,14 @@ TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") Benchmark bench; bench.start(); - Slic3r::TriangleMesh out_mesh = Slic3r::sla::generate_interior(in_mesh); + std::unique_ptr out_mesh_ptr = + Slic3r::sla::generate_interior(in_mesh); bench.stop(); std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl; - in_mesh.merge(out_mesh); + if (out_mesh_ptr) in_mesh.merge(*out_mesh_ptr); in_mesh.require_shared_vertices(); in_mesh.WriteOBJFile("merged_out.obj"); } From d4d037792d086b009473eeb3e3bda20e60808f59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 13 Nov 2019 15:55:37 +0100 Subject: [PATCH 039/130] Holes are now visible on slices in preview. --- cmake/modules/FindOpenVDB.cmake | 4 +- src/libslic3r/Model.hpp | 6 +- src/libslic3r/SLA/SupportTreeBuilder.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 39 +++++++---- src/libslic3r/SLAPrint.hpp | 3 +- src/libslic3r/SLAPrintSteps.cpp | 72 +++++++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 44 ++++++------ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 22 +++--- src/slic3r/GUI/MeshUtils.hpp | 2 +- 9 files changed, 123 insertions(+), 71 deletions(-) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index dd4ff5b20e..70bbe05f55 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -283,9 +283,9 @@ endif() macro(just_fail msg) set(OpenVDB_FOUND FALSE) if(OpenVDB_FIND_REQUIRED) - message(FATAL_ERROR msg) + message(FATAL_ERROR ${msg}) elseif(NOT OpenVDB_FIND_QUIETLY) - message(ERROR msg) + message(WARNING ${msg}) endif() return() endmacro() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9083e35e08..ccc48d52bb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -199,13 +199,13 @@ public: // This vector holds position of selected support points for SLA. The data are // saved in mesh coordinates to allow using them for several instances. // The format is (x, y, z, point_size, supports_island) - std::vector sla_support_points; + sla::SupportPoints sla_support_points; // To keep track of where the points came from (used for synchronization between // the SLA gizmo and the backend). - sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; + sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; // Holes to be drilled into the object so resin can flow out - std::vector sla_drain_holes; + sla::DrainHoles sla_drain_holes; /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 4859b004c3..90cf417c83 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -74,7 +74,7 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), // h: Height // ssteps: how many edges will create the base circle // sp: starting point -Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp = {0,0,0}); +Contour3D cylinder(double r, double h, size_t ssteps = 45, const Vec3d &sp = {0,0,0}); const constexpr long ID_UNSET = -1; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 53a0e82233..61723721e4 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -414,6 +414,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con model_object.sla_support_points = model_object_new.sla_support_points; } model_object.sla_points_status = model_object_new.sla_points_status; + + if (model_object.sla_drain_holes.size() != model_object_new.sla_drain_holes.size()) + { + model_object.sla_drain_holes = model_object_new.sla_drain_holes; + update_apply_status(it_print_object_status->print_object->invalidate_step(slaposHollowing)); + } // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. model_object.name = model_object_new.name; @@ -1154,21 +1160,30 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const { return m_transformed_rmesh.get(); } -std::vector SLAPrintObject::transformed_support_points() const +template::value_type> +std::vector transform_pts(It from, It to, Trafo &&tr) +{ + auto ret = reserve_vector(to - from); + for(auto it = from; it != to; ++it) { + V v = *it; + v.pos = tr * it->pos; + ret.emplace_back(std::move(v)); + } + return ret; +} + +sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); - std::vector& spts = m_model_object->sla_support_points; + auto& spts = m_model_object->sla_support_points; + return transform_pts(spts.begin(), spts.end(), trafo().cast()); +} - // this could be cached as well - std::vector ret; - ret.reserve(spts.size()); - - for(sla::SupportPoint& sp : spts) { - Vec3f transformed_pos = trafo().cast() * sp.pos; - ret.emplace_back(transformed_pos, sp.head_front_radius, sp.is_new_island); - } - - return ret; +sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const +{ + assert(m_model_object != nullptr); + auto& spts = m_model_object->sla_drain_holes; + return transform_pts(spts.begin(), spts.end(), trafo().cast()); } DynamicConfig SLAPrintStatistics::config() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 2edede1099..8be69545e1 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -83,7 +83,8 @@ public: // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; - std::vector transformed_support_points() const; + sla::SupportPoints transformed_support_points() const; + sla::DrainHoles transformed_drainhole_points() const; // Get the needed Z elevation for the model geometry if supports should be // displayed. This Z offset should also be applied to the support diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 09d449576f..55359cc5c1 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -1,5 +1,9 @@ #include + +// Need the cylinder method for the the drainholes in hollowing step +#include + #include #include #include @@ -98,6 +102,42 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; } +static void cut_drainholes(std::vector & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function thr) +{ + TriangleMesh mesh; + for (const sla::DrainHole &holept : holes) { + auto r = double(holept.radius); + auto h = double(holept.height); + sla::Contour3D hole = sla::cylinder(r, h); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, holept.normal.cast()); + for(auto& p : hole.points) p = q * p + holept.pos.cast(); + mesh.merge(sla::to_triangle_mesh(hole)); + } + + if (mesh.empty()) return; + + mesh.require_shared_vertices(); + + TriangleMeshSlicer slicer(&mesh); + + std::vector hole_slices; + slicer.slice(slicegrid, closing_radius, &hole_slices, thr); + + if (obj_slices.size() != hole_slices.size()) + BOOST_LOG_TRIVIAL(warning) + << "Sliced object and drain-holes layer count does not match!"; + + size_t until = std::min(obj_slices.size(), hole_slices.size()); + + for (size_t i = 0; i < until; ++i) + obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); +} + // The slicing will be performed on an imaginary 1D grid which starts from // the bottom of the bounding box created around the supported model. So // the first layer which is usually thicker will be part of the supports @@ -107,12 +147,10 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // of it. In any case, the model and the supports have to be sliced in the // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) -{ - +{ TriangleMesh hollowed_mesh; - bool is_hollowing = po.m_config.hollowing_enable.getBool() && - po.m_hollowing_data; + bool is_hollowing = po.m_config.hollowing_enable.getBool() && po.m_hollowing_data; if (is_hollowing) { hollowed_mesh = po.transformed_mesh(); @@ -120,8 +158,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) hollowed_mesh.require_shared_vertices(); } - const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : - po.transformed_mesh(); + const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : po.transformed_mesh(); // We need to prepare the slice index... @@ -163,10 +200,13 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) TriangleMeshSlicer slicer(&mesh); po.m_model_slices.clear(); - slicer.slice(po.m_model_height_levels, - float(po.config().slice_closing_radius.value), - &po.m_model_slices, - [this](){ m_print->throw_if_canceled(); }); + float closing_r = float(po.config().slice_closing_radius.value); + auto thr = [this]() { m_print->throw_if_canceled(); }; + auto &slice_grid = po.m_model_height_levels; + slicer.slice(slice_grid, closing_r, &po.m_model_slices, thr); + + sla::DrainHoles drainholes = po.transformed_drainhole_points(); + cut_drainholes(po.m_model_slices, slice_grid, closing_r, drainholes, thr); auto mit = slindex_it; double doffs = m_print->m_printer_config.absolute_correction.getFloat(); @@ -183,8 +223,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) mit->set_model_slice_idx(po, id); ++mit; } - if(po.m_config.supports_enable.getBool() || - po.m_config.pad_enable.getBool()) + if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) { po.m_supportdata.reset( new SLAPrintObject::SupportData(po.transformed_mesh()) ); @@ -324,8 +363,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // and before the supports had been sliced. (or the slicing has to be // repeated) - if(po.m_config.pad_enable.getBool()) - { + if(po.m_config.pad_enable.getBool()) { // Get the distilled pad configuration from the config sla::PadConfig pcfg = make_pad_cfg(po.m_config); @@ -368,13 +406,11 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { if(sd) sd->support_slices.clear(); // Don't bother if no supports and no pad is present. - if (!po.m_config.supports_enable.getBool() && - !po.m_config.pad_enable.getBool()) + if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) return; if(sd && sd->support_tree_ptr) { - - std::vector heights; heights.reserve(po.m_slice_index.size()); + auto heights = reserve_vector(po.m_slice_index.size()); for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index c6c02947f1..b320c6bbe7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -250,7 +250,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped(drain_hole.m_pos.cast())) + if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; // First decide about the color of the point. @@ -281,7 +281,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) @@ -290,17 +290,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); - ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.m_height)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); if (vol->is_left_handed()) @@ -433,7 +433,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); m_selected.push_back(false); - assert(m_selected.size == m_model_object->sla_drain_holes.size()); + assert(m_selected.size() == m_model_object->sla_drain_holes.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; } @@ -456,7 +456,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); std::vector points; for (unsigned int i=0; isla_drain_holes.size(); ++i) - points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].m_pos.cast()); + points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].pos.cast()); // Now ask the rectangle which of the points are inside. std::vector points_inside; @@ -558,8 +558,8 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; - m_model_object->sla_drain_holes[m_hover_id].m_normal = -pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; } } @@ -688,20 +688,20 @@ RENDER_AGAIN: if (ImGui::IsItemEdited()) { for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; } if (ImGui::IsItemDeactivatedAfterEdit()) { // momentarily restore the old value to take snapshot for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_old_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_old_hole_radius; float backup = m_new_hole_radius; m_new_hole_radius = m_old_hole_radius; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); m_new_hole_radius = backup; for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; m_old_hole_radius = 0.f; } @@ -863,7 +863,7 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].m_pos; + m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].pos; } else m_hole_before_drag = Vec3f::Zero(); @@ -873,14 +873,14 @@ void GLGizmoHollow::on_start_dragging() void GLGizmoHollow::on_stop_dragging() { if (m_hover_id != -1) { - Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].m_pos; + Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].pos; if (m_hole_before_drag != Vec3f::Zero() // some point was touched && backup != m_hole_before_drag) // and it was moved, not just selected { - m_model_object->sla_drain_holes[m_hover_id].m_pos = m_hole_before_drag; + m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_model_object->sla_drain_holes[m_hover_id].m_pos = backup; + m_model_object->sla_drain_holes[m_hover_id].pos = backup; } } m_hole_before_drag = Vec3f::Zero(); @@ -921,14 +921,14 @@ void GLGizmoHollow::select_point(int i) m_selection_empty = (i == NoPoints); if (i == AllPoints) - m_new_hole_radius = m_model_object->sla_drain_holes[0].m_radius; + m_new_hole_radius = m_model_object->sla_drain_holes[0].radius; } else { while (size_t(i) >= m_selected.size()) m_selected.push_back(false); m_selected[i] = true; m_selection_empty = false; - m_new_hole_radius = m_model_object->sla_drain_holes[i].m_radius; + m_new_hole_radius = m_model_object->sla_drain_holes[i].radius; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a7ad02cd8d..15f46aab63 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -336,7 +336,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) for (const sla::DrainHole& drain_hole : m_model_object->sla_drain_holes) { // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) @@ -345,17 +345,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); - ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.m_height)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); if (vol->is_left_handed()) @@ -421,10 +421,10 @@ bool GLGizmoSlaSupports::is_point_in_hole(const Vec3f& pt) const for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { - if ( hole.m_normal.dot(pt-hole.m_pos) < EPSILON - || hole.m_normal.dot(pt-(hole.m_pos+hole.m_height * hole.m_normal)) > 0.f) + if ( hole.normal.dot(pt-hole.pos) < EPSILON + || hole.normal.dot(pt-(hole.pos+hole.height * hole.normal)) > 0.f) continue; - if ( squared_distance_from_line(pt, hole.m_pos, hole.m_normal) < pow(hole.m_radius, 2.f)) + if ( squared_distance_from_line(pt, hole.pos, hole.normal) < pow(hole.radius, 2.f)) return true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index a12c8d6c69..5900cb820a 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -47,7 +47,7 @@ public: bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); } double distance(const Vec3d& pt) const { - assert(is_approx(get_normal().norm(), 1.)); + // FIXME: this fails: assert(is_approx(get_normal().norm(), 1.)); return (-get_normal().dot(pt) + m_data[3]); } From 9dd18a8d6d34941887daabf536da26024e769e9b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 15 Nov 2019 15:48:52 +0100 Subject: [PATCH 040/130] Started work on extending EigenMesh3D to account for possible drain holes when raycasting --- src/libslic3r/SLA/Common.cpp | 25 ++++- src/libslic3r/SLA/EigenMesh3D.hpp | 9 +- src/libslic3r/SLA/Hollowing.cpp | 96 ++++++++++++++++++++ src/libslic3r/SLA/Hollowing.hpp | 6 ++ src/libslic3r/SLAPrint.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 42 ++++----- 6 files changed, 149 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index caabdd7554..4b02e361a0 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -6,6 +6,7 @@ #include #include #include +#include // Workaround: IGL signed_distance.h will define PI in the igl namespace. @@ -181,6 +182,19 @@ void BoxIndex::foreach(std::function fn) for(auto& el : m_impl->m_store) fn(el); } + +namespace { +// Iterates over hits and holes and returns the true hit, possibly +// on the inside of a hole. Free function so it can return igl::Hit +// without including igl in a header. +igl::Hit filter_hits(const std::vector& hits, + const std::vector& holes) +{ + return igl::Hit(); +} + +} // namespace + /* **************************************************************************** * EigenMesh3D implementation * ****************************************************************************/ @@ -261,11 +275,18 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) } EigenMesh3D::hit_result -EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +EigenMesh3D::query_ray_hit(const Vec3d &s, + const Vec3d &dir, + const std::vector* holes + ) const { igl::Hit hit; hit.t = std::numeric_limits::infinity(); - m_aabb->intersect_ray(m_V, m_F, s, dir, hit); + + if (! holes) + m_aabb->intersect_ray(m_V, m_F, s, dir, hit); + else + hit = filter_hits(query_ray_hits(s, dir), *holes); hit_result ret(*this); ret.m_t = double(hit.t); diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index d272af3b1e..2144a8f7f6 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -10,10 +10,11 @@ class TriangleMesh; namespace sla { struct Contour3D; +struct DrainHole; /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree. -// Implemented in SLASupportTreeIGL.cpp +// Implemented in libslic3r/SLA/Common.cpp class EigenMesh3D { class AABBImpl; @@ -83,11 +84,13 @@ public: }; // Casting a ray on the mesh, returns the distance where the hit occures. - hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + hit_result query_ray_hit(const Vec3d &s, + const Vec3d &dir, + const std::vector* holes = nullptr) const; // Casts a ray on the mesh and returns all hits std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; - + class si_result { double m_value; int m_fidx; diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 2a496d2765..c336f49b3c 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -126,4 +126,100 @@ bool DrainHole::operator==(const DrainHole &sp) const is_approx(height, sp.height); } +bool DrainHole::is_inside(const Vec3f& pt) const +{ + Eigen::Hyperplane plane(normal, pos); + float dist = plane.signedDistance(pt); + if (dist < EPSILON || dist > height) + return false; + + Eigen::ParametrizedLine axis(pos, normal); + if ( axis.squaredDistance(pt) < pow(radius, 2.f)) + return true; + + return false; +} + + +// Given a line s+dir*t, return parameter t of intersections with the hole. +// If there is no intersection, returns nan. +std::pair DrainHole::get_intersections(const Vec3f& s, + const Vec3f& dir) const +{ + assert(is_approx(normal.norm(), 1.f)); + const Eigen::ParametrizedLine ray(s, dir.normalized()); + + std::pair out(std::nan(""), std::nan("")); + const float sqr_radius = pow(radius, 2.f); + + // first check a bounding sphere of the hole: + Vec3f center = pos+normal*height/2.f; + float sqr_dist_limit = pow(height/2.f, 2.f) + sqr_radius ; + if (ray.squaredDistance(center) > sqr_dist_limit) + return out; + + // The line intersects the bounding sphere, look for intersections with + // bases of the cylinder. + + size_t found = 0; // counts how many intersections were found + Eigen::Hyperplane base; + if (! is_approx(ray.direction().dot(normal), 0.f)) { + for (size_t i=1; i<=1; --i) { + Vec3f cylinder_center = pos+i*height*normal; + base = Eigen::Hyperplane(normal, cylinder_center); + Vec3f intersection = ray.intersectionPoint(base); + // Only accept the point if it is inside the cylinder base. + if ((cylinder_center-intersection).squaredNorm() < sqr_radius) { + (found ? out.second : out.first) = ray.intersectionParameter(base); + ++found; + } + } + } + else + { + // In case the line was perpendicular to the cylinder axis, previous + // block was skipped, but base will later be assumed to be valid. + base = Eigen::Hyperplane(normal, pos); + } + + // In case there is still an intersection to be found, check the wall + if (found != 2 && ! is_approx(std::abs(ray.direction().dot(normal)), 1.f)) { + // Project the ray onto the base plane + Vec3f proj_origin = base.projection(ray.origin()); + Vec3f proj_dir = base.projection(ray.origin()+ray.direction())-proj_origin; + // save how the parameter scales and normalize the projected direction + float par_scale = proj_dir.norm(); + proj_dir = proj_dir/par_scale; + Eigen::ParametrizedLine projected_ray(proj_origin, proj_dir); + // Calculate point on the secant that's closest to the center + // and its distance to the circle along the projected line + Vec3f closest = projected_ray.projection(pos); + float dist = sqrt((sqr_radius - (closest-pos).squaredNorm())); + // Unproject both intersections on the original line and check + // they are on the cylinder and not past it: + for (int i=-1; i<=1 && found !=2; i+=2) { + Vec3f isect = closest + i*dist * projected_ray.direction(); + float par = (isect-proj_origin).norm() / par_scale; + isect = ray.pointAt(par); + // check that the intersection is between the base planes: + float vert_dist = base.signedDistance(isect); + if (vert_dist > 0.f && vert_dist < height) { + (found ? out.second : out.first) = par; + ++found; + } + } + } + + // If only one intersection was found, it is some corner case, + // no intersection will be returned: + if (found != 0) + return std::pair(std::nan(""), std::nan("")); + + // Sort the intersections: + if (out.first > out.second) + std::swap(out.first, out.second); + + return out; +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index 543f5e6cf4..f2f6c468ae 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -36,6 +36,12 @@ struct DrainHole bool operator==(const DrainHole &sp) const; bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } + + bool is_inside(const Vec3f& pt) const; + + std::pair get_intersections(const Vec3f& s, + const Vec3f& dir + ) const; template inline void serialize(Archive &ar) { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 61723721e4..b1d1cf5ea2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -415,7 +415,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con } model_object.sla_points_status = model_object_new.sla_points_status; - if (model_object.sla_drain_holes.size() != model_object_new.sla_drain_holes.size()) + // Invalidate hollowing if drain holes have changed + if (model_object.sla_drain_holes != model_object_new.sla_drain_holes) { model_object.sla_drain_holes = model_object_new.sla_drain_holes; update_apply_status(it_print_object_status->print_object->invalidate_step(slaposHollowing)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 15f46aab63..29eb72fc52 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -412,25 +412,6 @@ void GLGizmoSlaSupports::update_mesh() } -bool GLGizmoSlaSupports::is_point_in_hole(const Vec3f& pt) const -{ - auto squared_distance_from_line = [](const Vec3f pt, const Vec3f& line_pt, const Vec3f& dir) -> float { - Vec3f diff = line_pt - pt; - return (diff - diff.dot(dir) * dir).squaredNorm(); - }; - - - for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { - if ( hole.normal.dot(pt-hole.pos) < EPSILON - || hole.normal.dot(pt-(hole.pos+hole.height * hole.normal)) > 0.f) - continue; - if ( squared_distance_from_line(pt, hole.pos, hole.normal) < pow(hole.radius, 2.f)) - return true; - } - - return false; -} - // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) @@ -448,14 +429,23 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get()) - && ! is_point_in_hole(hit)) { - // Return both the point and the facet normal. - pos_and_normal = std::make_pair(hit, normal); - return true; + if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + // Check whether the hit is in a hole + bool in_hole = false; + for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { + if (hole.is_inside(hit)) { + in_hole = true; + break; + } + } + if (! in_hole) { + // Return both the point and the facet normal. + pos_and_normal = std::make_pair(hit, normal); + return true; + } } - else - return false; + + return false; } // Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event. From 2c1d256b0ce7003995700687b6e4d4cc1e9d2dbc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Nov 2019 14:27:05 +0100 Subject: [PATCH 041/130] EigenMesh3D raycaster should now be able to pick a correct intersection on the object or inside a hole --- src/libslic3r/SLA/Common.cpp | 122 ++++++++++++++++---- src/libslic3r/SLA/EigenMesh3D.hpp | 40 ++++--- src/libslic3r/SLA/Hollowing.cpp | 33 +++--- src/libslic3r/SLA/Hollowing.hpp | 5 +- src/libslic3r/SLA/SupportPointGenerator.cpp | 4 +- 5 files changed, 147 insertions(+), 57 deletions(-) diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index 4b02e361a0..c54d78afa2 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -183,18 +183,6 @@ void BoxIndex::foreach(std::function fn) } -namespace { -// Iterates over hits and holes and returns the true hit, possibly -// on the inside of a hole. Free function so it can return igl::Hit -// without including igl in a header. -igl::Hit filter_hits(const std::vector& hits, - const std::vector& holes) -{ - return igl::Hit(); -} - -} // namespace - /* **************************************************************************** * EigenMesh3D implementation * ****************************************************************************/ @@ -280,21 +268,26 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const std::vector* holes ) const { + assert(is_approx(dir.norm(), 1.)); igl::Hit hit; hit.t = std::numeric_limits::infinity(); - if (! holes) + if (! holes) { m_aabb->intersect_ray(m_V, m_F, s, dir, hit); - else - hit = filter_hits(query_ray_hits(s, dir), *holes); - - hit_result ret(*this); - ret.m_t = double(hit.t); - ret.m_dir = dir; - ret.m_source = s; - if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id; - - return ret; + hit_result ret(*this); + ret.m_t = double(hit.t); + ret.m_dir = dir; + ret.m_source = s; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) + ret.m_normal = this->normal_by_face_id(hit.id); + + return ret; + } + else { + // If there are holes, the hit_results will be made by + // query_ray_hits (object) and filter_hits (holes): + return filter_hits(query_ray_hits(s, dir), *holes); + } } std::vector @@ -316,12 +309,91 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const outs.back().m_dir = dir; outs.back().m_source = s; if(!std::isinf(hit.t) && !std::isnan(hit.t)) - outs.back().m_face_id = hit.id; + outs.back().m_normal = this->normal_by_face_id(hit.id); } - + return outs; } +EigenMesh3D::hit_result EigenMesh3D::filter_hits( + const std::vector& object_hits, + const std::vector& holes) const +{ + hit_result out(*this); + out.m_t = std::nan(""); + + if (! holes.empty() && ! object_hits.empty()) { + Vec3d s = object_hits.front().source(); + Vec3d dir = object_hits.front().direction(); + + struct HoleHit { + HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) : + t(t_p), normal(normal_p), entry(entry_p) {} + float t; + Vec3d normal; + bool entry; + }; + std::vector hole_isects; + + // Collect hits on all holes, preserve information about entry/exit + for (const sla::DrainHole& hole : holes) { + std::array, 2> isects; + if (hole.get_intersections(s.cast(), + dir.cast(), isects)) { + hole_isects.emplace_back(isects[0].first, isects[0].second, true); + hole_isects.emplace_back(isects[1].first, isects[1].second, false); + } + } + // Holes can intersect each other, sort the hits by t + std::sort(hole_isects.begin(), hole_isects.end(), + [](const HoleHit& a, const HoleHit& b) { return a.t < b.t; }); + + // Now inspect the intersections with object and holes, keep track how + // deep are we nested in mesh/holes and pick the correct intersection + int hole_nested = 0; + int object_nested = 0; + + bool is_hole = false; + bool is_entry = false; + const HoleHit* next_hole_hit = &hole_isects.front(); + const hit_result* next_mesh_hit = &object_hits.front(); + + while (next_hole_hit || next_mesh_hit) { + if (next_hole_hit && next_mesh_hit) // still have hole and obj hits + is_hole = (next_hole_hit->t < next_mesh_hit->m_t); + else + is_hole = next_hole_hit; // one or the other ran out + + // Is this entry or exit hit? + is_entry = is_hole ? next_hole_hit->entry : ! next_mesh_hit->is_inside(); + + if (! is_hole && is_entry && hole_nested == 0) { + // This mesh point is the one we seek + return *next_mesh_hit; + } + if (is_hole && ! is_entry && object_nested != 0) { + // This holehit is the one we seek + out.m_t = next_hole_hit->t; + out.m_normal = next_hole_hit->normal; + out.m_source = s; + out.m_dir = dir; + return out; + } + + hole_nested += (is_hole ? (is_entry ? 1 : -1) : 0); + object_nested += (! is_hole ? (is_entry ? 1 : -1) : 0); + + // Advance the pointer + if (is_hole && next_hole_hit++ == &hole_isects.back()) + next_hole_hit = nullptr; + if (! is_hole && next_mesh_hit++ == &object_hits.back()) + next_mesh_hit = nullptr; + } + } + + return out; +} + #ifdef SLIC3R_SLA_NEEDS_WINDTREE EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { double sign = 0; double sqdst = 0; int i = 0; Vec3d c; diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index 2144a8f7f6..c92094756f 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -42,10 +42,10 @@ public: // Result of a raycast class hit_result { double m_t = std::nan(""); - int m_face_id = -1; const EigenMesh3D *m_mesh = nullptr; Vec3d m_dir; Vec3d m_source; + Vec3d m_normal = Vec3d::Zero(); friend class EigenMesh3D; // A valid object of this class can only be obtained from @@ -60,26 +60,23 @@ public: inline double distance() const { return m_t; } inline const Vec3d& direction() const { return m_dir; } + inline const Vec3d& source() const { return m_source; } inline Vec3d position() const { return m_source + m_dir * m_t; } - inline int face() const { return m_face_id; } inline bool is_valid() const { return m_mesh != nullptr; } + inline bool is_hit() const { return m_normal != Vec3d::Zero(); } // Hit_result can decay into a double as the hit distance. inline operator double() const { return distance(); } - - inline Vec3d normal() const { - if(m_face_id < 0 || !is_valid()) return {}; - auto trindex = m_mesh->m_F.row(m_face_id); - const Vec3d& p1 = m_mesh->V().row(trindex(0)); - const Vec3d& p2 = m_mesh->V().row(trindex(1)); - const Vec3d& p3 = m_mesh->V().row(trindex(2)); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - return U.cross(V).normalized(); + + inline const Vec3d& normal() const { + if(!is_valid()) + throw std::runtime_error("EigenMesh3D::hit_result::normal() " + "called on invalid object."); + return m_normal; } - inline bool is_inside() { - return m_face_id >= 0 && normal().dot(m_dir) > 0; + inline bool is_inside() const { + return normal().dot(m_dir) > 0; } }; @@ -91,6 +88,11 @@ public: // Casts a ray on the mesh and returns all hits std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; + // Iterates over hits and holes and returns the true hit, possibly + // on the inside of a hole. + hit_result filter_hits(const std::vector& obj_hits, + const std::vector& holes) const; + class si_result { double m_value; int m_fidx; @@ -123,6 +125,16 @@ public: Vec3d c; return squared_distance(p, i, c); } + + Vec3d normal_by_face_id(int face_id) const { + auto trindex = F().row(face_id); + const Vec3d& p1 = V().row(trindex(0)); + const Vec3d& p2 = V().row(trindex(1)); + const Vec3d& p3 = V().row(trindex(2)); + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + return U.cross(V).normalized(); + } }; // Calculate the normals for the selected points (from 'points' set) on the diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index c336f49b3c..a510ab3da0 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -141,22 +141,26 @@ bool DrainHole::is_inside(const Vec3f& pt) const } -// Given a line s+dir*t, return parameter t of intersections with the hole. -// If there is no intersection, returns nan. -std::pair DrainHole::get_intersections(const Vec3f& s, - const Vec3f& dir) const +// Given a line s+dir*t, find parameter t of intersections with the hole +// and the normal (points inside the hole). Outputs through out reference, +// returns true if two intersections were found. +bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, + std::array, 2>& out) + const { assert(is_approx(normal.norm(), 1.f)); const Eigen::ParametrizedLine ray(s, dir.normalized()); - std::pair out(std::nan(""), std::nan("")); + for (size_t i=0; i<2; ++i) + out[i] = std::make_pair(std::nan(""), Vec3d::Zero()); + const float sqr_radius = pow(radius, 2.f); // first check a bounding sphere of the hole: Vec3f center = pos+normal*height/2.f; float sqr_dist_limit = pow(height/2.f, 2.f) + sqr_radius ; if (ray.squaredDistance(center) > sqr_dist_limit) - return out; + return false; // The line intersects the bounding sphere, look for intersections with // bases of the cylinder. @@ -170,7 +174,8 @@ std::pair DrainHole::get_intersections(const Vec3f& s, Vec3f intersection = ray.intersectionPoint(base); // Only accept the point if it is inside the cylinder base. if ((cylinder_center-intersection).squaredNorm() < sqr_radius) { - (found ? out.second : out.first) = ray.intersectionParameter(base); + out[found].first = ray.intersectionParameter(base); + out[found].second = (i==0 ? 1. : -1.) * normal.cast(); ++found; } } @@ -200,11 +205,13 @@ std::pair DrainHole::get_intersections(const Vec3f& s, for (int i=-1; i<=1 && found !=2; i+=2) { Vec3f isect = closest + i*dist * projected_ray.direction(); float par = (isect-proj_origin).norm() / par_scale; + Vec3d hit_normal = (pos-isect).normalized().cast(); isect = ray.pointAt(par); // check that the intersection is between the base planes: float vert_dist = base.signedDistance(isect); if (vert_dist > 0.f && vert_dist < height) { - (found ? out.second : out.first) = par; + out[found].first = par; + out[found].second = hit_normal; ++found; } } @@ -212,14 +219,14 @@ std::pair DrainHole::get_intersections(const Vec3f& s, // If only one intersection was found, it is some corner case, // no intersection will be returned: - if (found != 0) - return std::pair(std::nan(""), std::nan("")); + if (found != 2) + return false; // Sort the intersections: - if (out.first > out.second) - std::swap(out.first, out.second); + if (out[0].first > out[1].first) + std::swap(out[0], out[1]); - return out; + return true; } }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index f2f6c468ae..d5c0d49fc8 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -39,9 +39,8 @@ struct DrainHole bool is_inside(const Vec3f& pt) const; - std::pair get_intersections(const Vec3f& s, - const Vec3f& dir - ) const; + bool get_intersections(const Vec3f& s, const Vec3f& dir, + std::array, 2>& out) const; template inline void serialize(Archive &ar) { diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 446dd3f403..36361f9ca7 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -83,8 +83,8 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); - bool up = hit_up.face() != -1; - bool down = hit_down.face() != -1; + bool up = hit_up.is_hit(); + bool down = hit_down.is_hit(); if (!up && !down) continue; From bc0db7dc91f207d9704b0dec31ca062a719ac62d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 22 Nov 2019 15:45:48 +0100 Subject: [PATCH 042/130] Quickly added a hole height setting --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index b320c6bbe7..9c0e2c4d90 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -709,6 +709,9 @@ RENDER_AGAIN: m_imgui->text("Hole height: "); ImGui::SameLine(); ImGui::SliderFloat(" ", &m_new_hole_height, 0.1f, 10.f, "%.1f"); + for (size_t idx=0; idxsla_drain_holes[idx].height = m_new_hole_height; m_imgui->disabled_begin(m_selection_empty); remove_selected = m_imgui->button(m_desc.at("remove_selected")); @@ -894,6 +897,7 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) *m_clipping_plane, m_model_object_id, m_new_hole_radius, + m_new_hole_height, m_selected, m_selection_empty ); @@ -907,6 +911,7 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const *m_clipping_plane, m_model_object_id, m_new_hole_radius, + m_new_hole_height, m_selected, m_selection_empty ); @@ -920,8 +925,10 @@ void GLGizmoHollow::select_point(int i) m_selected.assign(m_selected.size(), i == AllPoints); m_selection_empty = (i == NoPoints); - if (i == AllPoints) + if (i == AllPoints) { m_new_hole_radius = m_model_object->sla_drain_holes[0].radius; + m_new_hole_height = m_model_object->sla_drain_holes[0].height; + } } else { while (size_t(i) >= m_selected.size()) @@ -929,6 +936,7 @@ void GLGizmoHollow::select_point(int i) m_selected[i] = true; m_selection_empty = false; m_new_hole_radius = m_model_object->sla_drain_holes[i].radius; + m_new_hole_height = m_model_object->sla_drain_holes[i].height; } } From 281762db45bda41ca5449ad36957c2dc7b5d6d2d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Nov 2019 14:18:56 +0100 Subject: [PATCH 043/130] Add hollowed interiors to the support data --- src/libslic3r/SLAPrintSteps.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 55359cc5c1..0eb83d3c15 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -237,9 +237,19 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; + bool is_hollowing = po.m_config.hollowing_enable.getBool() && po.m_hollowing_data; + + TriangleMesh hollowed_mesh; + if (is_hollowing) { + hollowed_mesh = po.transformed_mesh(); + hollowed_mesh.merge(po.m_hollowing_data->interior); + hollowed_mesh.require_shared_vertices(); + } + + const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : po.transformed_mesh(); + if (!po.m_supportdata) - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh())); + po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); const ModelObject& mo = *po.m_model_object; From 73af7c64b8028a11d0123a95e236af014dc28008 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 26 Nov 2019 11:36:09 +0100 Subject: [PATCH 044/130] SLATreeSupports generator now takes account for holes and can build supports through them --- src/libslic3r/SLA/Common.cpp | 113 +++++++++++--------- src/libslic3r/SLA/EigenMesh3D.hpp | 44 ++++---- src/libslic3r/SLA/Hollowing.cpp | 10 +- src/libslic3r/SLA/SupportPointGenerator.cpp | 7 -- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 2 +- src/libslic3r/SLAPrintSteps.cpp | 6 ++ 6 files changed, 103 insertions(+), 79 deletions(-) diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index c54d78afa2..3710bf3bed 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -263,16 +263,13 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) } EigenMesh3D::hit_result -EigenMesh3D::query_ray_hit(const Vec3d &s, - const Vec3d &dir, - const std::vector* holes - ) const +EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { assert(is_approx(dir.norm(), 1.)); igl::Hit hit; hit.t = std::numeric_limits::infinity(); - if (! holes) { + if (m_holes.empty()) { m_aabb->intersect_ray(m_V, m_F, s, dir, hit); hit_result ret(*this); ret.m_t = double(hit.t); @@ -286,7 +283,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, else { // If there are holes, the hit_results will be made by // query_ray_hits (object) and filter_hits (holes): - return filter_hits(query_ray_hits(s, dir), *holes); + return filter_hits(query_ray_hits(s, dir)); } } @@ -316,46 +313,59 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const } EigenMesh3D::hit_result EigenMesh3D::filter_hits( - const std::vector& object_hits, - const std::vector& holes) const + const std::vector& object_hits) const { + assert(! m_holes.empty()); hit_result out(*this); - out.m_t = std::nan(""); - if (! holes.empty() && ! object_hits.empty()) { - Vec3d s = object_hits.front().source(); - Vec3d dir = object_hits.front().direction(); + if (object_hits.empty()) + return out; - struct HoleHit { - HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) : - t(t_p), normal(normal_p), entry(entry_p) {} - float t; - Vec3d normal; - bool entry; - }; - std::vector hole_isects; + const Vec3d& s = object_hits.front().source(); + const Vec3d& dir = object_hits.front().direction(); - // Collect hits on all holes, preserve information about entry/exit - for (const sla::DrainHole& hole : holes) { - std::array, 2> isects; - if (hole.get_intersections(s.cast(), - dir.cast(), isects)) { - hole_isects.emplace_back(isects[0].first, isects[0].second, true); - hole_isects.emplace_back(isects[1].first, isects[1].second, false); - } + // A helper struct to save an intersetion with a hole + struct HoleHit { + HoleHit(float t_p, const Vec3d& normal_p, bool entry_p) : + t(t_p), normal(normal_p), entry(entry_p) {} + float t; + Vec3d normal; + bool entry; + }; + std::vector hole_isects; + + // Collect hits on all holes, preserve information about entry/exit + for (const sla::DrainHole& hole : m_holes) { + std::array, 2> isects; + if (hole.get_intersections(s.cast(), + dir.cast(), isects)) { + hole_isects.emplace_back(isects[0].first, isects[0].second, true); + hole_isects.emplace_back(isects[1].first, isects[1].second, false); } - // Holes can intersect each other, sort the hits by t - std::sort(hole_isects.begin(), hole_isects.end(), - [](const HoleHit& a, const HoleHit& b) { return a.t < b.t; }); + } + // Remove hole hits behind the source + for (int i=0; i=0; --dry_run) { + hole_nested = -hole_nested; + object_nested = -object_nested; bool is_hole = false; bool is_entry = false; - const HoleHit* next_hole_hit = &hole_isects.front(); + const HoleHit* next_hole_hit = hole_isects.empty() ? nullptr : &hole_isects.front(); const hit_result* next_mesh_hit = &object_hits.front(); while (next_hole_hit || next_mesh_hit) { @@ -367,23 +377,25 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( // Is this entry or exit hit? is_entry = is_hole ? next_hole_hit->entry : ! next_mesh_hit->is_inside(); - if (! is_hole && is_entry && hole_nested == 0) { - // This mesh point is the one we seek - return *next_mesh_hit; - } - if (is_hole && ! is_entry && object_nested != 0) { - // This holehit is the one we seek - out.m_t = next_hole_hit->t; - out.m_normal = next_hole_hit->normal; - out.m_source = s; - out.m_dir = dir; - return out; + if (! dry_run) { + if (! is_hole && hole_nested == 0) { + // This is a valid object hit + return *next_mesh_hit; + } + if (is_hole && ! is_entry && object_nested != 0) { + // This holehit is the one we seek + out.m_t = next_hole_hit->t; + out.m_normal = next_hole_hit->normal; + out.m_source = s; + out.m_dir = dir; + return out; + } } - hole_nested += (is_hole ? (is_entry ? 1 : -1) : 0); - object_nested += (! is_hole ? (is_entry ? 1 : -1) : 0); + // Increase/decrease the counter + (is_hole ? hole_nested : object_nested) += (is_entry ? 1 : -1); - // Advance the pointer + // Advance the respective pointer if (is_hole && next_hole_hit++ == &hole_isects.back()) next_hole_hit = nullptr; if (! is_hole && next_mesh_hit++ == &object_hits.back()) @@ -391,6 +403,7 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( } } + // if we got here, the ray ended up in infinity return out; } diff --git a/src/libslic3r/SLA/EigenMesh3D.hpp b/src/libslic3r/SLA/EigenMesh3D.hpp index c92094756f..15176f0cbb 100644 --- a/src/libslic3r/SLA/EigenMesh3D.hpp +++ b/src/libslic3r/SLA/EigenMesh3D.hpp @@ -2,6 +2,7 @@ #define SLA_EIGENMESH3D_H #include +#include "libslic3r/SLA/Hollowing.hpp" namespace Slic3r { @@ -10,7 +11,6 @@ class TriangleMesh; namespace sla { struct Contour3D; -struct DrainHole; /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree. @@ -23,6 +23,11 @@ class EigenMesh3D { double m_ground_level = 0, m_gnd_offset = 0; std::unique_ptr m_aabb; + + // This holds a copy of holes in the mesh. Initialized externally + // by load_mesh setter. + std::vector m_holes; + public: EigenMesh3D(const TriangleMesh&); @@ -41,57 +46,58 @@ public: // Result of a raycast class hit_result { - double m_t = std::nan(""); + // m_t holds a distance from m_source to the intersection. + double m_t = infty(); const EigenMesh3D *m_mesh = nullptr; Vec3d m_dir; Vec3d m_source; - Vec3d m_normal = Vec3d::Zero(); + Vec3d m_normal; friend class EigenMesh3D; // A valid object of this class can only be obtained from // EigenMesh3D::query_ray_hit method. explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {} public: + // This denotes no hit on the mesh. + static inline constexpr double infty() { return std::numeric_limits::infinity(); } - // This can create a placeholder object which is invalid (not created - // by a query_ray_hit call) but the distance can be preset to - // a specific value for distinguishing the placeholder. - inline hit_result(double val = std::nan("")): m_t(val) {} + explicit inline hit_result(double val = infty()) : m_t(val) {} inline double distance() const { return m_t; } inline const Vec3d& direction() const { return m_dir; } inline const Vec3d& source() const { return m_source; } inline Vec3d position() const { return m_source + m_dir * m_t; } inline bool is_valid() const { return m_mesh != nullptr; } - inline bool is_hit() const { return m_normal != Vec3d::Zero(); } - + inline bool is_hit() const { return m_t != infty(); } + // Hit_result can decay into a double as the hit distance. inline operator double() const { return distance(); } inline const Vec3d& normal() const { - if(!is_valid()) - throw std::runtime_error("EigenMesh3D::hit_result::normal() " - "called on invalid object."); + assert(is_valid()); return m_normal; } - + inline bool is_inside() const { - return normal().dot(m_dir) > 0; + return is_hit() && normal().dot(m_dir) > 0; } }; + // Inform the object about location of holes + // creates internal copy of the vector + void load_holes(const std::vector& holes) { + m_holes = holes; + } + // Casting a ray on the mesh, returns the distance where the hit occures. - hit_result query_ray_hit(const Vec3d &s, - const Vec3d &dir, - const std::vector* holes = nullptr) const; + hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; // Casts a ray on the mesh and returns all hits std::vector query_ray_hits(const Vec3d &s, const Vec3d &dir) const; // Iterates over hits and holes and returns the true hit, possibly // on the inside of a hole. - hit_result filter_hits(const std::vector& obj_hits, - const std::vector& holes) const; + hit_result filter_hits(const std::vector& obj_hits) const; class si_result { double m_value; diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index a510ab3da0..46e60f91cb 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -152,7 +153,7 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, const Eigen::ParametrizedLine ray(s, dir.normalized()); for (size_t i=0; i<2; ++i) - out[i] = std::make_pair(std::nan(""), Vec3d::Zero()); + out[i] = std::make_pair(sla::EigenMesh3D::hit_result::infty(), Vec3d::Zero()); const float sqr_radius = pow(radius, 2.f); @@ -170,6 +171,11 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, if (! is_approx(ray.direction().dot(normal), 0.f)) { for (size_t i=1; i<=1; --i) { Vec3f cylinder_center = pos+i*height*normal; + if (i == 0) { + // The hole base can be identical to mesh surface if it is flat + // let's better move the base outward a bit + cylinder_center -= EPSILON*normal; + } base = Eigen::Hyperplane(normal, cylinder_center); Vec3f intersection = ray.intersectionPoint(base); // Only accept the point if it is inside the cylinder base. @@ -184,7 +190,7 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, { // In case the line was perpendicular to the cylinder axis, previous // block was skipped, but base will later be assumed to be valid. - base = Eigen::Hyperplane(normal, pos); + base = Eigen::Hyperplane(normal, pos-EPSILON*normal); } // In case there is still an intersection to be found, check the wall diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 36361f9ca7..66b6ea3f41 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -77,9 +77,6 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po m_throw_on_cancel(); Vec3f& p = points[point_id].pos; // Project the point upward and downward and choose the closer intersection with the mesh. - //bool up = igl::ray_mesh_intersect(p.cast(), Vec3f(0., 0., 1.), m_V, m_F, hit_up); - //bool down = igl::ray_mesh_intersect(p.cast(), Vec3f(0., 0., -1.), m_V, m_F, hit_down); - sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., 1.)); sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast(), Vec3d(0., 0., -1.)); @@ -90,10 +87,6 @@ void SupportPointGenerator::project_onto_mesh(std::vector& po continue; sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down; - //int fid = hit.face(); - //Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); - //p = (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))).cast(); - p = p + (hit.distance() * hit.direction()).cast(); } }); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 46a6160c1b..68afb73919 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -778,7 +778,7 @@ void SupportTreeBuildsteps::filter() nn = Vec3d(std::cos(azimuth) * std::sin(polar), std::sin(azimuth) * std::sin(polar), std::cos(polar)).normalized(); - t = oresult.score; + t = EigenMesh3D::hit_result(oresult.score); } } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 55359cc5c1..3135c33dde 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -253,6 +253,11 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // calculate heights of slices (slices are calculated already) const std::vector& heights = po.m_model_height_levels; + + // Tell the mesh where drain holes are. Although the points are + // calculated on slices, the algorithm then raycasts the points + // so they actually lie on the mesh. + po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); throw_if_canceled(); sla::SupportPointGenerator::Config config; @@ -321,6 +326,7 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); po.m_supportdata->cfg = make_support_cfg(po.m_config); + po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; From 735f82c01988d65efb8befa2c828747c76db5b34 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 29 Nov 2019 13:49:38 +0100 Subject: [PATCH 045/130] Hollowing gizmo has now its own shortcut [H], fixed a situation with supports showed in editing mode --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++++- src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 10 ++++------ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 16 +++++----------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 - 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c49c7c66d3..c5ade144ed 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1301,8 +1301,11 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } -void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) +bool GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { + if (m_render_sla_auxiliaries == visible) + return false; + for (GLVolume* vol : m_volumes.volumes) { if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) @@ -1311,6 +1314,7 @@ void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje } m_render_sla_auxiliaries = visible; + return true; } void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3430c3692f..32974a8df9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -462,7 +462,7 @@ public: void reset_volumes(); int check_volumes_outside_state() const; - void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); + bool toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void update_instance_printable_state_for_object(size_t obj_idx); void update_instance_printable_state_for_objects(std::vector& object_idxs); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 9c0e2c4d90..50bfb767a3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -37,7 +37,7 @@ GLGizmoHollow::~GLGizmoHollow() bool GLGizmoHollow::on_init() { - m_shortcut_key = WXK_CONTROL_L; + m_shortcut_key = WXK_CONTROL_H; m_desc["head_diameter"] = _(L("Head diameter")) + ": "; m_desc["lock_supports"] = _(L("Lock supports under new islands")); @@ -595,7 +595,6 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) m_volume_with_cavity->set_volume_transformation(volume_trafo); m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); } - m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); } @@ -751,10 +750,9 @@ RENDER_AGAIN: if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); - if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { - m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); - force_refresh = true; - } + // make sure supports are shown/hidden as appropriate + m_imgui->checkbox(m_desc["show_supports"], m_show_supports); + force_refresh = m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); m_imgui->end(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 29eb72fc52..9d8b7687c6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -738,11 +738,6 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_l bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. RENDER_AGAIN: - //m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - //const ImVec2 window_size(m_imgui->scaled(18.f, 16.f)); - //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); - //ImGui::SetNextWindowSize(ImVec2(window_size)); - const float approx_height = m_imgui->scaled(18.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -761,7 +756,6 @@ RENDER_AGAIN: float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); - bool force_refresh = false; bool remove_selected = false; bool remove_all = false; @@ -917,11 +911,11 @@ RENDER_AGAIN: m_imgui->end(); - if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode - m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); - force_refresh = true; - } - m_old_editing_state = m_editing_mode; + // Make sure that the supports are (not) visible as they should be. This + // is done on each refresh because the user can switch the editing mode + // before background process finishes. + force_refresh = m_parent.toggle_sla_auxiliaries_visibility( + ! m_editing_mode, m_model_object, m_active_instance); if (remove_selected || remove_all) { force_refresh = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index e536aab40f..1de241a53c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -99,7 +99,6 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? - bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). float m_new_point_head_diameter; // Size of a new point. CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited float m_old_point_head_diameter = 0.; // the same From 14d56929e7d27bf21bbda1aa9f178c219a62a6e7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 29 Nov 2019 14:09:53 +0100 Subject: [PATCH 046/130] SLA drain holes can be removed by right click, Ctrl+A selects all holes --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index f4427638b1..1ca764a031 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -491,7 +491,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } } - else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown)) + else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::RightDown)) { // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object pending_right_up = true; @@ -642,7 +642,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ { // Sla gizmo selects all support points - if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::SelectAll)) + if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::SelectAll)) processed = true; break; From a6e737f05adae6e69a662b7147642fd3e72fe798 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 3 Dec 2019 10:45:49 +0100 Subject: [PATCH 047/130] Various small changes in hollowing gizmo: - changed layout of controls - fixed supports appearing when they shouldn't - fixed clipping plane (holes were hidden at a bit different position of the plane then appropriate) - when hollowing is done, clipping plane is automatically moved to show the cavity - the dialog should no longed overlap bottom-left corner controls - gizmo controls now correspond to config values in ObjectSettings box and both update each other - added undo/redo support when manipulating holes --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 206 ++++++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 48 ++---- 2 files changed, 151 insertions(+), 103 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 50bfb767a3..07ed8450e2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -38,20 +38,17 @@ GLGizmoHollow::~GLGizmoHollow() bool GLGizmoHollow::on_init() { m_shortcut_key = WXK_CONTROL_H; - - m_desc["head_diameter"] = _(L("Head diameter")) + ": "; - m_desc["lock_supports"] = _(L("Lock supports under new islands")); + m_desc["enable"] = _(L("Hollow this object")); + m_desc["preview"] = _(L("Preview")); + m_desc["offset"] = _(L("Offset")) + ": "; + m_desc["quality"] = _(L("Quality")) + ": "; + m_desc["closing_distance"] = _(L("Closing distance")) + ": "; + m_desc["hole_diameter"] = _(L("Hole diameter")) + ": "; + m_desc["hole_depth"] = _(L("Hole depth")) + ": "; m_desc["remove_selected"] = _(L("Remove selected holes")); m_desc["remove_all"] = _(L("Remove all holes")); - m_desc["apply_changes"] = _(L("Apply changes")); - m_desc["discard_changes"] = _(L("Discard changes")); - m_desc["minimal_distance"] = _(L("Minimal points distance")) + ": "; - m_desc["points_density"] = _(L("Support points density")) + ": "; - m_desc["auto_generate"] = _(L("Auto-generate points")); - m_desc["manual_editing"] = _(L("Manual editing")); m_desc["clipping_of_view"] = _(L("Clipping of view"))+ ": "; m_desc["reset_direction"] = _(L("Reset direction")); - m_desc["hollow"] = _(L("Hollow")); m_desc["show_supports"] = _(L("Show supports")); return true; @@ -206,8 +203,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const ::glPopMatrix(); } - if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty()) { - // The supports are hidden in the editing mode, so it makes no sense to render the cuts. + if (m_show_supports && m_supports_clipper && ! m_supports_clipper->get_triangles().empty()) { ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); ::glBegin(GL_TRIANGLES); @@ -250,7 +246,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped(drain_hole.pos.cast())) + if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) continue; // First decide about the color of the point. @@ -565,7 +561,13 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair GLGizmoHollow::get_hollowing_parameters() const { - return std::make_pair(m_mesh, sla::HollowingConfig{double(m_offset), double(m_accuracy), double(m_closing_d)}); + // FIXME this function is probably obsolete, caller could + // get the data from model config himself + std::vector opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); + float offset = static_cast(opts[0])->value; + float quality = static_cast(opts[1])->value; + float closing_d = static_cast(opts[2])->value; + return std::make_pair(m_mesh, sla::HollowingConfig{double(offset), double(quality), double(closing_d)}); } void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) @@ -596,6 +598,10 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); } m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); + if (m_clipping_plane_distance == 0.f) { + m_clipping_plane_distance = 0.5f; + update_clipping_plane(); + } } std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const @@ -643,74 +649,151 @@ void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) bool first_run = true; // This is a hack to redraw the button when all points are removed, // so it is not delayed until the background process finishes. RENDER_AGAIN: - const float approx_height = m_imgui->scaled(18.0f); + const float approx_height = m_imgui->scaled(20.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_bg_alpha(0.5f); m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: - const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f); + const float settings_sliders_left = + std::max(std::max(m_imgui->calc_text_size(m_desc.at("offset")).x, + m_imgui->calc_text_size(m_desc.at("quality")).x), + m_imgui->calc_text_size(m_desc.at("closing_distance")).x) + + m_imgui->scaled(1.f); + const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("head_diameter")).x + m_imgui->scaled(1.f); + const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); - const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f); - const float lock_supports_width_approx = m_imgui->calc_text_size(m_desc.at("lock_supports")).x + m_imgui->scaled(2.f); + //const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f); float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); - window_width = std::max(std::max(window_width, buttons_width_approx), lock_supports_width_approx); + window_width = std::max(std::max(window_width, /*buttons_width_approx*/0.f), 0.f); + + { + std::vector opts = get_config_options({"hollowing_enable"}); + m_enable_hollowing = static_cast(opts[0])->value; + if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { + m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; + wxGetApp().obj_list()->update_and_show_object_settings_item(); + } + } + m_imgui->disabled_begin(! m_enable_hollowing); + + ImGui::SameLine(); + if (m_imgui->button(m_desc["preview"])) + hollow_mesh(); + + std::vector opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); + float offset = static_cast(opts[0])->value; + float quality = static_cast(opts[1])->value; + float closing_d = static_cast(opts[2])->value; + + m_imgui->text(m_desc.at("offset")); + ImGui::SameLine(settings_sliders_left); + ImGui::PushItemWidth(window_width - settings_sliders_left); + ImGui::SliderFloat(" ", &offset, 0.f, 5.f, "%.1f"); + bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider + bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider + bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider + + m_imgui->text(m_desc.at("quality")); + ImGui::SameLine(settings_sliders_left); + ImGui::SliderFloat(" ", &quality, 0.f, 1.f, "%.1f"); + slider_clicked |= ImGui::IsItemClicked(); + slider_edited |= ImGui::IsItemEdited(); + slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + + m_imgui->text(m_desc.at("closing_distance")); + ImGui::SameLine(settings_sliders_left); + ImGui::SliderFloat(" ", &closing_d, 0.f, 10.f, "%.1f"); + slider_clicked |= ImGui::IsItemClicked(); + slider_edited |= ImGui::IsItemEdited(); + slider_released |= ImGui::IsItemDeactivatedAfterEdit(); + + if (slider_clicked) { + m_offset_stash = offset; + m_quality_stash = quality; + m_closing_d_stash = closing_d; + } + if (slider_edited || slider_released) { + if (slider_released) { + m_model_object->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; + m_model_object->config.opt("hollowing_quality", true)->value = m_quality_stash; + m_model_object->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); + } + m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; + m_model_object->config.opt("hollowing_quality", true)->value = quality; + m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; + if (slider_released) + wxGetApp().obj_list()->update_and_show_object_settings_item(); + } + + m_imgui->disabled_end(); bool force_refresh = false; bool remove_selected = false; bool remove_all = false; - if (m_imgui->button(m_desc.at("hollow"))) - hollow_mesh(); + m_imgui->text(" "); // vertical gap float diameter_upper_cap = 20.f; //static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; if (m_new_hole_radius > diameter_upper_cap) m_new_hole_radius = diameter_upper_cap; - m_imgui->text(m_desc.at("head_diameter")); + m_imgui->text(m_desc.at("hole_diameter")); ImGui::SameLine(diameter_slider_left); ImGui::PushItemWidth(window_width - diameter_slider_left); + ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f"); + bool clicked = ImGui::IsItemClicked(); + bool edited = ImGui::IsItemEdited(); + bool deactivated = ImGui::IsItemDeactivatedAfterEdit(); + + m_imgui->text(m_desc["hole_depth"]); + ImGui::SameLine(diameter_slider_left); + m_new_hole_height -= HoleStickOutLength; + ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f"); + m_new_hole_height += HoleStickOutLength; + + clicked |= ImGui::IsItemClicked(); + edited |= ImGui::IsItemEdited(); + deactivated |= ImGui::IsItemDeactivatedAfterEdit(); + // Following is a nasty way to: // - save the initial value of the slider before one starts messing with it // - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene // - take correct undo/redo snapshot after the user is done with moving the slider - float initial_value = m_new_hole_radius; - ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f"); - if (ImGui::IsItemClicked()) { - if (m_old_hole_radius == 0.f) - m_old_hole_radius = initial_value; + if (! m_selection_empty) { + if (clicked) { + m_holes_stash = m_model_object->sla_drain_holes; + } + if (edited) { + for (size_t idx=0; idxsla_drain_holes[idx].radius = m_new_hole_radius; + m_model_object->sla_drain_holes[idx].height = m_new_hole_height; + } + } + if (deactivated) { + // momentarily restore the old value to take snapshot + sla::DrainHoles new_holes = m_model_object->sla_drain_holes; + m_model_object->sla_drain_holes = m_holes_stash; + float backup_rad = m_new_hole_radius; + float backup_hei = m_new_hole_height; + for (size_t i=0; isla_drain_holes = new_holes; + } } - if (ImGui::IsItemEdited()) { - for (size_t idx=0; idxsla_drain_holes[idx].radius = m_new_hole_radius; - } - if (ImGui::IsItemDeactivatedAfterEdit()) { - // momentarily restore the old value to take snapshot - for (size_t idx=0; idxsla_drain_holes[idx].radius = m_old_hole_radius; - float backup = m_new_hole_radius; - m_new_hole_radius = m_old_hole_radius; - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); - m_new_hole_radius = backup; - for (size_t idx=0; idxsla_drain_holes[idx].radius = m_new_hole_radius; - m_old_hole_radius = 0.f; - } - - // !!!! Something as above should be done for the undo/redo - m_imgui->text("Hole height: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_new_hole_height, 0.1f, 10.f, "%.1f"); - for (size_t idx=0; idxsla_drain_holes[idx].height = m_new_hole_height; m_imgui->disabled_begin(m_selection_empty); remove_selected = m_imgui->button(m_desc.at("remove_selected")); @@ -720,19 +803,6 @@ RENDER_AGAIN: remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); - m_imgui->text(" "); // vertical gap - - - m_imgui->text("Offset: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_offset, 0.f, 5.f, "%.1f"); - m_imgui->text("Quality: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_accuracy, 0.f, 1.f, "%.1f"); - m_imgui->text("Closing distance: "); - ImGui::SameLine(); - ImGui::SliderFloat(" ", &m_closing_d, 0.f, 10.f, "%.1f"); - // Following is rendered in both editing and non-editing mode: m_imgui->text(""); if (m_clipping_plane_distance == 0.f) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 428a978c48..1c548e14ab 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -43,32 +43,6 @@ private: mutable int m_print_object_idx = -1; mutable int m_print_objects_count = -1; - class CacheEntry { - public: - CacheEntry() : - drain_hole(sla::DrainHole()), selected(false) {} - - CacheEntry(const sla::DrainHole& point, bool sel = false) : - drain_hole(point), selected(sel) {} - - bool operator==(const CacheEntry& rhs) const { - return (drain_hole == rhs.drain_hole); - } - - bool operator!=(const CacheEntry& rhs) const { - return ! ((*this) == rhs); - } - - sla::DrainHole drain_hole; - bool selected; // whether the point is selected - - template - void serialize(Archive & ar) - { - ar(drain_hole, selected); - } - }; - public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); ~GLGizmoHollow() override; @@ -90,7 +64,6 @@ private: void on_render() const override; void on_render_for_picking() const override; - //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; @@ -102,15 +75,21 @@ private: bool m_show_supports = true; float m_new_hole_radius; // Size of a new hole. float m_new_hole_height = 5.f; - float m_old_hole_radius = 0.; // undo/redo - so we know what state was edited - Vec3f m_hole_before_drag = Vec3f::Zero(); - float m_minimal_point_distance_stash = 0.f; // and again - float m_density_stash = 0.f; // and again mutable std::vector m_selected; // which holes are currently selected - float m_offset = 2.0f; - float m_accuracy = 0.5f; - float m_closing_d = 2.f; + bool m_enable_hollowing = true; + + // Stashes to keep data for undo redo. Is taken after the editing + // is done, the data are updated continuously. + float m_offset_stash = 2.0f; + float m_quality_stash = 0.5f; + float m_closing_d_stash = 2.f; + Vec3f m_hole_before_drag = Vec3f::Zero(); + + + sla::DrainHoles m_holes_stash; + + float m_clipping_plane_distance = 0.f; std::unique_ptr m_clipping_plane; @@ -130,7 +109,6 @@ private: std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; - //void find_intersecting_facets(const igl::AABB* aabb, const Vec3f& normal, double offset, std::vector& out) const; // Methods that do the model_object and editing cache synchronization, // editing mode selection, etc: From 3b0241c98b342722b87d0c794a0bf6684ff91b4c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 6 Dec 2019 10:59:05 +0100 Subject: [PATCH 048/130] Fix trianglemesh slicer error with empty meshes --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 07ed8450e2..6b4f00a8cd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -131,7 +131,7 @@ void GLGizmoHollow::on_render() const void GLGizmoHollow::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f) + if (m_clipping_plane_distance == 0.f || mesh()->empty()) return; // Get transformation of the instance @@ -172,7 +172,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const if (m_print_object_idx >= 0) { const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; - if (print_object->is_step_done(slaposSupportTree)) { + if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { // If the supports are already calculated, save the timestamp of the respective step // so we can later tell they were recalculated. size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 9d8b7687c6..ac3dd80589 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -136,7 +136,7 @@ void GLGizmoSlaSupports::on_render() const void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f) + if (m_clipping_plane_distance == 0.f || m_mesh->empty()) return; // Get transformation of the instance @@ -177,7 +177,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const if (m_print_object_idx >= 0) { const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; - if (print_object->is_step_done(slaposSupportTree)) { + if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { // If the supports are already calculated, save the timestamp of the respective step // so we can later tell they were recalculated. size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; From 5be66a52c0b2a16794ba722974bd68f21fd405b9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 6 Dec 2019 15:47:58 +0100 Subject: [PATCH 049/130] add drain hole 3mf export and import --- src/libslic3r/Format/3mf.cpp | 165 +++++++++++++++++++++++++++- src/libslic3r/Format/3mf.hpp | 4 + src/libslic3r/libslic3r.h | 16 +++ tests/libslic3r/libslic3r_tests.cpp | 27 +++++ 4 files changed, 210 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index ff3cf777d3..ee40adb566 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -51,6 +51,7 @@ const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config"; const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt"; const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml"; const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt"; +const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt"; const char* MODEL_TAG = "model"; const char* RESOURCES_TAG = "resources"; @@ -227,6 +228,15 @@ bool is_valid_object_type(const std::string& type) return false; } +template std::string string_printf(const char *const fmt, Args &&...args) +{ + int bufflen = snprintf(nullptr, 0, fmt, std::forward(args)...); + std::vector buffer(size_t(bufflen), '\0'); + + snprintf(buffer.data(), buffer.size(), fmt, std::forward(args)...); + return std::string(buffer.begin(), buffer.end()); +} + namespace Slic3r { //! macro used to mark string used at localization, @@ -379,6 +389,7 @@ namespace Slic3r { typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; typedef std::map> IdToSlaSupportPointsMap; + typedef std::map> IdToSlaDrainHolesMap; // Version of the 3mf file unsigned int m_version; @@ -397,6 +408,7 @@ namespace Slic3r { IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerConfigRangesMap m_layer_config_ranges; IdToSlaSupportPointsMap m_sla_support_points; + IdToSlaDrainHolesMap m_sla_drain_holes; std::string m_curr_metadata_name; std::string m_curr_characters; std::string m_name; @@ -416,6 +428,7 @@ namespace Slic3r { void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename); bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); @@ -621,6 +634,11 @@ namespace Slic3r { // extract sla support points file _extract_sla_support_points_from_archive(archive, stat); } + else if (boost::algorithm::iequals(name, SLA_DRAIN_HOLES_FILE)) + { + // extract sla support points file + _extract_sla_drain_holes_from_archive(archive, stat); + } else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) { // extract slic3r print config file @@ -670,6 +688,11 @@ namespace Slic3r { model_object->sla_support_points = obj_sla_support_points->second; model_object->sla_points_status = sla::PointsStatus::UserModified; } + + IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1); + if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) { + model_object->sla_drain_holes = obj_drain_holes->second; + } IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); if (obj_metadata != m_objects_metadata.end()) @@ -942,8 +965,9 @@ namespace Slic3r { // Info on format versioning - see 3mf.hpp int version = 0; - if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) { - objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string + std::string key("support_points_format_version="); + if (!objects.empty() && objects[0].find(key) != std::string::npos) { + objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string version = std::stoi(objects[0]); objects.erase(objects.begin()); // pop the header } @@ -1009,6 +1033,90 @@ namespace Slic3r { } } } + + void _3MF_Importer::_extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) + { + if (stat.m_uncomp_size > 0) + { + std::string buffer(size_t(stat.m_uncomp_size), 0); + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == 0) + { + add_error("Error while reading sla support points data to buffer"); + return; + } + + if (buffer.back() == '\n') + buffer.pop_back(); + + std::vector objects; + boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off); + + // Info on format versioning - see 3mf.hpp + int version = 0; + std::string key("drain_holes_format_version="); + if (!objects.empty() && objects[0].find(key) != std::string::npos) { + objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string + version = std::stoi(objects[0]); + objects.erase(objects.begin()); // pop the header + } + + for (const std::string& object : objects) + { + std::vector object_data; + boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off); + + if (object_data.size() != 2) + { + add_error("Error while reading object data"); + continue; + } + + std::vector object_data_id; + boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off); + if (object_data_id.size() != 2) + { + add_error("Error while reading object id"); + continue; + } + + int object_id = std::atoi(object_data_id[1].c_str()); + if (object_id == 0) + { + add_error("Found invalid object id"); + continue; + } + + IdToSlaDrainHolesMap::iterator object_item = m_sla_drain_holes.find(object_id); + if (object_item != m_sla_drain_holes.end()) + { + add_error("Found duplicated SLA drain holes"); + continue; + } + + std::vector object_data_points; + boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off); + + sla::DrainHoles sla_drain_holes; + + if (version == 1) { + for (unsigned int i=0; isla_drain_holes; + if (!drain_holes.empty()) + { + out += string_printf(fmt, count); + + // Store the layer height profile as a single space separated list. + for (size_t i = 0; i < drain_holes.size(); ++i) + out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"), + drain_holes[i].pos(0), + drain_holes[i].pos(1), + drain_holes[i].pos(2), + drain_holes[i].normal(0), + drain_holes[i].normal(1), + drain_holes[i].normal(2), + drain_holes[i].radius, + drain_holes[i].height); + + out += "\n"; + } + } + + if (!out.empty()) + { + // Adds version header at the beginning: + out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out; + + if (!mz_zip_writer_add_mem(&archive, SLA_DRAIN_HOLES_FILE.c_str(), static_cast(out.data()), out.length(), mz_uint(MZ_DEFAULT_COMPRESSION))) + { + add_error("Unable to add sla support points file to archive"); + return false; + } + } + return true; + } bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config) { diff --git a/src/libslic3r/Format/3mf.hpp b/src/libslic3r/Format/3mf.hpp index 2e85b7f59e..7da0ce7fc4 100644 --- a/src/libslic3r/Format/3mf.hpp +++ b/src/libslic3r/Format/3mf.hpp @@ -19,6 +19,10 @@ namespace Slic3r { enum { support_points_format_version = 1 }; + + enum { + drain_holes_format_version = 1 + }; class Model; class DynamicPrintConfig; diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index afbf94fa39..f2dd001143 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -183,6 +183,22 @@ static inline bool is_approx(Number value, Number test_value) return std::fabs(double(value) - double(test_value)) < double(EPSILON); } +template +std::string string_printf(const char *const fmt, Args &&...args) +{ + static const size_t INITIAL_LEN = 1024; + std::vector buffer(INITIAL_LEN, '\0'); + + int bufflen = snprintf(buffer.data(), INITIAL_LEN - 1, fmt, std::forward(args)...); + + if (bufflen >= int(INITIAL_LEN)) { + buffer.resize(size_t(bufflen) + 1); + snprintf(buffer.data(), buffer.size(), fmt, std::forward(args)...); + } + + return std::string(buffer.begin(), buffer.begin() + bufflen); +} + } // namespace Slic3r #endif diff --git a/tests/libslic3r/libslic3r_tests.cpp b/tests/libslic3r/libslic3r_tests.cpp index caf5b3b9ac..b9c615d90a 100644 --- a/tests/libslic3r/libslic3r_tests.cpp +++ b/tests/libslic3r/libslic3r_tests.cpp @@ -11,4 +11,31 @@ TEST_CASE("sort_remove_duplicates", "[utils]") { REQUIRE(data_src == data_dst); } +TEST_CASE("string_printf", "[utils]") { + SECTION("Empty format with empty data should return empty string") { + std::string outs = Slic3r::string_printf(""); + REQUIRE(outs.empty()); + } + + SECTION("String output length should be the same as input") { + std::string outs = Slic3r::string_printf("1234"); + REQUIRE(outs.size() == 4); + } + + SECTION("String format should be interpreted as with sprintf") { + std::string outs = Slic3r::string_printf("%d %f %s", 10, 11.4, " This is a string"); + char buffer[1024]; + + sprintf(buffer, "%d %f %s", 10, 11.4, " This is a string"); + + REQUIRE(outs.compare(buffer) == 0); + } + + SECTION("String format should survive large input data") { + std::string input(2048, 'A'); + std::string outs = Slic3r::string_printf("%s", input.c_str()); + REQUIRE(outs.compare(input) == 0); + } +} + } From 5d77c345675046343316dbffc14b634087903f6b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 6 Dec 2019 16:28:14 +0100 Subject: [PATCH 050/130] Forgot to remove string_printf from 3mf.cpp --- src/libslic3r/Format/3mf.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index ee40adb566..516dc2919e 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -228,15 +228,6 @@ bool is_valid_object_type(const std::string& type) return false; } -template std::string string_printf(const char *const fmt, Args &&...args) -{ - int bufflen = snprintf(nullptr, 0, fmt, std::forward(args)...); - std::vector buffer(size_t(bufflen), '\0'); - - snprintf(buffer.data(), buffer.size(), fmt, std::forward(args)...); - return std::string(buffer.begin(), buffer.end()); -} - namespace Slic3r { //! macro used to mark string used at localization, From 0c4297b4f9b0d241a60280616c37ceb3fba0c71c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 10 Dec 2019 13:40:40 +0100 Subject: [PATCH 051/130] Small fix for OpenVDB find script --- cmake/modules/FindOpenVDB.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 70bbe05f55..3400fa786b 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -158,7 +158,7 @@ set(_OPENVDB_ROOT_SEARCH_DIR "") # Additionally try and use pkconfig to find OpenVDB -find_package(PkgConfig ${_quiet} ${_required}) +find_package(PkgConfig ${_quiet} ) pkg_check_modules(PC_OpenVDB QUIET OpenVDB) # ------------------------------------------------------------------------ From 135660decfd2cdb6d8b26059c05fa084e399bff2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 12 Dec 2019 10:37:15 +0100 Subject: [PATCH 052/130] SLA gizmos fix: clipping of points/holes always used coords from the first instance Few warnings fixed --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 16 ++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 6b4f00a8cd..0bbaa19c42 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -323,7 +323,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const if (m_clipping_plane_distance == 0.f) return false; - Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; + Vec3d transformed_point = m_model_object->instances[m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; return m_clipping_plane->is_point_clipped(transformed_point); } @@ -534,7 +534,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos return false; } -void GLGizmoHollow::delete_selected_points(bool force) +void GLGizmoHollow::delete_selected_points() { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); @@ -564,10 +564,10 @@ std::pair GLGizmoHollow::get_hollowi // FIXME this function is probably obsolete, caller could // get the data from model config himself std::vector opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); - float offset = static_cast(opts[0])->value; - float quality = static_cast(opts[1])->value; - float closing_d = static_cast(opts[2])->value; - return std::make_pair(m_mesh, sla::HollowingConfig{double(offset), double(quality), double(closing_d)}); + double offset = static_cast(opts[0])->value; + double quality = static_cast(opts[1])->value; + double closing_d = static_cast(opts[2])->value; + return std::make_pair(m_mesh, sla::HollowingConfig{offset, quality, closing_d}); } void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) @@ -833,10 +833,10 @@ RENDER_AGAIN: if (remove_all) { select_point(AllPoints); - delete_selected_points(true); // true - delete regardless of locked status + delete_selected_points(); } if (remove_selected) - delete_selected_points(false); // leave locked points + delete_selected_points(); if (first_run) { first_run = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 1c548e14ab..8e022eb1e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -48,7 +48,7 @@ public: ~GLGizmoHollow() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); - void delete_selected_points(bool force = false); + void delete_selected_points(); ClippingPlane get_sla_clipping_plane() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index ac3dd80589..cf3f91dac7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -377,7 +377,7 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const if (m_clipping_plane_distance == 0.f) return false; - Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point; + Vec3d transformed_point = m_model_object->instances[m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; return m_clipping_plane->is_point_clipped(transformed_point); } From 9805b02a25f3d24120ba8331cf14e00afe998708 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 12 Dec 2019 13:19:16 +0100 Subject: [PATCH 053/130] Removed an obsolete variable from MeshRaycaster --- src/slic3r/GUI/MeshUtils.cpp | 8 ++++---- src/slic3r/GUI/MeshUtils.hpp | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 573952f75a..3bf99f73c6 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -195,10 +195,10 @@ Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const Vec3d closest_point; m_emesh.squared_distance(point.cast(), idx, closest_point); if (normal) { - const stl_triangle_vertex_indices& indices = m_mesh->its.indices[idx]; - Vec3f a(m_mesh->its.vertices[indices(1)] - m_mesh->its.vertices[indices(0)]); - Vec3f b(m_mesh->its.vertices[indices(2)] - m_mesh->its.vertices[indices(0)]); - *normal = Vec3f(a.cross(b)); + auto indices = m_emesh.F().row(idx); + Vec3d a(m_emesh.V().row(indices(1)) - m_emesh.V().row(indices(0))); + Vec3d b(m_emesh.V().row(indices(2)) - m_emesh.V().row(indices(0))); + *normal = Vec3f(a.cross(b).cast()); } return closest_point.cast(); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 56d726596f..b4ad030114 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -105,10 +105,10 @@ private: // whether certain points are visible or obscured by the mesh etc. class MeshRaycaster { public: - // The class saves a const* to the mesh, calledz is responsible - // for making sure it does not get invalid. + // The class makes a copy of the mesh as EigenMesh3D. + // The pointer can be invalidated after constructor returns. MeshRaycaster(const TriangleMesh& mesh) - : m_emesh(mesh), m_mesh(&mesh) + : m_emesh(mesh) {} // Given a mouse position, this returns true in case it is on the mesh. @@ -138,7 +138,6 @@ public: private: sla::EigenMesh3D m_emesh; - const TriangleMesh* m_mesh = nullptr; }; From 2cb30f3641d13ccbc93bcaebd35ac2892e1e606b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 13 Dec 2019 13:42:10 +0100 Subject: [PATCH 054/130] First prototype of CGAL hole-drilling --- src/libslic3r/CMakeLists.txt | 2 ++ src/slic3r/CMakeLists.txt | 2 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 30 ++++++++++++++++++++----- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 2571748fdb..922d153e43 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -117,6 +117,8 @@ add_library(libslic3r STATIC "${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h" Line.cpp Line.hpp + MeshBoolean.cpp + MeshBoolean.hpp Model.cpp Model.hpp Arrange.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 859c17e89d..eec2b6a01b 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -176,7 +176,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi CGAL::CGAL) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 0bbaa19c42..cad0243f08 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -1,4 +1,3 @@ -// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoHollow.hpp" #include "slic3r/GUI/GLCanvas3D.hpp" #include "slic3r/GUI/Gizmos/GLGizmos.hpp" @@ -12,6 +11,8 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" #include "libslic3r/SLAPrint.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/MeshBoolean.hpp" namespace Slic3r { @@ -107,15 +108,15 @@ void GLGizmoHollow::on_render() const if (! m_mesh) const_cast(this)->update_mesh(); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + if (m_volume_with_cavity) { m_parent.get_shader().start_using(); m_volume_with_cavity->render(); m_parent.get_shader().stop_using(); } - glsafe(::glEnable(GL_BLEND)); - glsafe(::glEnable(GL_DEPTH_TEST)); - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); if (m_quadric != nullptr && selection.is_from_single_instance()) @@ -583,12 +584,31 @@ void GLGizmoHollow::hollow_mesh() wxGetApp().plater()->hollow(); } + void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) { // Called from Plater when the UI job finishes m_cavity_mesh = std::move(mesh); - if(m_cavity_mesh) {// create a new GLVolume that only has the cavity inside + if(m_cavity_mesh) { + // First subtract the holes: + if (! m_model_object->sla_drain_holes.empty()) { + TriangleMesh holes_mesh; + for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { + TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/8); + Eigen::Quaternionf q; + Transform3f m = Transform3f::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3f::UnitZ(), hole.normal).toRotationMatrix(); + hole_mesh.transform(m.cast()); + hole_mesh.translate(hole.pos); + holes_mesh.merge(hole_mesh); + //MeshBoolean::minus(*m_cavity_mesh.get(), hole_mesh); + } + MeshBoolean::minus(*m_cavity_mesh.get(), holes_mesh); + } + + + // create a new GLVolume that only has the cavity inside Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); From f60ff1c7ce02a577003910902a27414612b8de14 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 13 Dec 2019 15:08:44 +0100 Subject: [PATCH 055/130] Fixup of previous commit: actually adding new source files --- src/libslic3r/MeshBoolean.cpp | 63 +++++++++++++++++++++++++++++++++++ src/libslic3r/MeshBoolean.hpp | 17 ++++++++++ 2 files changed, 80 insertions(+) create mode 100644 src/libslic3r/MeshBoolean.cpp create mode 100644 src/libslic3r/MeshBoolean.hpp diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp new file mode 100644 index 0000000000..e639319a20 --- /dev/null +++ b/src/libslic3r/MeshBoolean.cpp @@ -0,0 +1,63 @@ +#include "MeshBoolean.hpp" + +// Include igl first. It defines "L" macro which then clashes with our localization +#include +#undef L + +#include "libslic3r/TriangleMesh.hpp" + + +namespace Slic3r { +namespace MeshBoolean { + +typedef Eigen::Map> MapMatrixXfUnaligned; +typedef Eigen::Map> MapMatrixXiUnaligned; + +static TriangleMesh eigen_to_triangle_mesh(const Eigen::MatrixXd& VC, const Eigen::MatrixXi& FC) +{ + Pointf3s vertices; + for (size_t i=0; i facets; + for (size_t i=0; i(); + Eigen::MatrixXi FA = MapMatrixXiUnaligned(A.its.indices.front().data(), A.its.indices.size(), 3); + Eigen::MatrixXd VB = MapMatrixXfUnaligned(B.its.vertices.front().data(), B.its.vertices.size(), 3).cast(); + Eigen::MatrixXi FB = MapMatrixXiUnaligned(B.its.indices.front().data(), B.its.indices.size(), 3); + + Eigen::MatrixXd VC; + Eigen::MatrixXi FC; + igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_MINUS); + igl::copyleft::cgal::mesh_boolean(VA, FA, VB, FB, boolean_type, VC, FC); + + A = eigen_to_triangle_mesh(VC, FC); +} + + +void self_union(TriangleMesh& mesh) +{ + Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); + Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); + + Eigen::MatrixXd VC; + Eigen::MatrixXi FC; + + igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION); + igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC); + mesh = eigen_to_triangle_mesh(VC, FC); +} + + + +} // namespace MeshBoolean +} // namespace Slic3r diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp new file mode 100644 index 0000000000..783bde3f58 --- /dev/null +++ b/src/libslic3r/MeshBoolean.hpp @@ -0,0 +1,17 @@ +#ifndef libslic3r_MeshBoolean_hpp_ +#define libslic3r_MeshBoolean_hpp_ + + +namespace Slic3r { + +class TriangleMesh; + +namespace MeshBoolean { + +void minus(TriangleMesh& A, const TriangleMesh& B); +void self_union(TriangleMesh& mesh); + + +} // namespace MeshBoolean +} // namespace Slic3r +#endif // libslic3r_MeshBoolean_hpp_ From a9403319b7767e920d7d993e91bf0623c91c7b4b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 09:47:31 +0100 Subject: [PATCH 056/130] Separate Job, ProgressStatusBar and ProgressIndicator * Separate GUI::Job * make use of ProgressIndicator interface * make ProgressStatusbar independent from GUI::App --- src/slic3r/CMakeLists.txt | 1 + src/slic3r/GUI/Job.hpp | 154 +++++++++++++++++++++++++++ src/slic3r/GUI/MainFrame.cpp | 3 +- src/slic3r/GUI/MainFrame.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 143 ++++--------------------- src/slic3r/GUI/ProgressIndicator.hpp | 63 ++--------- src/slic3r/GUI/ProgressStatusBar.cpp | 12 ++- src/slic3r/GUI/ProgressStatusBar.hpp | 20 ++-- 8 files changed, 206 insertions(+), 192 deletions(-) create mode 100644 src/slic3r/GUI/Job.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index eec2b6a01b..5ce464eb19 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -140,6 +140,7 @@ set(SLIC3R_GUI_SOURCES GUI/ProgressStatusBar.cpp GUI/PrintHostDialogs.cpp GUI/PrintHostDialogs.hpp + GUI/Job.hpp GUI/Mouse3DController.cpp GUI/Mouse3DController.hpp Utils/Http.cpp diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp new file mode 100644 index 0000000000..9accd0ef39 --- /dev/null +++ b/src/slic3r/GUI/Job.hpp @@ -0,0 +1,154 @@ +#ifndef JOB_HPP +#define JOB_HPP + +#include + +#include +#include +#include + +#include + +#include + +namespace Slic3r { namespace GUI { + +// 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; + boost::thread m_thread; + std::atomic m_running{false}, m_canceled{false}; + bool m_finalized = false; + std::shared_ptr m_progress; + + 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); + } + + 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() { m_finalized = true; } + + bool is_finalized() const { return m_finalized; } + +public: + Job(std::shared_ptr pri) : m_progress(pri) + { + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { + auto msg = evt.GetString(); + if (!msg.empty()) + m_progress->set_status_text(msg.ToUTF8().data()); + + if (m_finalized) return; + + m_progress->set_progress(evt.GetInt()); + if (evt.GetInt() == status_range()) { + // set back the original range and cancel callback + m_progress->set_range(m_range); + m_progress->set_cancel_callback(); + wxEndBusyCursor(); + + finalize(); + + // dont do finalization again for the same process + m_finalized = true; + } + }); + } + + Job(const Job &) = delete; + Job(Job &&) = delete; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = delete; + + 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 = m_progress->get_range(); + m_progress->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); + m_progress->set_cancel_callback( + [this]() { m_canceled.store(true); }); + + m_finalized = false; + + // Changing cursor to busy + wxBeginBusyCursor(); + + try { // Execute the job + m_thread = create_thread([this] { this->run(); }); + } 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) + { + if (!m_thread.joinable()) return true; + + if (timeout_ms <= 0) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) + return false; + + return true; + } + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } +}; + +} +} + +#endif // JOB_HPP diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index e613e69150..9e70fbaa2e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -58,7 +58,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S #endif // _WIN32 // initialize status bar - m_statusbar.reset(new ProgressStatusBar(this)); + m_statusbar = std::make_shared(this); + m_statusbar->set_font(GUI::wxGetApp().normal_font()); m_statusbar->embed(this); m_statusbar->set_status_text(_(L("Version")) + " " + SLIC3R_VERSION + diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 28bd0242b5..1a75680928 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -135,7 +135,7 @@ public: Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; - std::unique_ptr m_statusbar; + std::shared_ptr m_statusbar; }; } // GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index df705f25f6..43d2f06a58 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -69,6 +69,7 @@ #include "Camera.hpp" #include "Mouse3DController.hpp" #include "Tab.hpp" +#include "Job.hpp" #include "PresetBundle.hpp" #include "BackgroundSlicingProcess.hpp" #include "ProgressStatusBar.hpp" @@ -1457,136 +1458,30 @@ struct Plater::priv // 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 + class PlaterJob: public Job { - int m_range = 100; - boost::thread m_thread; - priv * m_plater = nullptr; - std::atomic 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(), ""); - } - + priv *m_plater; 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; } const priv &plater() const { 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() + void finalize() override { // 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()) + if (!Job::was_canceled()) plater().update(unsigned(UpdateParams::FORCE_FULL_SCREEN_REFRESH)); + + Job::finalize(); } 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 &&) = delete; - Job &operator=(const Job &) = delete; - Job &operator=(Job &&) = delete; - - 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_thread = create_thread([this] { this->run(); }); - } 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) - { - if (!m_thread.joinable()) return true; - - if (timeout_ms <= 0) - m_thread.join(); - else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) - return false; - - return true; - } - - bool is_running() const { return m_running.load(); } - void cancel() { m_canceled.store(true); } + PlaterJob(priv *_plater) + : Job(_plater->statusbar()), m_plater(_plater) + {} }; enum class Jobs : size_t { @@ -1595,7 +1490,7 @@ struct Plater::priv Hollow }; - class ArrangeJob : public Job + class ArrangeJob : public PlaterJob { using ArrangePolygon = arrangement::ArrangePolygon; using ArrangePolygons = arrangement::ArrangePolygons; @@ -1712,7 +1607,7 @@ struct Plater::priv } public: - using Job::Job; + using PlaterJob::PlaterJob; int status_range() const override { return int(m_selected.size()); } @@ -1729,17 +1624,17 @@ struct Plater::priv } }; - class RotoptimizeJob : public Job + class RotoptimizeJob : public PlaterJob { public: - using Job::Job; + using PlaterJob::PlaterJob; void process() override; }; - class HollowJob : public Job + class HollowJob : public PlaterJob { public: - using Job::Job; + using PlaterJob::PlaterJob; void prepare() override; void process() override; void finalize() override; @@ -1847,7 +1742,7 @@ struct Plater::priv void reset_all_gizmos(); void update_ui_from_settings(); - ProgressStatusBar* statusbar(); + std::shared_ptr statusbar(); std::string get_config(const std::string &key) const; BoundingBoxf bed_shape_bb() const; BoundingBox scaled_bed_shape_bb() const; @@ -2266,9 +2161,9 @@ void Plater::priv::update_ui_from_settings() preview->get_canvas3d()->update_ui_from_settings(); } -ProgressStatusBar* Plater::priv::statusbar() +std::shared_ptr Plater::priv::statusbar() { - return main_frame->m_statusbar.get(); + return main_frame->m_statusbar; } std::string Plater::priv::get_config(const std::string &key) const diff --git a/src/slic3r/GUI/ProgressIndicator.hpp b/src/slic3r/GUI/ProgressIndicator.hpp index 280ba63ac8..674a81ba26 100644 --- a/src/slic3r/GUI/ProgressIndicator.hpp +++ b/src/slic3r/GUI/ProgressIndicator.hpp @@ -11,58 +11,17 @@ namespace Slic3r { */ class ProgressIndicator { public: - using CancelFn = std::function; // Cancel function signature. - -private: - float m_state = .0f, m_max = 1.f, m_step; - CancelFn m_cancelfunc = [](){}; - -public: - - inline virtual ~ProgressIndicator() {} - - /// Get the maximum of the progress range. - float max() const { return m_max; } - - /// Get the current progress state - float state() const { return m_state; } - - /// Set the maximum of the progress range - virtual void max(float maxval) { m_max = maxval; } - - /// Set the current state of the progress. - virtual void state(float val) { m_state = val; } - - /** - * @brief Number of states int the progress. Can be used instead of giving a - * maximum value. - */ - virtual void states(unsigned statenum) { - m_step = m_max / statenum; - } - - /// Message shown on the next status update. - virtual void message(const std::string&) = 0; - - /// Title of the operation. - virtual void title(const std::string&) = 0; - - /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const std::string& fmt, ...); - - /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { m_cancelfunc = func; } - - /** - * Explicitly shut down the progress indicator and call the associated - * callback. - */ - virtual void cancel() { m_cancelfunc(); } - - /// Convenience function to call message and status update in one function. - void update(float st, const std::string& msg) { - message(msg); state(st); - } + + /// Cancel callback function type + using CancelFn = std::function; + + virtual ~ProgressIndicator() = default; + + virtual void set_range(int range) = 0; + virtual void set_cancel_callback(CancelFn = CancelFn()) = 0; + virtual void set_progress(int pr) = 0; + virtual void set_status_text(const char *) = 0; // utf8 char array + virtual int get_range() const = 0; }; } diff --git a/src/slic3r/GUI/ProgressStatusBar.cpp b/src/slic3r/GUI/ProgressStatusBar.cpp index 33e4855cdd..1ec2b81930 100644 --- a/src/slic3r/GUI/ProgressStatusBar.cpp +++ b/src/slic3r/GUI/ProgressStatusBar.cpp @@ -15,8 +15,7 @@ namespace Slic3r { ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) - : self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe, - id == -1 ? wxID_ANY : id)} + : self{new wxStatusBar(parent, id == -1 ? wxID_ANY : id)} , m_prog{new wxGauge(self, wxGA_HORIZONTAL, 100, @@ -32,7 +31,6 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id) m_prog->Hide(); m_cancelbutton->Hide(); - self->SetFont(GUI::wxGetApp().normal_font()); self->SetFieldsCount(3); int w[] = {-1, 150, 155}; self->SetStatusWidths(3, w); @@ -149,8 +147,7 @@ void ProgressStatusBar::run(int rate) void ProgressStatusBar::embed(wxFrame *frame) { - wxFrame* mf = frame ? frame : GUI::wxGetApp().mainframe; - if(mf) mf->SetStatusBar(self); + if(frame) frame->SetStatusBar(self); } void ProgressStatusBar::set_status_text(const wxString& txt) @@ -173,6 +170,11 @@ wxString ProgressStatusBar::get_status_text() const return self->GetStatusText(); } +void ProgressStatusBar::set_font(const wxFont &font) +{ + self->SetFont(font); +} + void ProgressStatusBar::show_cancel_button() { if(m_cancelbutton) m_cancelbutton->Show(); diff --git a/src/slic3r/GUI/ProgressStatusBar.hpp b/src/slic3r/GUI/ProgressStatusBar.hpp index 1aab67d5aa..faeb7a34ef 100644 --- a/src/slic3r/GUI/ProgressStatusBar.hpp +++ b/src/slic3r/GUI/ProgressStatusBar.hpp @@ -6,6 +6,8 @@ #include #include +#include "ProgressIndicator.hpp" + class wxTimer; class wxGauge; class wxButton; @@ -14,6 +16,7 @@ class wxStatusBar; class wxWindow; class wxFrame; class wxString; +class wxFont; namespace Slic3r { @@ -22,7 +25,7 @@ namespace Slic3r { * of the Slicer main window. It consists of a message area to the left and a * progress indication area to the right with an optional cancel button. */ -class ProgressStatusBar +class ProgressStatusBar : public ProgressIndicator { wxStatusBar *self; // we cheat! It should be the base class but: perl! wxGauge *m_prog; @@ -30,30 +33,29 @@ class ProgressStatusBar std::unique_ptr m_timer; public: - /// Cancel callback function type - using CancelFn = std::function; ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); - ~ProgressStatusBar(); + ~ProgressStatusBar() override; int get_progress() const; // if the argument is less than 0 it shows the last state or // pulses if no state was set before. - void set_progress(int); - int get_range() const; - void set_range(int = 100); + void set_progress(int) override; + int get_range() const override; + void set_range(int = 100) override; void show_progress(bool); void start_busy(int = 100); void stop_busy(); inline bool is_busy() const { return m_busy; } - void set_cancel_callback(CancelFn = CancelFn()); + void set_cancel_callback(CancelFn = CancelFn()) override; inline void reset_cancel_callback() { set_cancel_callback(); } void run(int rate); void embed(wxFrame *frame = nullptr); void set_status_text(const wxString& txt); void set_status_text(const std::string& txt); - void set_status_text(const char *txt); + void set_status_text(const char *txt) override; wxString get_status_text() const; + void set_font(const wxFont &font); // Temporary methods to satisfy Perl side void show_cancel_button(); From 464ec8da6ab42b1014aa2b39b58dd480860f2cc3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 11:08:36 +0100 Subject: [PATCH 057/130] Add MeshBoolean.cpp with CGAL as object library. --- src/libslic3r/CMakeLists.txt | 21 +++++++++++++++++++-- src/slic3r/CMakeLists.txt | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 922d153e43..1ddb7fa8b1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -117,8 +117,6 @@ add_library(libslic3r STATIC "${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h" Line.cpp Line.hpp - MeshBoolean.cpp - MeshBoolean.hpp Model.cpp Model.hpp Arrange.hpp @@ -220,6 +218,24 @@ add_library(libslic3r STATIC SLA/Clustering.hpp ) +if (SLIC3R_STATIC) + set(CGAL_Boost_USE_STATIC_LIBS ON) + set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE TRUE) +endif () + +find_package(CGAL REQUIRED) + +add_library(libslic3r_cgal OBJECT MeshBoolean.cpp MeshBoolean.hpp) +target_include_directories(libslic3r_cgal PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + $ + $) +target_compile_definitions(libslic3r_cgal PRIVATE + $) +target_compile_options(libslic3r_cgal PRIVATE + $) +target_sources(libslic3r PRIVATE $) + encoding_check(libslic3r) target_compile_definitions(libslic3r PUBLIC -DUSE_TBB -DTBB_USE_CAPTURED_EXCEPTION=0) @@ -240,6 +256,7 @@ target_link_libraries(libslic3r qhull semver TBB::tbb + $ ${CMAKE_DL_LIBS} ) diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5ce464eb19..4156481b0d 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -177,7 +177,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) encoding_check(libslic3r_gui) -target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi CGAL::CGAL) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () From 66759e10e348c627ace37085c225113841f768e6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 11:02:54 +0100 Subject: [PATCH 058/130] Add opencsg demo sandbox --- sandboxes/CMakeLists.txt | 3 +- sandboxes/opencsg/CMakeLists.txt | 21 ++ sandboxes/opencsg/Canvas.hpp | 112 +++++++ sandboxes/opencsg/GLScene.cpp | 509 +++++++++++++++++++++++++++++++ sandboxes/opencsg/GLScene.hpp | 321 +++++++++++++++++++ sandboxes/opencsg/main.cpp | 195 ++++++++++++ src/libslic3r/SLA/Hollowing.cpp | 14 +- src/libslic3r/SLA/Hollowing.hpp | 3 + 8 files changed, 1176 insertions(+), 2 deletions(-) create mode 100644 sandboxes/opencsg/CMakeLists.txt create mode 100644 sandboxes/opencsg/Canvas.hpp create mode 100644 sandboxes/opencsg/GLScene.cpp create mode 100644 sandboxes/opencsg/GLScene.hpp create mode 100644 sandboxes/opencsg/main.cpp diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 91d6ca225e..181c70d48e 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,3 +1,4 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) -add_subdirectory(meshboolean) +#add_subdirectory(meshboolean) +add_subdirectory(opencsg) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt new file mode 100644 index 0000000000..9a216a7dc1 --- /dev/null +++ b/sandboxes/opencsg/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) + +project(OpenCSG-example) + +add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp Canvas.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) + +find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html) +find_package(OpenGL REQUIRED) +find_package(GLEW REQUIRED) +find_package(OpenCSG REQUIRED) +find_package(GLUT REQUIRED) +include(${wxWidgets_USE_FILE}) + + +target_link_libraries(opencsg_example libslic3r) +target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS}) +target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) +target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} GLEW::GLEW OpenCSG::opencsg GLUT::GLUT OpenGL::OpenGL -lXrandr -lXext -lX11) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp new file mode 100644 index 0000000000..85d490ddf5 --- /dev/null +++ b/sandboxes/opencsg/Canvas.hpp @@ -0,0 +1,112 @@ +#ifndef CANVAS_HPP +#define CANVAS_HPP + +#include + +// For compilers that support precompilation, includes "wx/wx.h". +#include +#ifndef WX_PRECOMP +#include +#endif + +#include +#include + +#include "GLScene.hpp" + +namespace Slic3r { namespace GL { + +class Canvas: public wxGLCanvas, public Slic3r::GL::Display +{ + std::unique_ptr m_context; +public: + + void set_active(long w, long h) override + { + SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + void repaint(long width, long height) override + { + Slic3r::GL::Display::repaint(width, height); + } + + using Slic3r::GL::Display::repaint; + + void swap_buffers() override { SwapBuffers(); } + + void on_scroll(long v, long d, Slic3r::GL::MouseInput::WheelAxis wa) override + { + Slic3r::GL::Display::on_scroll(v, d, wa); + } + + template + Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) + { + auto ctx = new wxGLContext(this); + if (!ctx || !ctx->IsOK()) { + wxMessageBox("Could not create OpenGL context.", "Error", + wxOK | wxICON_ERROR); + return; + } + + m_context.reset(ctx); + + Bind( + wxEVT_MOUSEWHEEL, + [this](wxMouseEvent &evt) { + on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), + evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? + Slic3r::GL::MouseInput::waVertical : + Slic3r::GL::MouseInput::waHorizontal); + }, + GetId()); + + Bind( + wxEVT_MOTION, + [this](wxMouseEvent &evt) { + on_moved_to(evt.GetPosition().x, evt.GetPosition().y); + }, + GetId()); + + Bind( + wxEVT_RIGHT_DOWN, + [this](wxMouseEvent & /*evt*/) { on_right_click_down(); }, + GetId()); + + Bind( + wxEVT_RIGHT_UP, + [this](wxMouseEvent & /*evt*/) { on_right_click_up(); }, + GetId()); + + Bind( + wxEVT_LEFT_DOWN, + [this](wxMouseEvent & /*evt*/) { on_left_click_down(); }, + GetId()); + + Bind( + wxEVT_LEFT_UP, + [this](wxMouseEvent & /*evt*/) { on_left_click_up(); }, + GetId()); + + Bind(wxEVT_PAINT, [this](wxPaintEvent &) { + // This is required even though dc is not used otherwise. + wxPaintDC dc(this); + + // Set the OpenGL viewport according to the client size of this + // canvas. This is done here rather than in a wxSizeEvent handler + // because our OpenGL rendering context (and thus viewport setting) is + // used with multiple canvases: If we updated the viewport in the + // wxSizeEvent handler, changing the size of one canvas causes a + // viewport setting that is wrong when next another canvas is + // repainted. + const wxSize ClientSize = GetClientSize(); + repaint(ClientSize.x, ClientSize.y); + }, GetId()); + } +}; + +}} // namespace Slic3r::GL + +#endif // CANVAS_HPP diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp new file mode 100644 index 0000000000..5f4a205327 --- /dev/null +++ b/sandboxes/opencsg/GLScene.cpp @@ -0,0 +1,509 @@ +#include "GLScene.hpp" +#include +#include +#include + +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include + +#ifndef NDEBUG +#define HAS_GLSAFE +#endif + +#ifdef HAS_GLSAFE +extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name); +inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } +#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false) +#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false) + +void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name) +{ + GLenum err = glGetError(); + if (err == GL_NO_ERROR) + return; + const char *sErr = 0; + switch (err) { + case GL_INVALID_ENUM: sErr = "Invalid Enum"; break; + case GL_INVALID_VALUE: sErr = "Invalid Value"; break; + // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd + case GL_INVALID_OPERATION: sErr = "Invalid Operation"; break; + case GL_STACK_OVERFLOW: sErr = "Stack Overflow"; break; + case GL_STACK_UNDERFLOW: sErr = "Stack Underflow"; break; + case GL_OUT_OF_MEMORY: sErr = "Out Of Memory"; break; + default: sErr = "Unknown"; break; + } + BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr; + assert(false); +} + +#else +inline void glAssertRecentCall() { } +#define glsafe(cmd) cmd +#define glcheck() +#endif + +namespace Slic3r { namespace GL { + +Scene::Scene() = default; + +Scene::~Scene() = default; + +void renderfps () { + static std::ostringstream fpsStream; + static int fps = 0; + static int ancient = 0; + static int last = 0; + static int msec = 0; + + last = msec; + msec = glutGet(GLUT_ELAPSED_TIME); + if (last / 1000 != msec / 1000) { + + float correctedFps = fps * 1000.0f / float(msec - ancient); + fpsStream.str(""); + fpsStream << "fps: " << correctedFps << std::ends; + + ancient = msec; + fps = 0; + } + glDisable(GL_DEPTH_TEST); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glColor3f(0.0f, 0.0f, 0.0f); + glRasterPos2f(-1.0f, -1.0f); + glDisable(GL_LIGHTING); + std::string s = fpsStream.str(); + for (unsigned int i=0; icsg_primitives()); + + glDepthFunc(GL_EQUAL); + for (auto& p : m_scene->csg_primitives()) p->render(); + glDepthFunc(GL_LESS); + + for (auto& p : m_scene->free_primitives()) p->render(); + + glFlush(); +} + +template::value_type> +std::vector transform_pts( + It from, It to, Trafo &&tr, GetPt &&point) +{ + auto ret = reserve_vector(to - from); + for(auto it = from; it != to; ++it) { + V v = *it; + v.pos = tr * point(*it); + ret.emplace_back(std::move(v)); + } + return ret; +} + +void Scene::set_print(uqptr &&print) +{ + m_print = std::move(print); + + for (const SLAPrintObject *po : m_print->objects()) { + const ModelObject *mo = po->model_object(); + TriangleMesh msh = mo->raw_mesh(); + + sla::DrainHoles holedata = mo->sla_drain_holes; + + for (const ModelInstance *mi : mo->instances) { + + TriangleMesh mshinst = msh; + auto interior = po->hollowed_interior_mesh(); + interior.transform(po->trafo().inverse()); + + mshinst.merge(interior); + mshinst.require_shared_vertices(); + + mi->transform_mesh(&mshinst); + + auto bb = mshinst.bounding_box(); + auto center = bb.center().cast(); + mshinst.translate(-center); + + mshinst.require_shared_vertices(); + add_mesh(mshinst, OpenCSG::Intersection, 15); + + auto tr = Transform3f::Identity(); + tr.translate(-center); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.pos; + }); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.normal; + }); + } + + for (const sla::DrainHole &holept : holedata) { + TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); + holemesh.require_shared_vertices(); + add_mesh(holemesh, OpenCSG::Subtraction, 1); + } + } + + // Notify displays + call(&Display::on_scene_updated, m_displays); +} + +BoundingBoxf3 Scene::get_bounding_box() const +{ + return m_print->model().bounding_box(); +} + +shptr Scene::add_mesh(const TriangleMesh &mesh) +{ + auto p = std::make_shared(); + p->load_mesh(mesh); + m_primitives.emplace_back(p); + m_primitives_free.emplace_back(p.get()); + return p; +} + +shptr Scene::add_mesh(const TriangleMesh &mesh, OpenCSG::Operation o, unsigned c) +{ + auto p = std::make_shared(o, c); + p->load_mesh(mesh); + m_primitives.emplace_back(p); + m_primitives_csg.emplace_back(p.get()); + return p; +} + +void IndexedVertexArray::push_geometry(float x, float y, float z, float nx, float ny, float nz) +{ + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + if (this->vertices_and_normals_interleaved_VBO_id != 0) + return; + + if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) + this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); + this->vertices_and_normals_interleaved.emplace_back(nx); + this->vertices_and_normals_interleaved.emplace_back(ny); + this->vertices_and_normals_interleaved.emplace_back(nz); + this->vertices_and_normals_interleaved.emplace_back(x); + this->vertices_and_normals_interleaved.emplace_back(y); + this->vertices_and_normals_interleaved.emplace_back(z); + + this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); +} + +void IndexedVertexArray::push_triangle(int idx1, int idx2, int idx3) { + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + if (this->vertices_and_normals_interleaved_VBO_id != 0) + return; + + if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) + this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); + this->triangle_indices.emplace_back(idx1); + this->triangle_indices.emplace_back(idx2); + this->triangle_indices.emplace_back(idx3); + this->triangle_indices_size = this->triangle_indices.size(); +} + +void IndexedVertexArray::load_mesh(const TriangleMesh &mesh) +{ + assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); + assert(quad_indices.empty() && triangle_indices_size == 0); + assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); + + this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); + + int vertices_count = 0; + for (size_t i = 0; i < mesh.stl.stats.number_of_facets; ++i) { + const stl_facet &facet = mesh.stl.facet_start[i]; + for (int j = 0; j < 3; ++j) + this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); + + this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); + vertices_count += 3; + } +} + +void IndexedVertexArray::finalize_geometry() +{ + assert(this->vertices_and_normals_interleaved_VBO_id == 0); + assert(this->triangle_indices_VBO_id == 0); + assert(this->quad_indices_VBO_id == 0); + + if (!this->vertices_and_normals_interleaved.empty()) { + glsafe( + ::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, + this->vertices_and_normals_interleaved_VBO_id)); + glsafe( + ::glBufferData(GL_ARRAY_BUFFER, + GLsizeiptr( + this->vertices_and_normals_interleaved.size() * + 4), + this->vertices_and_normals_interleaved.data(), + GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + this->vertices_and_normals_interleaved.clear(); + } + if (!this->triangle_indices.empty()) { + glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->triangle_indices_VBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, + GLsizeiptr(this->triangle_indices.size() * 4), + this->triangle_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + this->triangle_indices.clear(); + } + if (!this->quad_indices.empty()) { + glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->quad_indices_VBO_id)); + glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, + GLsizeiptr(this->quad_indices.size() * 4), + this->quad_indices.data(), GL_STATIC_DRAW)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + this->quad_indices.clear(); + } +} + +void IndexedVertexArray::release_geometry() +{ + if (this->vertices_and_normals_interleaved_VBO_id) { + glsafe( + ::glDeleteBuffers(1, + &this->vertices_and_normals_interleaved_VBO_id)); + this->vertices_and_normals_interleaved_VBO_id = 0; + } + if (this->triangle_indices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id)); + this->triangle_indices_VBO_id = 0; + } + if (this->quad_indices_VBO_id) { + glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id)); + this->quad_indices_VBO_id = 0; + } + this->clear(); +} + +void IndexedVertexArray::render() const +{ + assert(this->vertices_and_normals_interleaved_VBO_id != 0); + assert(this->triangle_indices_VBO_id != 0 || + this->quad_indices_VBO_id != 0); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, + this->vertices_and_normals_interleaved_VBO_id)); + glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), + reinterpret_cast(3 * sizeof(float)))); + glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); + + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + + // Render using the Vertex Buffer Objects. + if (this->triangle_indices_size > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->triangle_indices_VBO_id)); + glsafe(::glDrawElements(GL_TRIANGLES, + GLsizei(this->triangle_indices_size), + GL_UNSIGNED_INT, nullptr)); + glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + if (this->quad_indices_size > 0) { + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, + this->quad_indices_VBO_id)); + glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), + GL_UNSIGNED_INT, nullptr)); + glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); +} + +void IndexedVertexArray::clear() { + this->vertices_and_normals_interleaved.clear(); + this->triangle_indices.clear(); + this->quad_indices.clear(); + vertices_and_normals_interleaved_size = 0; + triangle_indices_size = 0; + quad_indices_size = 0; +} + +void IndexedVertexArray::shrink_to_fit() { + this->vertices_and_normals_interleaved.shrink_to_fit(); + this->triangle_indices.shrink_to_fit(); + this->quad_indices.shrink_to_fit(); +} + +void Primitive::render() +{ + glsafe(::glPushMatrix()); + glsafe(::glMultMatrixd(m_trafo.get_matrix().data())); + m_geom.render(); + glsafe(::glPopMatrix()); +} + +void Display::clear_screen() +{ + glViewport(0, 0, GLsizei(m_size.x()), GLsizei(m_size.y())); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void Display::set_active(long width, long height) +{ + static int argc = 0; + + if (!m_initialized) { + glewInit(); + glutInit(&argc, nullptr); + m_initialized = true; + } + + m_size = {width, height}; + + // gray background + glClearColor(0.9f, 0.9f, 0.9f, 1.0f); + + // Enable two OpenGL lights + GLfloat light_diffuse[] = { 1.0f, 1.0f, 0.0f, 1.0f}; // White diffuse light + GLfloat light_position0[] = {-1.0f, -1.0f, -1.0f, 0.0f}; // Infinite light location + GLfloat light_position1[] = { 1.0f, 1.0f, 1.0f, 0.0f}; // Infinite light location + + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, light_position0); + glEnable(GL_LIGHT0); + glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT1, GL_POSITION, light_position1); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHTING); + glEnable(GL_NORMALIZE); + + // Use depth buffering for hidden surface elimination + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + + m_camera->set_screen(width, height); +} + +void Display::repaint(long width, long height) +{ + if (m_size.x() != width || m_size.y() != height) + m_camera->set_screen(width, height); + + m_size = {width, height}; + + clear_screen(); + + m_camera->view(); + render_scene(); + + renderfps(); + + swap_buffers(); +} + +void Display::on_scroll(long v, long d, MouseInput::WheelAxis wa) +{ + m_wheel_pos += v / d; + + m_camera->set_zoom(m_wheel_pos); + + m_scene->on_scroll(v, d, wa); + + repaint(m_size.x(), m_size.y()); +} + +void Display::on_moved_to(long x, long y) +{ + if (m_left_btn) { + m_camera->rotate((Vec2i{x, y} - m_mouse_pos).cast()); + repaint(); + } + m_mouse_pos = {x, y}; +} + +void CSGSettings::set_csg_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } + +void Display::on_scene_updated() +{ + auto bb = m_scene->get_bounding_box(); + double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); + m_wheel_pos = long(2 * d); + m_camera->set_zoom(m_wheel_pos); + repaint(); +} + +void Display::set_scene(shptr scene) +{ + m_scene = scene; + m_scene->add_display(shared_from_this()); +} + +void Camera::view() +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gluLookAt(0.0, m_zoom, 0.0, /* eye is at (0,zoom,0) */ + m_referene.x(), m_referene.y(), m_referene.z(), + 0.0, 0.0, 1.0); /* up is in positive Y direction */ + + // TODO Could have been set in prevoius gluLookAt in first argument + glRotatef(m_rot.y(), 1.0, 0.0, 0.0); + glRotatef(m_rot.x(), 0.0, 0.0, 1.0); + + // glClipPlane() +} + +void PerspectiveCamera::set_screen(long width, long height) +{ + // Setup the view of the CSG shape + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(45.0, width / double(height), .1, 200.0); + glMatrixMode(GL_MODELVIEW); +} + +bool enable_multisampling(bool e) +{ + if (!e) { glDisable(GL_MULTISAMPLE); return false; } + + GLint is_ms_context; + glGetIntegerv(GL_SAMPLE_BUFFERS, &is_ms_context); + + if (is_ms_context) { glEnable(GL_MULTISAMPLE); return true; } + else return false; +} + +}} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp new file mode 100644 index 0000000000..411b0db71d --- /dev/null +++ b/sandboxes/opencsg/GLScene.hpp @@ -0,0 +1,321 @@ +#ifndef GLSCENE_HPP +#define GLSCENE_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace Slic3r { + +class SLAPrint; + +namespace GL { + +template using shptr = std::shared_ptr; +template using uqptr = std::unique_ptr; +template using wkptr = std::weak_ptr; + +template> +using Collection = std::vector; + +template void cleanup(Collection> &listeners) { + auto it = std::remove_if(listeners.begin(), listeners.end(), + [](auto &l) { return !l.lock(); }); + listeners.erase(it, listeners.end()); +} + +template +void call(F &&f, Collection> &listeners, Args&&... args) { + for (auto &l : listeners) + if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); +} + +class MouseInput +{ +public: + + enum WheelAxis { + waVertical, waHorizontal + }; + + class Listener { + public: + + virtual ~Listener() = default; + + virtual void on_left_click_down() {} + virtual void on_left_click_up() {} + virtual void on_right_click_down() {} + virtual void on_right_click_up() {} + virtual void on_double_click() {} + virtual void on_scroll(long /*v*/, long /*delta*/, WheelAxis ) {} + virtual void on_moved_to(long /*x*/, long /*y*/) {} + }; + +private: + Collection> m_listeners; + +public: + virtual ~MouseInput() = default; + + virtual void left_click_down() + { + call(&Listener::on_left_click_down, m_listeners); + } + virtual void left_click_up() + { + call(&Listener::on_left_click_up, m_listeners); + } + virtual void right_click_down() + { + call(&Listener::on_right_click_down, m_listeners); + } + virtual void right_click_up() + { + call(&Listener::on_right_click_up, m_listeners); + } + virtual void double_click() + { + call(&Listener::on_double_click, m_listeners); + } + virtual void scroll(long v, long d, WheelAxis wa) + { + call(&Listener::on_scroll, m_listeners, v, d, wa); + } + virtual void move_to(long x, long y) + { + call(&Listener::on_moved_to, m_listeners, x, y); + } + + void add_listener(shptr listener) + { + m_listeners.emplace_back(listener); + cleanup(m_listeners); + } +}; + +class IndexedVertexArray { +public: + ~IndexedVertexArray() { release_geometry(); } + + // Vertices and their normals, interleaved to be used by void + // glInterleavedArrays(GL_N3F_V3F, 0, x) + Collection vertices_and_normals_interleaved; + Collection triangle_indices; + Collection quad_indices; + + // When the geometry data is loaded into the graphics card as Vertex + // Buffer Objects, the above mentioned std::vectors are cleared and the + // following variables keep their original length. + size_t vertices_and_normals_interleaved_size{ 0 }; + size_t triangle_indices_size{ 0 }; + size_t quad_indices_size{ 0 }; + + // IDs of the Vertex Array Objects, into which the geometry has been loaded. + // Zero if the VBOs are not sent to GPU yet. + unsigned int vertices_and_normals_interleaved_VBO_id{ 0 }; + unsigned int triangle_indices_VBO_id{ 0 }; + unsigned int quad_indices_VBO_id{ 0 }; + + + void push_geometry(float x, float y, float z, float nx, float ny, float nz); + + inline void push_geometry( + double x, double y, double z, double nx, double ny, double nz) + { + push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); + } + + inline void push_geometry(const Vec3d &p, const Vec3d &n) + { + push_geometry(p(0), p(1), p(2), n(0), n(1), n(2)); + } + + void push_triangle(int idx1, int idx2, int idx3); + + void load_mesh(const TriangleMesh &mesh); + + inline bool has_VBOs() const + { + return vertices_and_normals_interleaved_VBO_id != 0; + } + + // Finalize the initialization of the geometry & indices, + // upload the geometry and indices to OpenGL VBO objects + // and shrink the allocated data, possibly relasing it if it has been + // loaded into the VBOs. + void finalize_geometry(); + // Release the geometry data, release OpenGL VBOs. + void release_geometry(); + + void render() const; + + // Is there any geometry data stored? + bool empty() const { return vertices_and_normals_interleaved_size == 0; } + + void clear(); + + // Shrink the internal storage to tighly fit the data stored. + void shrink_to_fit(); +}; + +bool enable_multisampling(bool e = true); +void renderfps(); + +class Primitive : public OpenCSG::Primitive +{ + IndexedVertexArray m_geom; + Geometry::Transformation m_trafo; +public: + + using OpenCSG::Primitive::Primitive; + + Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} + + void render(); + + void translation(const Vec3d &offset) { m_trafo.set_offset(offset); } + void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); } + void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); } + void scale(double s) { scale({s, s, s}); } + + inline void load_mesh(const TriangleMesh &mesh) { + m_geom.load_mesh(mesh); + m_geom.finalize_geometry(); + } +}; + +class Scene; + +class Camera { +protected: + Vec2f m_rot = {0., 0.}; + Vec3d m_referene = {0., 0., 0.}; + double m_zoom = 0.; + double m_clip_z = 0.; +public: + + virtual ~Camera() = default; + + virtual void view(); + virtual void set_screen(long width, long height) = 0; + + void set_rotation(const Vec2f &rotation) { m_rot = rotation; } + void rotate(const Vec2f &rotation) { m_rot += rotation; } + void set_zoom(double z) { m_zoom = z; } + void set_reference_point(const Vec3d &p) { m_referene = p; } + void set_clip_z(double z) { m_clip_z = z; } +}; + +class PerspectiveCamera: public Camera { +public: + + void set_screen(long width, long height) override; +}; + +class CSGSettings { + OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; +public: + void set_csg_algo(OpenCSG::Algorithm alg); +}; + +class Display : public std::enable_shared_from_this, + public MouseInput::Listener +{ +protected: + shptr m_scene; + long m_wheel_pos = 0; + Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; + Vec2i m_size; + bool m_initialized = false, m_left_btn = false, m_right_btn = false; + + CSGSettings m_csgsettings; + + shptr m_camera; + +public: + Display(shptr scene = nullptr, shptr camera = nullptr) + : m_scene(scene) + , m_camera(camera ? camera : std::make_shared()) + {} + + virtual void swap_buffers() = 0; + + virtual void set_active(long width, long height); + + virtual void repaint(long width, long height); + void repaint() { repaint(m_size.x(), m_size.y()); } + + void set_scene(shptr scene); + shptr get_scene() { return m_scene; } + + bool is_initialized() const { return m_initialized; } + + void on_scroll(long v, long d, MouseInput::WheelAxis wa) override; + void on_moved_to(long x, long y) override; + void on_left_click_down() override { m_left_btn = true; } + void on_left_click_up() override { m_left_btn = false; } + void on_right_click_down() override { m_right_btn = true; } + void on_right_click_up() override { m_right_btn = false; } + + void move_clip_plane(double z) { m_camera->set_clip_z(z); } + + const CSGSettings & csgsettings() const { return m_csgsettings; } + void csgsettings(const CSGSettings &settings) { m_csgsettings = settings; } + + virtual void on_scene_updated(); + virtual void clear_screen(); + virtual void render_scene(); +}; + +class Scene: public MouseInput::Listener +{ + Collection> m_primitives; + Collection m_primitives_free; + Collection m_primitives_csg; + + uqptr m_print; +public: + + Scene(); + ~Scene(); + + const Collection& free_primitives() const + { + return m_primitives_free; + } + + const Collection& csg_primitives() const + { + return m_primitives_csg; + } + + void add_display(shptr disp) + { + m_displays.emplace_back(disp); + cleanup(m_displays); + } + + void set_print(uqptr &&print); + + BoundingBoxf3 get_bounding_box() const; + +protected: + + shptr add_mesh(const TriangleMesh &mesh); + shptr add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation op, + unsigned covexity); + +private: + + Collection> m_displays; +}; + +}} // namespace Slic3r::GL +#endif // GLSCENE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp new file mode 100644 index 0000000000..f2e9dc6c1b --- /dev/null +++ b/sandboxes/opencsg/main.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +#include + +#include +// For compilers that support precompilation, includes "wx/wx.h". +#include +#ifndef WX_PRECOMP + #include +#endif + +#include +#include +#include +#include + +#include "Canvas.hpp" +#include "GLScene.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/Format/3mf.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include "slic3r/GUI/Job.hpp" +#include "slic3r/GUI/ProgressStatusBar.hpp" +//#include "slic3r/GUI/3DEngine.hpp" + +using namespace Slic3r::GL; + +class MyFrame: public wxFrame +{ + std::shared_ptr m_canvas; + std::shared_ptr m_stbar; + std::unique_ptr m_ui_job; + + class SLAJob: public Slic3r::GUI::Job { + MyFrame *m_parent; + std::unique_ptr m_print; + std::string m_fname; + public: + + SLAJob(MyFrame *frame, const std::string &fname) + : Slic3r::GUI::Job{frame->m_stbar} + , m_parent{frame} + , m_fname{fname} + { + } + + void process() override + { + using Status = Slic3r::PrintBase::SlicingStatus; + + Slic3r::DynamicPrintConfig cfg; + auto model = Slic3r::Model::read_from_file(m_fname, &cfg); + + m_print = std::make_unique(); + m_print->apply(model, cfg); + + m_print->set_status_callback([this](const Status &status) { + update_status(status.percent, status.text); + }); + + m_print->process(); + } + + protected: + + void finalize() override + { + m_parent->m_canvas->get_scene()->set_print(std::move(m_print)); + m_parent->m_stbar->set_status_text( + wxString::Format("Model %s loaded.", m_fname)); + } + }; + +public: + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size): + wxFrame(nullptr, wxID_ANY, title, pos, size) + { + wxMenu *menuFile = new wxMenu; + menuFile->Append(wxID_OPEN); + menuFile->Append(wxID_EXIT); + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append( menuFile, "&File" ); + SetMenuBar( menuBar ); + + m_stbar = std::make_shared(this); + m_stbar->embed(this); + + SetStatusText( "Welcome to wxWidgets!" ); + + int attribList[] = + {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, + // RGB channels each should be allocated with 8 bit depth. One + // should almost certainly get these bit depths by default. + WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, + // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA + // drivers would most likely work with some alpha plane, but + // glReadPixels would not return the alpha channel on NVIDIA if + // not requested when the GL context is created. + WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, WX_GL_SAMPLE_BUFFERS, + GL_TRUE, WX_GL_SAMPLES, 4, 0}; + + m_canvas = std::make_shared(this, wxID_ANY, attribList, + wxDefaultPosition, wxDefaultSize, + wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); + + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); + auto slider_sizer = new wxBoxSizer(wxVERTICAL); + auto console_sizer = new wxBoxSizer(wxVERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); + auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + wxString algorithms [] = {"Default", "Different"}; + auto alg_select = new wxComboBox(control_panel, wxID_ANY, "Default", wxDefaultPosition, wxDefaultSize, 2, algorithms); + + slider_sizer->Add(slider, 1, wxEXPAND); + console_sizer->Add(toggle, 0, wxALL, 5); + console_sizer->Add(alg_select, 0, wxALL, 5); + controlsizer->Add(slider_sizer, 0, wxEXPAND); + controlsizer->Add(console_sizer, 1, wxEXPAND); + control_panel->SetSizer(controlsizer); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_canvas.get(), 1, wxEXPAND); + sizer->Add(control_panel, 0, wxEXPAND); + SetSizer(sizer); + + Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); + Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); + Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); + Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { + m_canvas->move_clip_plane(double(slider->GetValue())); + }, slider->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, toggle](wxCommandEvent &){ + enable_multisampling(toggle->GetValue()); + m_canvas->repaint(); + }, toggle->GetId()); + + m_canvas->set_scene(std::make_shared()); + } + +private: + + void OnExit(wxCommandEvent& /*event*/) + { + RemoveChild(m_canvas.get()); + m_canvas->Destroy(); + Close( true ); + } + + void OnOpen(wxCommandEvent &/*evt*/) + { + wxFileDialog dlg(this, "Select project file", + wxEmptyString, wxEmptyString, "*.3mf"); + + if (dlg.ShowModal() == wxID_OK) + { + m_ui_job = std::make_unique(this, dlg.GetPath().ToStdString()); + m_ui_job->start(); + } + } + + void OnShown(wxShowEvent&) + { + const wxSize ClientSize = GetClientSize(); + m_canvas->set_active(ClientSize.x, ClientSize.y); + + m_canvas->repaint(ClientSize.x, ClientSize.y); + + // Do the repaint continuously + Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + m_canvas->repaint(); + evt.RequestMore(); + }); + } +}; + +class App : public wxApp { + MyFrame *m_frame; +public: + bool OnInit() override { + + m_frame = new MyFrame( "PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768) ); + m_frame->Show( true ); + + return true; + } +}; + +wxIMPLEMENT_APP(App); diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 46e60f91cb..2b35722478 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -120,6 +121,17 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, hc.closing_distance)); } +Contour3D DrainHole::to_mesh() const +{ + auto r = double(radius); + auto h = double(height); + sla::Contour3D hole = sla::cylinder(r, h); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, normal.cast()); + for(auto& p : hole.points) p = q * p + pos.cast(); + return hole; +} + bool DrainHole::operator==(const DrainHole &sp) const { return (pos == sp.pos) && (normal == sp.normal) && @@ -131,7 +143,7 @@ bool DrainHole::is_inside(const Vec3f& pt) const { Eigen::Hyperplane plane(normal, pos); float dist = plane.signedDistance(pt); - if (dist < EPSILON || dist > height) + if (dist < float(EPSILON) || dist > height) return false; Eigen::ParametrizedLine axis(pos, normal); diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index d5c0d49fc8..ba1eb2d622 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace Slic3r { @@ -42,6 +43,8 @@ struct DrainHole bool get_intersections(const Vec3f& s, const Vec3f& dir, std::array, 2>& out) const; + Contour3D to_mesh() const; + template inline void serialize(Archive &ar) { ar(pos, normal, radius, height); From b1186e339dd7b756d5995f2e74de7fdc6144d696 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 12:36:44 +0100 Subject: [PATCH 059/130] Added opencsg parameter console --- sandboxes/opencsg/GLScene.cpp | 9 +++++- sandboxes/opencsg/GLScene.hpp | 15 +++++++-- sandboxes/opencsg/main.cpp | 59 +++++++++++++++++++++++++++++------ 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 5f4a205327..60eb0ce03b 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -454,7 +454,14 @@ void Display::on_moved_to(long x, long y) m_mouse_pos = {x, y}; } -void CSGSettings::set_csg_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } +void Display::apply_csgsettings(const CSGSettings &settings) +{ + using namespace OpenCSG; + m_csgsettings = settings; + setOption(AlgorithmSetting, m_csgsettings.get_algo()); + setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo()); + setOption(DepthBoundsOptimization, m_csgsettings.get_optimization()); +} void Display::on_scene_updated() { diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 411b0db71d..6beecb79a6 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -220,8 +220,17 @@ public: class CSGSettings { OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; + OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; + OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; public: - void set_csg_algo(OpenCSG::Algorithm alg); + int get_algo() const { return int(m_csgalg); } + void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } + + int get_depth_algo() const { return int(m_depth_algo); } + void set_depth_algo(OpenCSG::DepthComplexityAlgorithm alg) { m_depth_algo = alg; } + + int get_optimization() const { return int(m_optim); } + void set_optimization(OpenCSG::Optimization o) { m_optim = o; } }; class Display : public std::enable_shared_from_this, @@ -265,8 +274,8 @@ public: void move_clip_plane(double z) { m_camera->set_clip_z(z); } - const CSGSettings & csgsettings() const { return m_csgsettings; } - void csgsettings(const CSGSettings &settings) { m_csgsettings = settings; } + const CSGSettings & get_csgsettings() const { return m_csgsettings; } + void apply_csgsettings(const CSGSettings &settings); virtual void on_scene_updated(); virtual void clear_screen(); diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index f2e9dc6c1b..8c106fa85c 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -100,8 +100,8 @@ public: // drivers would most likely work with some alpha plane, but // glReadPixels would not return the alpha channel on NVIDIA if // not requested when the GL context is created. - WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, WX_GL_SAMPLE_BUFFERS, - GL_TRUE, WX_GL_SAMPLES, 4, 0}; + WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, + WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; m_canvas = std::make_shared(this, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, @@ -111,17 +111,33 @@ public: auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); - + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); - auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - wxString algorithms [] = {"Default", "Different"}; - auto alg_select = new wxComboBox(control_panel, wxID_ANY, "Default", wxDefaultPosition, wxDefaultSize, 2, algorithms); - slider_sizer->Add(slider, 1, wxEXPAND); - console_sizer->Add(toggle, 0, wxALL, 5); - console_sizer->Add(alg_select, 0, wxALL, 5); + + auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(toggle, 0, wxALL | wxEXPAND, 5); + + auto add_combobox = [control_panel, console_sizer] + (const wxString &label, std::vector &&list) { + auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], + wxDefaultPosition, wxDefaultSize, + int(list.size()), list.data()); + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); + auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); + depth_select->Disable(); + auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); + controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); + control_panel->SetSizer(controlsizer); auto sizer = new wxBoxSizer(wxHORIZONTAL); @@ -141,6 +157,31 @@ public: m_canvas->repaint(); }, toggle->GetId()); + Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + { + int sel = alg_select->GetSelection(); + depth_select->Enable(sel > 0); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, alg_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) + { + int sel = depth_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) + { + int sel = optimization_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + m_canvas->set_scene(std::make_shared()); } From 878f8a8ead6ffa4b53e77b3dd7ff226dc74fe0b1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 14:04:26 +0100 Subject: [PATCH 060/130] Add convexity to csgsettings. Defer all rendering to Display. --- sandboxes/opencsg/GLScene.cpp | 143 ++++++++++++++++++++-------------- sandboxes/opencsg/GLScene.hpp | 51 ++++++------ sandboxes/opencsg/main.cpp | 65 +++++++++++++--- 3 files changed, 169 insertions(+), 90 deletions(-) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 60eb0ce03b..5037ef6a54 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -99,13 +99,15 @@ void Display::render_scene() GLfloat color[] = {1.f, 1.f, 0.f, 0.f}; glsafe(::glColor4fv(color)); - OpenCSG::render(m_scene->csg_primitives()); + if (m_csgsettings.is_enabled()) { + OpenCSG::render(m_scene_cache.primitives_csg); + glDepthFunc(GL_EQUAL); + } - glDepthFunc(GL_EQUAL); - for (auto& p : m_scene->csg_primitives()) p->render(); - glDepthFunc(GL_LESS); + for (auto& p : m_scene_cache.primitives_csg) p->render(); + if (m_csgsettings.is_enabled()) glDepthFunc(GL_LESS); - for (auto& p : m_scene->free_primitives()) p->render(); + for (auto& p : m_scene_cache.primitives_free) p->render(); glFlush(); } @@ -127,53 +129,8 @@ std::vector transform_pts( } void Scene::set_print(uqptr &&print) -{ +{ m_print = std::move(print); - - for (const SLAPrintObject *po : m_print->objects()) { - const ModelObject *mo = po->model_object(); - TriangleMesh msh = mo->raw_mesh(); - - sla::DrainHoles holedata = mo->sla_drain_holes; - - for (const ModelInstance *mi : mo->instances) { - - TriangleMesh mshinst = msh; - auto interior = po->hollowed_interior_mesh(); - interior.transform(po->trafo().inverse()); - - mshinst.merge(interior); - mshinst.require_shared_vertices(); - - mi->transform_mesh(&mshinst); - - auto bb = mshinst.bounding_box(); - auto center = bb.center().cast(); - mshinst.translate(-center); - - mshinst.require_shared_vertices(); - add_mesh(mshinst, OpenCSG::Intersection, 15); - - auto tr = Transform3f::Identity(); - tr.translate(-center); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.pos; - }); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.normal; - }); - } - - for (const sla::DrainHole &holept : holedata) { - TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); - holemesh.require_shared_vertices(); - add_mesh(holemesh, OpenCSG::Subtraction, 1); - } - } // Notify displays call(&Display::on_scene_updated, m_displays); @@ -184,21 +141,30 @@ BoundingBoxf3 Scene::get_bounding_box() const return m_print->model().bounding_box(); } -shptr Scene::add_mesh(const TriangleMesh &mesh) +void Display::SceneCache::clear() +{ + primitives_csg.clear(); + primitives_free.clear(); + primitives.clear(); +} + +shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) { auto p = std::make_shared(); p->load_mesh(mesh); - m_primitives.emplace_back(p); - m_primitives_free.emplace_back(p.get()); + primitives.emplace_back(p); + primitives_free.emplace_back(p.get()); return p; } -shptr Scene::add_mesh(const TriangleMesh &mesh, OpenCSG::Operation o, unsigned c) +shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation o, + unsigned c) { auto p = std::make_shared(o, c); p->load_mesh(mesh); - m_primitives.emplace_back(p); - m_primitives_csg.emplace_back(p.get()); + primitives.emplace_back(p); + primitives_csg.emplace_back(p.get()); return p; } @@ -457,18 +423,79 @@ void Display::on_moved_to(long x, long y) void Display::apply_csgsettings(const CSGSettings &settings) { using namespace OpenCSG; + + bool update = m_csgsettings.get_convexity() != settings.get_convexity(); + m_csgsettings = settings; setOption(AlgorithmSetting, m_csgsettings.get_algo()); setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo()); setOption(DepthBoundsOptimization, m_csgsettings.get_optimization()); + + if (update) on_scene_updated(); + + repaint(); } void Display::on_scene_updated() { + const SLAPrint *print = m_scene->get_print(); + if (!print) return; + + { auto bb = m_scene->get_bounding_box(); double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); m_wheel_pos = long(2 * d); m_camera->set_zoom(m_wheel_pos); + } + + m_scene_cache.clear(); + + for (const SLAPrintObject *po : print->objects()) { + const ModelObject *mo = po->model_object(); + TriangleMesh msh = mo->raw_mesh(); + + sla::DrainHoles holedata = mo->sla_drain_holes; + + for (const ModelInstance *mi : mo->instances) { + + TriangleMesh mshinst = msh; + auto interior = po->hollowed_interior_mesh(); + interior.transform(po->trafo().inverse()); + + mshinst.merge(interior); + mshinst.require_shared_vertices(); + + mi->transform_mesh(&mshinst); + + auto bb = mshinst.bounding_box(); + auto center = bb.center().cast(); + mshinst.translate(-center); + + mshinst.require_shared_vertices(); + m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection, + m_csgsettings.get_convexity()); + + auto tr = Transform3f::Identity(); + tr.translate(-center); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.pos; + }); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.normal; + }); + } + + for (const sla::DrainHole &holept : holedata) { + TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); + holemesh.require_shared_vertices(); + m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1); + } + } + repaint(); } @@ -513,4 +540,6 @@ bool enable_multisampling(bool e) else return false; } +MouseInput::Listener::~Listener() = default; + }} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 6beecb79a6..a127ab90be 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -45,8 +45,7 @@ public: class Listener { public: - - virtual ~Listener() = default; + virtual ~Listener(); virtual void on_left_click_down() {} virtual void on_left_click_up() {} @@ -219,9 +218,16 @@ public: }; class CSGSettings { +public: + static const constexpr unsigned DEFAULT_CONVEXITY = 10; + +private: OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; + bool m_enable = true; + unsigned int m_convexity = DEFAULT_CONVEXITY; + public: int get_algo() const { return int(m_csgalg); } void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } @@ -231,6 +237,12 @@ public: int get_optimization() const { return int(m_optim); } void set_optimization(OpenCSG::Optimization o) { m_optim = o; } + + void enable_csg(bool en = true) { m_enable = en; } + bool is_enabled() const { return m_enable; } + + unsigned get_convexity() const { return m_convexity; } + void set_convexity(unsigned c) { m_convexity = c; } }; class Display : public std::enable_shared_from_this, @@ -247,6 +259,19 @@ protected: shptr m_camera; + struct SceneCache { + Collection> primitives; + Collection primitives_free; + Collection primitives_csg; + + void clear(); + + shptr add_mesh(const TriangleMesh &mesh); + shptr add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation op, + unsigned covexity); + } m_scene_cache; + public: Display(shptr scene = nullptr, shptr camera = nullptr) : m_scene(scene) @@ -284,26 +309,12 @@ public: class Scene: public MouseInput::Listener { - Collection> m_primitives; - Collection m_primitives_free; - Collection m_primitives_csg; - uqptr m_print; public: Scene(); ~Scene(); - const Collection& free_primitives() const - { - return m_primitives_free; - } - - const Collection& csg_primitives() const - { - return m_primitives_csg; - } - void add_display(shptr disp) { m_displays.emplace_back(disp); @@ -311,16 +322,10 @@ public: } void set_print(uqptr &&print); + const SLAPrint * get_print() const { return m_print.get(); } BoundingBoxf3 get_bounding_box() const; -protected: - - shptr add_mesh(const TriangleMesh &mesh); - shptr add_mesh(const TriangleMesh &mesh, - OpenCSG::Operation op, - unsigned covexity); - private: Collection> m_displays; diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 8c106fa85c..6274f264a1 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "Canvas.hpp" @@ -111,25 +112,53 @@ public: auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); - - auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, + wxDefaultPosition, wxDefaultSize, + wxSL_VERTICAL); slider_sizer->Add(slider, 1, wxEXPAND); - auto toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - console_sizer->Add(toggle, 0, wxALL | wxEXPAND, 5); - + auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); + + auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); + csg_toggle->SetValue(true); + console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); + auto add_combobox = [control_panel, console_sizer] - (const wxString &label, std::vector &&list) { + (const wxString &label, std::vector &&list) + { auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], wxDefaultPosition, wxDefaultSize, int(list.size()), list.data()); + auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, wxALL | wxALIGN_CENTER, 5); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); sz->Add(widget, 1, wxALL | wxEXPAND, 5); console_sizer->Add(sz, 0, wxEXPAND); return widget; }; + auto add_spinctl = [control_panel, console_sizer] + (const wxString &label, int initial, int min, int max) + { + auto widget = new wxSpinCtrl( + control_panel, wxID_ANY, + wxString::Format("%d", initial), + wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, + initial); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); + auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); depth_select->Disable(); @@ -152,10 +181,16 @@ public: m_canvas->move_clip_plane(double(slider->GetValue())); }, slider->GetId()); - Bind(wxEVT_TOGGLEBUTTON, [this, toggle](wxCommandEvent &){ - enable_multisampling(toggle->GetValue()); + Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + enable_multisampling(ms_toggle->GetValue()); m_canvas->repaint(); - }, toggle->GetId()); + }, ms_toggle->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + CSGSettings settings = m_canvas->get_csgsettings(); + settings.enable_csg(csg_toggle->GetValue()); + m_canvas->apply_csgsettings(settings); + }, csg_toggle->GetId()); Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) { @@ -182,6 +217,16 @@ public: m_canvas->apply_csgsettings(settings); }, depth_select->GetId()); + Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + CSGSettings settings = m_canvas->get_csgsettings(); + int c = convexity_spin->GetValue(); + + if (c > 0) { + settings.set_convexity(unsigned(c)); + m_canvas->apply_csgsettings(settings); + } + }, convexity_spin->GetId()); + m_canvas->set_scene(std::make_shared()); } From 5aaddd82a44475c37f933d476a10351f03ddee01 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 15:36:43 +0100 Subject: [PATCH 061/130] Remove bloat, add some clipping plane functionality. --- sandboxes/opencsg/Canvas.hpp | 14 +- sandboxes/opencsg/GLScene.cpp | 8 +- sandboxes/opencsg/main.cpp | 314 +++++++++++++++++----------------- 3 files changed, 168 insertions(+), 168 deletions(-) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp index 85d490ddf5..17fd8f0440 100644 --- a/sandboxes/opencsg/Canvas.hpp +++ b/sandboxes/opencsg/Canvas.hpp @@ -27,20 +27,8 @@ public: Slic3r::GL::Display::set_active(w, h); } - void repaint(long width, long height) override - { - Slic3r::GL::Display::repaint(width, height); - } - - using Slic3r::GL::Display::repaint; - void swap_buffers() override { SwapBuffers(); } - - void on_scroll(long v, long d, Slic3r::GL::MouseInput::WheelAxis wa) override - { - Slic3r::GL::Display::on_scroll(v, d, wa); - } - + template Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) { diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 5037ef6a54..02a4b79914 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -517,7 +517,13 @@ void Camera::view() glRotatef(m_rot.y(), 1.0, 0.0, 0.0); glRotatef(m_rot.x(), 0.0, 0.0, 1.0); - // glClipPlane() + if (m_clip_z > 0.) { + GLdouble plane[] = {0., 0., 1., m_clip_z}; + glClipPlane(GL_CLIP_PLANE0, plane); + glEnable(GL_CLIP_PLANE0); + } else { + glDisable(GL_CLIP_PLANE0); + } } void PerspectiveCamera::set_screen(long width, long height) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 6274f264a1..ac18e177d1 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -26,7 +26,6 @@ #include "slic3r/GUI/Job.hpp" #include "slic3r/GUI/ProgressStatusBar.hpp" -//#include "slic3r/GUI/3DEngine.hpp" using namespace Slic3r::GL; @@ -59,6 +58,10 @@ class MyFrame: public wxFrame m_print = std::make_unique(); m_print->apply(model, cfg); + Slic3r::PrintBase::TaskParams params; + params.to_object_step = Slic3r::slaposHollowing; + m_print->set_task(params); + m_print->set_status_callback([this](const Status &status) { update_status(status.percent, status.text); }); @@ -77,158 +80,7 @@ class MyFrame: public wxFrame }; public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size): - wxFrame(nullptr, wxID_ANY, title, pos, size) - { - wxMenu *menuFile = new wxMenu; - menuFile->Append(wxID_OPEN); - menuFile->Append(wxID_EXIT); - wxMenuBar *menuBar = new wxMenuBar; - menuBar->Append( menuFile, "&File" ); - SetMenuBar( menuBar ); - - m_stbar = std::make_shared(this); - m_stbar->embed(this); - - SetStatusText( "Welcome to wxWidgets!" ); - - int attribList[] = - {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, - // RGB channels each should be allocated with 8 bit depth. One - // should almost certainly get these bit depths by default. - WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, - // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA - // drivers would most likely work with some alpha plane, but - // glReadPixels would not return the alpha channel on NVIDIA if - // not requested when the GL context is created. - WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, - WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; - - m_canvas = std::make_shared(this, wxID_ANY, attribList, - wxDefaultPosition, wxDefaultSize, - wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - - wxPanel *control_panel = new wxPanel(this); - auto controlsizer = new wxBoxSizer(wxHORIZONTAL); - auto slider_sizer = new wxBoxSizer(wxVERTICAL); - auto console_sizer = new wxBoxSizer(wxVERTICAL); - - auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, - wxDefaultPosition, wxDefaultSize, - wxSL_VERTICAL); - slider_sizer->Add(slider, 1, wxEXPAND); - - auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); - - auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); - csg_toggle->SetValue(true); - console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); - - auto add_combobox = [control_panel, console_sizer] - (const wxString &label, std::vector &&list) - { - auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], - wxDefaultPosition, wxDefaultSize, - int(list.size()), list.data()); - - auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, - wxALL | wxALIGN_CENTER, 5); - sz->Add(widget, 1, wxALL | wxEXPAND, 5); - console_sizer->Add(sz, 0, wxEXPAND); - return widget; - }; - - auto add_spinctl = [control_panel, console_sizer] - (const wxString &label, int initial, int min, int max) - { - auto widget = new wxSpinCtrl( - control_panel, wxID_ANY, - wxString::Format("%d", initial), - wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, - initial); - - auto sz = new wxBoxSizer(wxHORIZONTAL); - sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, - wxALL | wxALIGN_CENTER, 5); - sz->Add(widget, 1, wxALL | wxEXPAND, 5); - console_sizer->Add(sz, 0, wxEXPAND); - return widget; - }; - - auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); - - auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); - auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); - depth_select->Disable(); - auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); - - controlsizer->Add(slider_sizer, 0, wxEXPAND); - controlsizer->Add(console_sizer, 1, wxEXPAND); - - control_panel->SetSizer(controlsizer); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_canvas.get(), 1, wxEXPAND); - sizer->Add(control_panel, 0, wxEXPAND); - SetSizer(sizer); - - Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); - Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); - Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); - Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { - m_canvas->move_clip_plane(double(slider->GetValue())); - }, slider->GetId()); - - Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ - enable_multisampling(ms_toggle->GetValue()); - m_canvas->repaint(); - }, ms_toggle->GetId()); - - Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_csgsettings(); - settings.enable_csg(csg_toggle->GetValue()); - m_canvas->apply_csgsettings(settings); - }, csg_toggle->GetId()); - - Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) - { - int sel = alg_select->GetSelection(); - depth_select->Enable(sel > 0); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->apply_csgsettings(settings); - }, alg_select->GetId()); - - Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) - { - int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); - - Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) - { - int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); - settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); - - Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_csgsettings(); - int c = convexity_spin->GetValue(); - - if (c > 0) { - settings.set_convexity(unsigned(c)); - m_canvas->apply_csgsettings(settings); - } - }, convexity_spin->GetId()); - - m_canvas->set_scene(std::make_shared()); - } + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); private: @@ -270,8 +122,8 @@ class App : public wxApp { MyFrame *m_frame; public: bool OnInit() override { + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768)); - m_frame = new MyFrame( "PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768) ); m_frame->Show( true ); return true; @@ -279,3 +131,157 @@ public: }; wxIMPLEMENT_APP(App); + +MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): + wxFrame(nullptr, wxID_ANY, title, pos, size) +{ + wxMenu *menuFile = new wxMenu; + menuFile->Append(wxID_OPEN); + menuFile->Append(wxID_EXIT); + wxMenuBar *menuBar = new wxMenuBar; + menuBar->Append( menuFile, "&File" ); + SetMenuBar( menuBar ); + + m_stbar = std::make_shared(this); + m_stbar->embed(this); + + SetStatusText( "Welcome to wxWidgets!" ); + + int attribList[] = + {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, + // RGB channels each should be allocated with 8 bit depth. One + // should almost certainly get these bit depths by default. + WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8, + // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA + // drivers would most likely work with some alpha plane, but + // glReadPixels would not return the alpha channel on NVIDIA if + // not requested when the GL context is created. + WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, + WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; + + m_canvas = std::make_shared(this, wxID_ANY, attribList, + wxDefaultPosition, wxDefaultSize, + wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); + + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); + auto slider_sizer = new wxBoxSizer(wxVERTICAL); + auto console_sizer = new wxBoxSizer(wxVERTICAL); + + auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100, + wxDefaultPosition, wxDefaultSize, + wxSL_VERTICAL); + slider_sizer->Add(slider, 1, wxEXPAND); + + auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); + + auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); + csg_toggle->SetValue(true); + console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); + + auto add_combobox = [control_panel, console_sizer] + (const wxString &label, std::vector &&list) + { + auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], + wxDefaultPosition, wxDefaultSize, + int(list.size()), list.data()); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto add_spinctl = [control_panel, console_sizer] + (const wxString &label, int initial, int min, int max) + { + auto widget = new wxSpinCtrl( + control_panel, wxID_ANY, + wxString::Format("%d", initial), + wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max, + initial); + + auto sz = new wxBoxSizer(wxHORIZONTAL); + sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0, + wxALL | wxALIGN_CENTER, 5); + sz->Add(widget, 1, wxALL | wxEXPAND, 5); + console_sizer->Add(sz, 0, wxEXPAND); + return widget; + }; + + auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); + + auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); + auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); + auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); + depth_select->Disable(); + + controlsizer->Add(slider_sizer, 0, wxEXPAND); + controlsizer->Add(console_sizer, 1, wxEXPAND); + + control_panel->SetSizer(controlsizer); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_canvas.get(), 1, wxEXPAND); + sizer->Add(control_panel, 0, wxEXPAND); + SetSizer(sizer); + + Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); + Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); + Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); + + Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { + m_canvas->move_clip_plane(double(slider->GetValue())); + }, slider->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + enable_multisampling(ms_toggle->GetValue()); + m_canvas->repaint(); + }, ms_toggle->GetId()); + + Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + CSGSettings settings = m_canvas->get_csgsettings(); + settings.enable_csg(csg_toggle->GetValue()); + m_canvas->apply_csgsettings(settings); + }, csg_toggle->GetId()); + + Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + { + int sel = alg_select->GetSelection(); + depth_select->Enable(sel > 0); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, alg_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) + { + int sel = depth_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) + { + int sel = optimization_select->GetSelection(); + CSGSettings settings = m_canvas->get_csgsettings(); + settings.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->apply_csgsettings(settings); + }, depth_select->GetId()); + + Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + CSGSettings settings = m_canvas->get_csgsettings(); + int c = convexity_spin->GetValue(); + + if (c > 0) { + settings.set_convexity(unsigned(c)); + m_canvas->apply_csgsettings(settings); + } + }, convexity_spin->GetId()); + + m_canvas->set_scene(std::make_shared()); +} From 47ec708c3a33beb9c14c5d6c7c730f3eee4a4e94 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 15:55:49 +0100 Subject: [PATCH 062/130] Fix event dispatching to handlers --- sandboxes/opencsg/main.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ac18e177d1..ddd4bf33ef 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -235,45 +235,45 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { m_canvas->move_clip_plane(double(slider->GetValue())); - }, slider->GetId()); + }); - Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ + ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ enable_multisampling(ms_toggle->GetValue()); m_canvas->repaint(); - }, ms_toggle->GetId()); + }); - Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ + csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ CSGSettings settings = m_canvas->get_csgsettings(); settings.enable_csg(csg_toggle->GetValue()); m_canvas->apply_csgsettings(settings); - }, csg_toggle->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, alg_select, depth_select](wxCommandEvent &) + alg_select->Bind(wxEVT_COMBOBOX, + [this, alg_select, depth_select](wxCommandEvent &) { int sel = alg_select->GetSelection(); depth_select->Enable(sel > 0); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_algo(OpenCSG::Algorithm(sel)); m_canvas->apply_csgsettings(settings); - }, alg_select->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) - { + depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); + }); - Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) - { + optimization_select->Bind(wxEVT_COMBOBOX, + [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); CSGSettings settings = m_canvas->get_csgsettings(); settings.set_optimization(OpenCSG::Optimization(sel)); m_canvas->apply_csgsettings(settings); - }, depth_select->GetId()); + }); - Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { + convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { CSGSettings settings = m_canvas->get_csgsettings(); int c = convexity_spin->GetValue(); @@ -281,7 +281,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): settings.set_convexity(unsigned(c)); m_canvas->apply_csgsettings(settings); } - }, convexity_spin->GetId()); + }); m_canvas->set_scene(std::make_shared()); } From bb3b39016fa9381506e1594cd46f361c6d348fd6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 16 Dec 2019 18:49:44 +0100 Subject: [PATCH 063/130] Add ctl and fix applying opencsg params on the fly. --- sandboxes/opencsg/Canvas.hpp | 52 --------------- sandboxes/opencsg/GLScene.cpp | 63 ++++++++++-------- sandboxes/opencsg/GLScene.hpp | 122 ++++++++++++++++++++++------------ sandboxes/opencsg/main.cpp | 122 +++++++++++++++++++++++++--------- 4 files changed, 205 insertions(+), 154 deletions(-) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp index 17fd8f0440..4245799458 100644 --- a/sandboxes/opencsg/Canvas.hpp +++ b/sandboxes/opencsg/Canvas.hpp @@ -40,58 +40,6 @@ public: } m_context.reset(ctx); - - Bind( - wxEVT_MOUSEWHEEL, - [this](wxMouseEvent &evt) { - on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), - evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? - Slic3r::GL::MouseInput::waVertical : - Slic3r::GL::MouseInput::waHorizontal); - }, - GetId()); - - Bind( - wxEVT_MOTION, - [this](wxMouseEvent &evt) { - on_moved_to(evt.GetPosition().x, evt.GetPosition().y); - }, - GetId()); - - Bind( - wxEVT_RIGHT_DOWN, - [this](wxMouseEvent & /*evt*/) { on_right_click_down(); }, - GetId()); - - Bind( - wxEVT_RIGHT_UP, - [this](wxMouseEvent & /*evt*/) { on_right_click_up(); }, - GetId()); - - Bind( - wxEVT_LEFT_DOWN, - [this](wxMouseEvent & /*evt*/) { on_left_click_down(); }, - GetId()); - - Bind( - wxEVT_LEFT_UP, - [this](wxMouseEvent & /*evt*/) { on_left_click_up(); }, - GetId()); - - Bind(wxEVT_PAINT, [this](wxPaintEvent &) { - // This is required even though dc is not used otherwise. - wxPaintDC dc(this); - - // Set the OpenGL viewport according to the client size of this - // canvas. This is done here rather than in a wxSizeEvent handler - // because our OpenGL rendering context (and thus viewport setting) is - // used with multiple canvases: If we updated the viewport in the - // wxSizeEvent handler, changing the size of one canvas causes a - // viewport setting that is wrong when next another canvas is - // repainted. - const wxSize ClientSize = GetClientSize(); - repaint(ClientSize.x, ClientSize.y); - }, GetId()); } }; diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 02a4b79914..1cfccb3b19 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -133,7 +133,7 @@ void Scene::set_print(uqptr &&print) m_print = std::move(print); // Notify displays - call(&Display::on_scene_updated, m_displays); + call(&Listener::on_scene_updated, m_listeners, *this); } BoundingBoxf3 Scene::get_bounding_box() const @@ -383,13 +383,18 @@ void Display::set_active(long width, long height) m_camera->set_screen(width, height); } -void Display::repaint(long width, long height) +void Display::set_screen_size(long width, long height) { if (m_size.x() != width || m_size.y() != height) m_camera->set_screen(width, height); m_size = {width, height}; + repaint(); +} + +void Display::repaint() +{ clear_screen(); m_camera->view(); @@ -400,23 +405,34 @@ void Display::repaint(long width, long height) swap_buffers(); } -void Display::on_scroll(long v, long d, MouseInput::WheelAxis wa) +void Controller::on_scene_updated(const Scene &scene) +{ + const SLAPrint *print = scene.get_print(); + if (!print) return; + + auto bb = scene.get_bounding_box(); + double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); + m_wheel_pos = long(2 * d); + + call_cameras(&Camera::set_zoom, m_wheel_pos); + call(&Display::on_scene_updated, m_displays, scene); +} + +void Controller::on_scroll(long v, long d, MouseInput::WheelAxis /*wa*/) { m_wheel_pos += v / d; - m_camera->set_zoom(m_wheel_pos); - - m_scene->on_scroll(v, d, wa); - - repaint(m_size.x(), m_size.y()); + call_cameras(&Camera::set_zoom, m_wheel_pos); + call(&Display::repaint, m_displays); } -void Display::on_moved_to(long x, long y) +void Controller::on_moved_to(long x, long y) { if (m_left_btn) { - m_camera->rotate((Vec2i{x, y} - m_mouse_pos).cast()); - repaint(); + call_cameras(&Camera::rotate, (Vec2i{x, y} - m_mouse_pos).cast()); + call(&Display::repaint, m_displays); } + m_mouse_pos = {x, y}; } @@ -424,30 +440,27 @@ void Display::apply_csgsettings(const CSGSettings &settings) { using namespace OpenCSG; - bool update = m_csgsettings.get_convexity() != settings.get_convexity(); + bool needupdate = m_csgsettings.get_convexity() != settings.get_convexity(); m_csgsettings = settings; setOption(AlgorithmSetting, m_csgsettings.get_algo()); setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo()); setOption(DepthBoundsOptimization, m_csgsettings.get_optimization()); - if (update) on_scene_updated(); + if (needupdate) { + for (OpenCSG::Primitive * p : m_scene_cache.primitives_csg) + if (p->getConvexity() > 1) + p->setConvexity(m_csgsettings.get_convexity()); + } repaint(); } -void Display::on_scene_updated() +void Display::on_scene_updated(const Scene &scene) { - const SLAPrint *print = m_scene->get_print(); + const SLAPrint *print = scene.get_print(); if (!print) return; - { - auto bb = m_scene->get_bounding_box(); - double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z()); - m_wheel_pos = long(2 * d); - m_camera->set_zoom(m_wheel_pos); - } - m_scene_cache.clear(); for (const SLAPrintObject *po : print->objects()) { @@ -499,12 +512,6 @@ void Display::on_scene_updated() repaint(); } -void Display::set_scene(shptr scene) -{ - m_scene = scene; - m_scene->add_display(shared_from_this()); -} - void Camera::view() { glMatrixMode(GL_MODELVIEW); diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index a127ab90be..5a4afb396b 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -223,8 +223,8 @@ public: private: OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic; - OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::DepthComplexityAlgorithm::NoDepthComplexitySampling; - OpenCSG::Optimization m_optim = OpenCSG::Optimization::OptimizationDefault; + OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::NoDepthComplexitySampling; + OpenCSG::Optimization m_optim = OpenCSG::OptimizationDefault; bool m_enable = true; unsigned int m_convexity = DEFAULT_CONVEXITY; @@ -244,21 +244,44 @@ public: unsigned get_convexity() const { return m_convexity; } void set_convexity(unsigned c) { m_convexity = c; } }; - -class Display : public std::enable_shared_from_this, - public MouseInput::Listener + +class Scene +{ + uqptr m_print; +public: + + class Listener { + public: + virtual ~Listener() = default; + virtual void on_scene_updated(const Scene &scene) = 0; + }; + + Scene(); + ~Scene(); + + void set_print(uqptr &&print); + const SLAPrint * get_print() const { return m_print.get(); } + + BoundingBoxf3 get_bounding_box() const; + + void add_listener(shptr listener) + { + m_listeners.emplace_back(listener); + cleanup(m_listeners); + } + +private: + Collection> m_listeners; +}; + +class Display : public Scene::Listener { protected: - shptr m_scene; - long m_wheel_pos = 0; - Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; Vec2i m_size; - bool m_initialized = false, m_left_btn = false, m_right_btn = false; + bool m_initialized = false; CSGSettings m_csgsettings; - shptr m_camera; - struct SceneCache { Collection> primitives; Collection primitives_free; @@ -272,64 +295,79 @@ protected: unsigned covexity); } m_scene_cache; + shptr m_camera; + public: - Display(shptr scene = nullptr, shptr camera = nullptr) - : m_scene(scene) - , m_camera(camera ? camera : std::make_shared()) + + explicit Display(shptr camera = nullptr) + : m_camera(camera ? camera : std::make_shared()) {} - + + Camera * camera() { return m_camera.get(); } + virtual void swap_buffers() = 0; - virtual void set_active(long width, long height); + virtual void set_screen_size(long width, long height); + Vec2i get_screen_size() const { return m_size; } - virtual void repaint(long width, long height); - void repaint() { repaint(m_size.x(), m_size.y()); } - - void set_scene(shptr scene); - shptr get_scene() { return m_scene; } + virtual void repaint(); bool is_initialized() const { return m_initialized; } - void on_scroll(long v, long d, MouseInput::WheelAxis wa) override; - void on_moved_to(long x, long y) override; - void on_left_click_down() override { m_left_btn = true; } - void on_left_click_up() override { m_left_btn = false; } - void on_right_click_down() override { m_right_btn = true; } - void on_right_click_up() override { m_right_btn = false; } - - void move_clip_plane(double z) { m_camera->set_clip_z(z); } - const CSGSettings & get_csgsettings() const { return m_csgsettings; } void apply_csgsettings(const CSGSettings &settings); - virtual void on_scene_updated(); + void on_scene_updated(const Scene &scene) override; + virtual void clear_screen(); virtual void render_scene(); }; -class Scene: public MouseInput::Listener +class Controller : public std::enable_shared_from_this, + public MouseInput::Listener, + public Scene::Listener { - uqptr m_print; + long m_wheel_pos = 0; + Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; + bool m_left_btn = false, m_right_btn = false; + + shptr m_scene; + Collection> m_displays; + + template + void call_cameras(F &&f, Args&&... args) { + for (wkptr &l : m_displays) + if (auto disp = l.lock()) if (disp->camera()) + (disp->camera()->*f)(std::forward(args)...); + } + public: - Scene(); - ~Scene(); + void set_scene(shptr scene) + { + m_scene = scene; + m_scene->add_listener(shared_from_this()); + } + const Scene * get_scene() const { return m_scene.get(); } + void add_display(shptr disp) { m_displays.emplace_back(disp); cleanup(m_displays); } - void set_print(uqptr &&print); - const SLAPrint * get_print() const { return m_print.get(); } + void on_scene_updated(const Scene &scene) override; - BoundingBoxf3 get_bounding_box() const; + void on_left_click_down() override { m_left_btn = true; } + void on_left_click_up() override { m_left_btn = false; } + void on_right_click_down() override { m_right_btn = true; } + void on_right_click_up() override { m_right_btn = false; } + + void on_scroll(long v, long d, MouseInput::WheelAxis wa) override; + void on_moved_to(long x, long y) override; -private: - - Collection> m_displays; + void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); } }; - }} // namespace Slic3r::GL #endif // GLSCENE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ddd4bf33ef..d01687428e 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -31,49 +31,31 @@ using namespace Slic3r::GL; class MyFrame: public wxFrame { - std::shared_ptr m_canvas; - std::shared_ptr m_stbar; - std::unique_ptr m_ui_job; + shptr m_scene; // Model + shptr m_canvas; // View + shptr m_ctl; // Controller + + shptr m_stbar; + uqptr m_ui_job; class SLAJob: public Slic3r::GUI::Job { MyFrame *m_parent; std::unique_ptr m_print; std::string m_fname; public: - - SLAJob(MyFrame *frame, const std::string &fname) + SLAJob(MyFrame *frame, const std::string &fname) : Slic3r::GUI::Job{frame->m_stbar} , m_parent{frame} , m_fname{fname} - { - } - - void process() override - { - using Status = Slic3r::PrintBase::SlicingStatus; - - Slic3r::DynamicPrintConfig cfg; - auto model = Slic3r::Model::read_from_file(m_fname, &cfg); - - m_print = std::make_unique(); - m_print->apply(model, cfg); - - Slic3r::PrintBase::TaskParams params; - params.to_object_step = Slic3r::slaposHollowing; - m_print->set_task(params); - - m_print->set_status_callback([this](const Status &status) { - update_status(status.percent, status.text); - }); - - m_print->process(); - } + {} + + void process() override; protected: void finalize() override { - m_parent->m_canvas->get_scene()->set_print(std::move(m_print)); + m_parent->m_scene->set_print(std::move(m_print)); m_parent->m_stbar->set_status_text( wxString::Format("Model %s loaded.", m_fname)); } @@ -84,6 +66,8 @@ public: private: + void bind_canvas_events_to_controller(); + void OnExit(wxCommandEvent& /*event*/) { RemoveChild(m_canvas.get()); @@ -108,7 +92,8 @@ private: const wxSize ClientSize = GetClientSize(); m_canvas->set_active(ClientSize.x, ClientSize.y); - m_canvas->repaint(ClientSize.x, ClientSize.y); + m_canvas->set_screen_size(ClientSize.x, ClientSize.y); + m_canvas->repaint(); // Do the repaint continuously Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { @@ -159,9 +144,14 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; + m_scene = std::make_shared(); + m_ctl = std::make_shared(); + m_ctl->set_scene(m_scene); + m_canvas = std::make_shared(this, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); + m_ctl->add_display(m_canvas); wxPanel *control_panel = new wxPanel(this); auto controlsizer = new wxBoxSizer(wxHORIZONTAL); @@ -234,7 +224,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { - m_canvas->move_clip_plane(double(slider->GetValue())); + m_ctl->move_clip_plane(double(slider->GetValue())); }); ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ @@ -283,5 +273,73 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): } }); - m_canvas->set_scene(std::make_shared()); + bind_canvas_events_to_controller(); +} + +void MyFrame::bind_canvas_events_to_controller() +{ + m_canvas->Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent &evt) { + m_ctl->on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), + evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? + Slic3r::GL::MouseInput::waVertical : + Slic3r::GL::MouseInput::waHorizontal); + }); + + m_canvas->Bind(wxEVT_MOTION, [this](wxMouseEvent &evt) { + m_ctl->on_moved_to(evt.GetPosition().x, evt.GetPosition().y); + }); + + m_canvas->Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent & /*evt*/) { + m_ctl->on_right_click_down(); + }); + + m_canvas->Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent & /*evt*/) { + m_ctl->on_right_click_up(); + }); + + m_canvas->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent & /*evt*/) { + m_ctl->on_left_click_down(); + }); + + m_canvas->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent & /*evt*/) { + m_ctl->on_left_click_up(); + }); + + m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { + // This is required even though dc is not used otherwise. + wxPaintDC dc(this); + + // Set the OpenGL viewport according to the client size of this + // canvas. This is done here rather than in a wxSizeEvent handler + // because our OpenGL rendering context (and thus viewport setting) is + // used with multiple canvases: If we updated the viewport in the + // wxSizeEvent handler, changing the size of one canvas causes a + // viewport setting that is wrong when next another canvas is + // repainted. + const wxSize ClientSize = m_canvas->GetClientSize(); + + m_canvas->set_screen_size(ClientSize.x, ClientSize.y); + m_canvas->repaint(); + }); +} + +void MyFrame::SLAJob::process() +{ + using Status = Slic3r::PrintBase::SlicingStatus; + + Slic3r::DynamicPrintConfig cfg; + auto model = Slic3r::Model::read_from_file(m_fname, &cfg); + + m_print = std::make_unique(); + m_print->apply(model, cfg); + + Slic3r::PrintBase::TaskParams params; + params.to_object_step = Slic3r::slaposHollowing; + m_print->set_task(params); + + m_print->set_status_callback([this](const Status &status) { + update_status(status.percent, status.text); + }); + + m_print->process(); } From 11b98b22419df799f73f61d79fd8bb09bb9ca880 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 10:12:37 +0100 Subject: [PATCH 064/130] Deal with cmake warnings caused by find CGAL --- src/libslic3r/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 1ddb7fa8b1..8a3817b95e 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -219,11 +219,14 @@ add_library(libslic3r STATIC ) if (SLIC3R_STATIC) - set(CGAL_Boost_USE_STATIC_LIBS ON) - set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE TRUE) + set(CGAL_Boost_USE_STATIC_LIBS ON CACHE BOOL "" FORCE) endif () +set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE) +cmake_policy(PUSH) +cmake_policy(SET CMP0011 NEW) find_package(CGAL REQUIRED) +cmake_policy(POP) add_library(libslic3r_cgal OBJECT MeshBoolean.cpp MeshBoolean.hpp) target_include_directories(libslic3r_cgal PRIVATE @@ -234,7 +237,6 @@ target_compile_definitions(libslic3r_cgal PRIVATE $) target_compile_options(libslic3r_cgal PRIVATE $) -target_sources(libslic3r PRIVATE $) encoding_check(libslic3r) @@ -275,3 +277,5 @@ endif() if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) endif () + +target_sources(libslic3r PRIVATE $) From 695950b2e6f9ae534c5568b52500c643d10cb257 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 10:19:46 +0100 Subject: [PATCH 065/130] further simplification --- sandboxes/opencsg/CMakeLists.txt | 2 +- sandboxes/opencsg/Canvas.hpp | 48 -------------------------------- sandboxes/opencsg/GLScene.hpp | 25 +++++++++-------- sandboxes/opencsg/main.cpp | 29 ++++++++++++++++++- 4 files changed, 42 insertions(+), 62 deletions(-) delete mode 100644 sandboxes/opencsg/Canvas.hpp diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index 9a216a7dc1..651fbe82f7 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(OpenCSG-example) -add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp Canvas.hpp +add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) diff --git a/sandboxes/opencsg/Canvas.hpp b/sandboxes/opencsg/Canvas.hpp deleted file mode 100644 index 4245799458..0000000000 --- a/sandboxes/opencsg/Canvas.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef CANVAS_HPP -#define CANVAS_HPP - -#include - -// For compilers that support precompilation, includes "wx/wx.h". -#include -#ifndef WX_PRECOMP -#include -#endif - -#include -#include - -#include "GLScene.hpp" - -namespace Slic3r { namespace GL { - -class Canvas: public wxGLCanvas, public Slic3r::GL::Display -{ - std::unique_ptr m_context; -public: - - void set_active(long w, long h) override - { - SetCurrent(*m_context); - Slic3r::GL::Display::set_active(w, h); - } - - void swap_buffers() override { SwapBuffers(); } - - template - Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) - { - auto ctx = new wxGLContext(this); - if (!ctx || !ctx->IsOK()) { - wxMessageBox("Could not create OpenGL context.", "Error", - wxOK | wxICON_ERROR); - return; - } - - m_context.reset(ctx); - } -}; - -}} // namespace Slic3r::GL - -#endif // CANVAS_HPP diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 5a4afb396b..68cc59b01c 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -21,16 +21,16 @@ template using uqptr = std::unique_ptr; template using wkptr = std::weak_ptr; template> -using Collection = std::vector; +using vector = std::vector; -template void cleanup(Collection> &listeners) { +template void cleanup(vector> &listeners) { auto it = std::remove_if(listeners.begin(), listeners.end(), [](auto &l) { return !l.lock(); }); listeners.erase(it, listeners.end()); } template -void call(F &&f, Collection> &listeners, Args&&... args) { +void call(F &&f, vector> &listeners, Args&&... args) { for (auto &l : listeners) if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); } @@ -57,7 +57,7 @@ public: }; private: - Collection> m_listeners; + vector> m_listeners; public: virtual ~MouseInput() = default; @@ -104,9 +104,9 @@ public: // Vertices and their normals, interleaved to be used by void // glInterleavedArrays(GL_N3F_V3F, 0, x) - Collection vertices_and_normals_interleaved; - Collection triangle_indices; - Collection quad_indices; + vector vertices_and_normals_interleaved; + vector triangle_indices; + vector quad_indices; // When the geometry data is loaded into the graphics card as Vertex // Buffer Objects, the above mentioned std::vectors are cleared and the @@ -271,7 +271,7 @@ public: } private: - Collection> m_listeners; + vector> m_listeners; }; class Display : public Scene::Listener @@ -283,9 +283,9 @@ protected: CSGSettings m_csgsettings; struct SceneCache { - Collection> primitives; - Collection primitives_free; - Collection primitives_csg; + vector> primitives; + vector primitives_free; + vector primitives_csg; void clear(); @@ -332,8 +332,9 @@ class Controller : public std::enable_shared_from_this, bool m_left_btn = false, m_right_btn = false; shptr m_scene; - Collection> m_displays; + vector> m_displays; + // Call a method of Camera on all the cameras of the attached displays template void call_cameras(F &&f, Args&&... args) { for (wkptr &l : m_displays) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index d01687428e..c2f8a74aa1 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -15,9 +15,9 @@ #include #include #include +#include #include -#include "Canvas.hpp" #include "GLScene.hpp" #include "libslic3r/Model.hpp" @@ -29,6 +29,33 @@ using namespace Slic3r::GL; +class Canvas: public wxGLCanvas, public Slic3r::GL::Display +{ + std::unique_ptr m_context; +public: + + void set_active(long w, long h) override + { + SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + void swap_buffers() override { SwapBuffers(); } + + template + Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) + { + auto ctx = new wxGLContext(this); + if (!ctx || !ctx->IsOK()) { + wxMessageBox("Could not create OpenGL context.", "Error", + wxOK | wxICON_ERROR); + return; + } + + m_context.reset(ctx); + } +}; + class MyFrame: public wxFrame { shptr m_scene; // Model From c81b1fbbbdd3c7a1dcec5eb5883b7a88499a04a4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 10:51:18 +0100 Subject: [PATCH 066/130] Fix missing gmpxx from dep_GMP --- deps/GMP/GMP.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/GMP/GMP.cmake b/deps/GMP/GMP.cmake index 6c93107c42..8bcf948592 100644 --- a/deps/GMP/GMP.cmake +++ b/deps/GMP/GMP.cmake @@ -20,7 +20,7 @@ else () ExternalProject_Add(dep_GMP URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2 BUILD_IN_SOURCE ON - CONFIGURE_COMMAND ./configure --enable-shared=no --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic + CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic BUILD_COMMAND make -j INSTALL_COMMAND make install ) From d349f2402196cf9c69d8e8542b20fbe6326b66b8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 13:05:18 +0100 Subject: [PATCH 067/130] Fix CGAL config script being non-relocatable --- deps/CGAL/CGAL.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deps/CGAL/CGAL.cmake b/deps/CGAL/CGAL.cmake index 4b127cd512..96a6292580 100644 --- a/deps/CGAL/CGAL.cmake +++ b/deps/CGAL/CGAL.cmake @@ -6,4 +6,10 @@ prusaslicer_add_cmake_project( # URL https://github.com/CGAL/cgal/archive/releases/CGAL-5.0.zip # URL_HASH SHA256=bd9327be903ab7ee379a8a7a0609eba0962f5078d2497cf8e13e8e1598584154 DEPENDS dep_boost dep_GMP dep_MPFR +) + +ExternalProject_Add_Step(dep_CGAL dep_CGAL_relocation_fix + DEPENDEES install + COMMAND ${CMAKE_COMMAND} -E remove CGALConfig-installation-dirs.cmake + WORKING_DIRECTORY "${DESTDIR}/usr/local/lib/cmake/CGAL" ) \ No newline at end of file From 57cf3d17e2dc25c1500f403e584684430f9580a0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 17 Dec 2019 15:57:24 +0100 Subject: [PATCH 068/130] First steps on SLA and Hollowing gizmo data sharing --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 36 ++- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 262 +++++++++---------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 34 ++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 194 +++++++------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 27 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 6 +- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 1 + 8 files changed, 296 insertions(+), 267 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index cb18bdb166..7dce249f28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -135,7 +135,7 @@ void GLGizmoBase::Grabber::render_face(float half_size) const } -GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* common_data_ptr) : m_parent(parent) , m_group_id(-1) , m_state(Off) @@ -146,6 +146,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u , m_dragging(false) , m_imgui(wxGetApp().imgui()) , m_first_input_window_render(true) + , m_c(common_data_ptr) { ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float)); ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index da30427793..9479174fbd 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -30,7 +30,7 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; class ImGuiWrapper; - +class CommonGizmosData; class GLGizmoBase { @@ -99,9 +99,13 @@ protected: mutable std::vector m_grabbers; ImGuiWrapper* m_imgui; bool m_first_input_window_render; + CommonGizmosData* m_c = nullptr; public: - GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoBase(GLCanvas3D& parent, + const std::string& icon_filename, + unsigned int sprite_id, + CommonGizmosData* common_data = nullptr); virtual ~GLGizmoBase() {} bool init() { return on_init(); } @@ -179,6 +183,34 @@ protected: // were not interpolated by alpha blending or multi sampling. extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue); +class MeshRaycaster; +class MeshClipper; + +class CommonGizmosData { +public: + const TriangleMesh* mesh() const { + return (! m_mesh ? nullptr : (m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); + } + + + + ModelObject* m_model_object = nullptr; + const TriangleMesh* m_mesh; + std::unique_ptr m_mesh_raycaster; + std::unique_ptr m_object_clipper; + std::unique_ptr m_supports_clipper; + + std::unique_ptr m_cavity_mesh; + std::unique_ptr m_volume_with_cavity; + + int m_active_instance = -1; + float m_active_instance_bb_radius = 0; + ObjectID m_model_object_id = 0; + int m_print_object_idx = -1; + int m_print_objects_count = -1; + int m_old_timestamp = -1; +}; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index cad0243f08..f8f2e125fc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -18,8 +18,8 @@ namespace Slic3r { namespace GUI { -GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd) + : GLGizmoBase(parent, icon_filename, sprite_id, cd) , m_quadric(nullptr) { m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); @@ -58,23 +58,23 @@ bool GLGizmoHollow::on_init() void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Selection& selection) { if (! model_object || selection.is_empty()) { - m_model_object = nullptr; + m_c->m_model_object = nullptr; return; } - if (m_model_object != model_object || m_model_object_id != model_object->id()) { - m_model_object = model_object; - m_print_object_idx = -1; + if (m_c->m_model_object != model_object || m_c->m_model_object_id != model_object->id()) { + m_c->m_model_object = model_object; + m_c->m_print_object_idx = -1; } - m_active_instance = selection.get_instance_idx(); + m_c->m_active_instance = selection.get_instance_idx(); if (model_object && selection.is_from_single_instance()) { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. //FIXME calling ModelObject::instance_bounding_box() is expensive! - m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); + m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); if (is_mesh_update_necessary()) { update_mesh(); @@ -83,7 +83,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Select if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -96,24 +96,24 @@ void GLGizmoHollow::on_render() const { const Selection& selection = m_parent.get_selection(); - // If current m_model_object does not match selection, ask GLCanvas3D to turn us off + // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On - && (m_model_object != selection.get_model()->objects[selection.get_object_idx()] - || m_active_instance != selection.get_instance_idx() - || m_model_object_id != m_model_object->id())) { + && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] + || m_c->m_active_instance != selection.get_instance_idx() + || m_c->m_model_object_id != m_c->m_model_object->id())) { m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); return; } - if (! m_mesh) + if (! m_c->m_mesh) const_cast(this)->update_mesh(); glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); - if (m_volume_with_cavity) { + if (m_c->m_volume_with_cavity) { m_parent.get_shader().start_using(); - m_volume_with_cavity->render(); + m_c->m_volume_with_cavity->render(); m_parent.get_shader().stop_using(); } @@ -132,7 +132,7 @@ void GLGizmoHollow::on_render() const void GLGizmoHollow::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f || mesh()->empty()) + if (m_clipping_plane_distance == 0.f || m_c->mesh()->empty()) return; // Get transformation of the instance @@ -150,65 +150,65 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const 1.)); // Now initialize the TMS for the object, perform the cut and save the result. - if (! m_object_clipper) { - m_object_clipper.reset(new MeshClipper); - m_object_clipper->set_mesh(*mesh()); + if (! m_c->m_object_clipper) { + m_c->m_object_clipper.reset(new MeshClipper); + m_c->m_object_clipper->set_mesh(*m_c->mesh()); } - m_object_clipper->set_plane(*m_clipping_plane); - m_object_clipper->set_transformation(trafo); + m_c->m_object_clipper->set_plane(*m_clipping_plane); + m_c->m_object_clipper->set_transformation(trafo); // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. // First we need a pointer to the respective SLAPrintObject. The index into objects vector is // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) { - m_print_objects_count = m_parent.sla_print()->objects().size(); - m_print_object_idx = -1; + if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { + m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); + m_c->m_print_object_idx = -1; for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - ++m_print_object_idx; - if (po->model_object()->id() == m_model_object->id()) + ++m_c->m_print_object_idx; + if (po->model_object()->id() == m_c->m_model_object->id()) break; } } - if (m_print_object_idx >= 0) { - const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; + if (m_c->m_print_object_idx >= 0) { + const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { // If the supports are already calculated, save the timestamp of the respective step // so we can later tell they were recalculated. size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - if (! m_supports_clipper || (int)timestamp != m_old_timestamp) { + if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { // The timestamp has changed. - m_supports_clipper.reset(new MeshClipper); + m_c->m_supports_clipper.reset(new MeshClipper); // The mesh should already have the shared vertices calculated. - m_supports_clipper->set_mesh(print_object->support_mesh()); - m_old_timestamp = timestamp; + m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); + m_c->m_old_timestamp = timestamp; } - m_supports_clipper->set_plane(*m_clipping_plane); - m_supports_clipper->set_transformation(supports_trafo); + m_c->m_supports_clipper->set_plane(*m_clipping_plane); + m_c->m_supports_clipper->set_transformation(supports_trafo); } else // The supports are not valid. We better dump the cached data. - m_supports_clipper.reset(); + m_c->m_supports_clipper.reset(); } // At this point we have the triangulated cuts for both the object and supports - let's render. - if (! m_object_clipper->get_triangles().empty()) { + if (! m_c->m_object_clipper->get_triangles().empty()) { ::glPushMatrix(); ::glColor3f(1.0f, 0.37f, 0.0f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_object_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); } - if (m_show_supports && m_supports_clipper && ! m_supports_clipper->get_triangles().empty()) { + if (m_show_supports && m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty()) { ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_supports_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); @@ -241,10 +241,10 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons glsafe(::glMultMatrixd(instance_matrix.data())); float render_color[4]; - size_t cache_size = m_model_object->sla_drain_holes.size(); + size_t cache_size = m_c->m_model_object->sla_drain_holes.size(); for (size_t i = 0; i < cache_size; ++i) { - const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; + const sla::DrainHole& drain_hole = m_c->m_model_object->sla_drain_holes[i]; const bool& point_selected = m_selected[i]; if (is_mesh_point_clipped((drain_hole.pos+HoleStickOutLength*drain_hole.normal).cast())) @@ -324,7 +324,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const if (m_clipping_plane_distance == 0.f) return false; - Vec3d transformed_point = m_model_object->instances[m_active_instance]->get_transformation().get_matrix() * point; + Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; return m_clipping_plane->is_point_clipped(transformed_point); } @@ -333,34 +333,34 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const bool GLGizmoHollow::is_mesh_update_necessary() const { - return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_model_object_id) || ! m_mesh); + return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) + && ((m_c->m_model_object->id() != m_c->m_model_object_id) || ! m_c->m_mesh); } void GLGizmoHollow::update_mesh() { - if (! m_model_object) + if (! m_c->m_model_object) return; wxBusyCursor wait; // 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(); + m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); // If this is different mesh than last time - if (m_model_object_id != m_model_object->id()) { - m_cavity_mesh.reset(); // dump the cavity - m_volume_with_cavity.reset(); - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); - m_mesh_raycaster.reset(); + if (m_c->m_model_object_id != m_c->m_model_object->id()) { + m_c->m_cavity_mesh.reset(); // dump the cavity + m_c->m_volume_with_cavity.reset(); + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_c->m_mesh_raycaster.reset(); } - if (! m_mesh_raycaster) - m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); + if (! m_c->m_mesh_raycaster) + m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); - m_model_object_id = m_model_object->id(); + m_c->m_model_object_id = m_c->m_model_object->id(); } @@ -370,7 +370,7 @@ void GLGizmoHollow::update_mesh() bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: - if (! m_mesh_raycaster) + if (! m_c->m_mesh_raycaster) update_mesh(); const Camera& camera = m_parent.get_camera(); @@ -382,7 +382,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; @@ -427,10 +427,10 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, + m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); m_selected.push_back(false); - assert(m_selected.size() == m_model_object->sla_drain_holes.size()); + assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; } @@ -449,11 +449,11 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); std::vector points; - for (unsigned int i=0; isla_drain_holes.size(); ++i) - points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].pos.cast()); + for (unsigned int i=0; im_model_object->sla_drain_holes.size(); ++i) + points.push_back(trafo.get_matrix() * m_c->m_model_object->sla_drain_holes[i].pos.cast()); // Now ask the rectangle which of the points are inside. std::vector points_inside; @@ -462,7 +462,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -539,10 +539,10 @@ void GLGizmoHollow::delete_selected_points() { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete drainage hole"))); - for (unsigned int idx=0; idxsla_drain_holes.size(); ++idx) { + for (unsigned int idx=0; idxm_model_object->sla_drain_holes.size(); ++idx) { if (m_selected[idx]) { m_selected.erase(m_selected.begin()+idx); - m_model_object->sla_drain_holes.erase(m_model_object->sla_drain_holes.begin() + (idx--)); + m_c->m_model_object->sla_drain_holes.erase(m_c->m_model_object->sla_drain_holes.begin() + (idx--)); } } @@ -555,8 +555,8 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; - m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + m_c->m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; } } @@ -568,14 +568,14 @@ std::pair GLGizmoHollow::get_hollowi double offset = static_cast(opts[0])->value; double quality = static_cast(opts[1])->value; double closing_d = static_cast(opts[2])->value; - return std::make_pair(m_mesh, sla::HollowingConfig{offset, quality, closing_d}); + return std::make_pair(m_c->m_mesh, sla::HollowingConfig{offset, quality, closing_d}); } void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) { - m_mesh_raycaster = std::move(rc); - m_object_clipper.reset(); - m_volume_with_cavity.reset(); + m_c->m_mesh_raycaster = std::move(rc); + m_c->m_object_clipper.reset(); + m_c->m_volume_with_cavity.reset(); } void GLGizmoHollow::hollow_mesh() @@ -588,13 +588,13 @@ void GLGizmoHollow::hollow_mesh() void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) { // Called from Plater when the UI job finishes - m_cavity_mesh = std::move(mesh); + m_c->m_cavity_mesh = std::move(mesh); - if(m_cavity_mesh) { + if(m_c->m_cavity_mesh) { // First subtract the holes: - if (! m_model_object->sla_drain_holes.empty()) { + if (! m_c->m_model_object->sla_drain_holes.empty()) { TriangleMesh holes_mesh; - for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { + for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/8); Eigen::Quaternionf q; Transform3f m = Transform3f::Identity(); @@ -602,22 +602,22 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) hole_mesh.transform(m.cast()); hole_mesh.translate(hole.pos); holes_mesh.merge(hole_mesh); - //MeshBoolean::minus(*m_cavity_mesh.get(), hole_mesh); + //MeshBoolean::minus(*m_c->m_cavity_mesh.get(), hole_mesh); } - MeshBoolean::minus(*m_cavity_mesh.get(), holes_mesh); + MeshBoolean::minus(*m_c->m_cavity_mesh.get(), holes_mesh); } // create a new GLVolume that only has the cavity inside - Geometry::Transformation volume_trafo = m_model_object->volumes.front()->get_transformation(); + Geometry::Transformation volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); - m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_cavity_mesh.get()); - m_volume_with_cavity->finalize_geometry(true); - m_volume_with_cavity->set_volume_transformation(volume_trafo); - m_volume_with_cavity->set_instance_transformation(m_model_object->instances[size_t(m_active_instance)]->get_transformation()); + m_c->m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_c->m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_c->m_cavity_mesh.get()); + m_c->m_volume_with_cavity->finalize_geometry(true); + m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); } - m_parent.toggle_model_objects_visibility(! m_cavity_mesh, m_model_object, m_active_instance); + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); if (m_clipping_plane_distance == 0.f) { m_clipping_plane_distance = 0.5f; update_clipping_plane(); @@ -628,10 +628,10 @@ std::vector GLGizmoHollow::get_config_options(const std::ve { std::vector out; - if (!m_model_object) + if (!m_c->m_model_object) return out; - const DynamicPrintConfig& object_cfg = m_model_object->config; + const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -654,7 +654,7 @@ std::vector GLGizmoHollow::get_config_options(const std::ve ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const { - if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) return ClippingPlane::ClipsNothing(); else return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); @@ -663,7 +663,7 @@ ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) { - if (! m_model_object) + if (! m_c->m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, @@ -694,7 +694,7 @@ RENDER_AGAIN: std::vector opts = get_config_options({"hollowing_enable"}); m_enable_hollowing = static_cast(opts[0])->value; if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { - m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; + m_c->m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; wxGetApp().obj_list()->update_and_show_object_settings_item(); } } @@ -738,14 +738,14 @@ RENDER_AGAIN: } if (slider_edited || slider_released) { if (slider_released) { - m_model_object->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; - m_model_object->config.opt("hollowing_quality", true)->value = m_quality_stash; - m_model_object->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; + m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = m_offset_stash; + m_c->m_model_object->config.opt("hollowing_quality", true)->value = m_quality_stash; + m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = m_closing_d_stash; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Hollowing parameter change"))); } - m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; - m_model_object->config.opt("hollowing_quality", true)->value = quality; - m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; + m_c->m_model_object->config.opt("hollowing_min_thickness", true)->value = offset; + m_c->m_model_object->config.opt("hollowing_quality", true)->value = quality; + m_c->m_model_object->config.opt("hollowing_closing_distance", true)->value = closing_d; if (slider_released) wxGetApp().obj_list()->update_and_show_object_settings_item(); } @@ -786,19 +786,19 @@ RENDER_AGAIN: // - take correct undo/redo snapshot after the user is done with moving the slider if (! m_selection_empty) { if (clicked) { - m_holes_stash = m_model_object->sla_drain_holes; + m_holes_stash = m_c->m_model_object->sla_drain_holes; } if (edited) { for (size_t idx=0; idxsla_drain_holes[idx].radius = m_new_hole_radius; - m_model_object->sla_drain_holes[idx].height = m_new_hole_height; + m_c->m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; + m_c->m_model_object->sla_drain_holes[idx].height = m_new_hole_height; } } if (deactivated) { // momentarily restore the old value to take snapshot - sla::DrainHoles new_holes = m_model_object->sla_drain_holes; - m_model_object->sla_drain_holes = m_holes_stash; + sla::DrainHoles new_holes = m_c->m_model_object->sla_drain_holes; + m_c->m_model_object->sla_drain_holes = m_holes_stash; float backup_rad = m_new_hole_radius; float backup_hei = m_new_hole_height; for (size_t i=0; isla_drain_holes = new_holes; + m_c->m_model_object->sla_drain_holes = new_holes; } } @@ -819,7 +819,7 @@ RENDER_AGAIN: remove_selected = m_imgui->button(m_desc.at("remove_selected")); m_imgui->disabled_end(); - m_imgui->disabled_begin(m_model_object->sla_drain_holes.empty()); + m_imgui->disabled_begin(m_c->m_model_object->sla_drain_holes.empty()); remove_all = m_imgui->button(m_desc.at("remove_all")); m_imgui->disabled_end(); @@ -842,7 +842,7 @@ RENDER_AGAIN: // make sure supports are shown/hidden as appropriate m_imgui->checkbox(m_desc["show_supports"], m_show_supports); - force_refresh = m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_model_object, m_active_instance); + force_refresh = m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); m_imgui->end(); @@ -896,19 +896,19 @@ std::string GLGizmoHollow::on_get_name() const } -const TriangleMesh* GLGizmoHollow::mesh() const { - return (! m_mesh ? nullptr : (m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); -} +//const TriangleMesh* GLGizmoHollow::mesh() const { +// return (! m_c->m_mesh ? nullptr : (m_c->m_cavity_mesh ? m_c->m_cavity_mesh.get() : m_c->m_mesh)); +//} void GLGizmoHollow::on_set_state() { - // m_model_object pointer can be invalid (for instance because of undo/redo action), + // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id - m_model_object = nullptr; + m_c->m_model_object = nullptr; for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_model_object_id) { - m_model_object = mo; + if (mo->id() == m_c->m_model_object_id) { + m_c->m_model_object = mo; break; } } @@ -922,12 +922,12 @@ void GLGizmoHollow::on_set_state() update_mesh(); // we'll now reload support points: - if (m_model_object) + if (m_c->m_model_object) reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_model_object) - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + if (m_c->m_model_object) + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; @@ -938,11 +938,11 @@ void GLGizmoHollow::on_set_state() m_parent.toggle_model_objects_visibility(true); m_clipping_plane_distance = 0.f; // Release clippers and the AABB raycaster. - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_mesh_raycaster.reset(); - m_cavity_mesh.reset(); - m_volume_with_cavity.reset(); + m_c->m_object_clipper.reset(); + m_c->m_supports_clipper.reset(); + m_c->m_mesh_raycaster.reset(); + m_c->m_cavity_mesh.reset(); + m_c->m_volume_with_cavity.reset(); } m_old_state = m_state; } @@ -954,7 +954,7 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].pos; + m_hole_before_drag = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; } else m_hole_before_drag = Vec3f::Zero(); @@ -964,14 +964,14 @@ void GLGizmoHollow::on_start_dragging() void GLGizmoHollow::on_stop_dragging() { if (m_hover_id != -1) { - Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].pos; + Vec3f backup = m_c->m_model_object->sla_drain_holes[m_hover_id].pos; if (m_hole_before_drag != Vec3f::Zero() // some point was touched && backup != m_hole_before_drag) // and it was moved, not just selected { - m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_model_object->sla_drain_holes[m_hover_id].pos = backup; + m_c->m_model_object->sla_drain_holes[m_hover_id].pos = backup; } } m_hole_before_drag = Vec3f::Zero(); @@ -983,7 +983,7 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) { ar(m_clipping_plane_distance, *m_clipping_plane, - m_model_object_id, + m_c->m_model_object_id, m_new_hole_radius, m_new_hole_height, m_selected, @@ -997,7 +997,7 @@ void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const { ar(m_clipping_plane_distance, *m_clipping_plane, - m_model_object_id, + m_c->m_model_object_id, m_new_hole_radius, m_new_hole_height, m_selected, @@ -1014,8 +1014,8 @@ void GLGizmoHollow::select_point(int i) m_selection_empty = (i == NoPoints); if (i == AllPoints) { - m_new_hole_radius = m_model_object->sla_drain_holes[0].radius; - m_new_hole_height = m_model_object->sla_drain_holes[0].height; + m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height; } } else { @@ -1023,8 +1023,8 @@ void GLGizmoHollow::select_point(int i) m_selected.push_back(false); m_selected[i] = true; m_selection_empty = false; - m_new_hole_radius = m_model_object->sla_drain_holes[i].radius; - m_new_hole_height = m_model_object->sla_drain_holes[i].height; + m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height; } } @@ -1044,7 +1044,7 @@ void GLGizmoHollow::unselect_point(int i) void GLGizmoHollow::reload_cache() { m_selected.clear(); - m_selected.assign(m_model_object->sla_drain_holes.size(), false); + m_selected.assign(m_c->m_model_object->sla_drain_holes.size(), false); } void GLGizmoHollow::update_clipping_plane(bool keep_normal) const @@ -1052,9 +1052,9 @@ void GLGizmoHollow::update_clipping_plane(bool keep_normal) const Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); - const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); + const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); float dist = normal.dot(center); - *m_clipping_plane = ClippingPlane(normal, (dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); + *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 8e022eb1e6..bea3960973 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -21,10 +21,10 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoHollow : public GLGizmoBase { private: - ModelObject* m_model_object = nullptr; - ObjectID m_model_object_id = 0; - int m_active_instance = -1; - float m_active_instance_bb_radius; // to cache the bb + //ModelObject* m_model_object = nullptr; + //ObjectID m_model_object_id = 0; + //int m_active_instance = -1; + //float m_active_instance_bb_radius; // to cache the bb mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); @@ -32,19 +32,16 @@ private: GLUquadricObj* m_quadric; - std::unique_ptr m_mesh_raycaster; - std::unique_ptr m_cavity_mesh; - std::unique_ptr m_volume_with_cavity; - const TriangleMesh* m_mesh; - mutable const TriangleMesh* m_supports_mesh; - mutable std::vector m_triangles; - mutable std::vector m_supports_triangles; - mutable int m_old_timestamp = -1; - mutable int m_print_object_idx = -1; - mutable int m_print_objects_count = -1; + //std::unique_ptr m_mesh_raycaster; + //std::unique_ptr m_cavity_mesh; + //std::unique_ptr m_volume_with_cavity; + //const TriangleMesh* m_mesh; + //mutable int m_old_timestamp = -1; + //mutable int m_print_object_idx = -1; + //mutable int m_print_objects_count = -1; public: - GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd); ~GLGizmoHollow() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); @@ -70,7 +67,6 @@ private: void update_mesh(); void hollow_mesh(); bool unsaved_changes() const; - const TriangleMesh* mesh() const; bool m_show_supports = true; float m_new_hole_radius; // Size of a new hole. @@ -104,8 +100,8 @@ private: bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - mutable std::unique_ptr m_object_clipper; - mutable std::unique_ptr m_supports_clipper; + //mutable std::unique_ptr m_object_clipper; + //mutable std::unique_ptr m_supports_clipper; std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; @@ -126,7 +122,7 @@ protected: void on_set_hover_id() override { - if (int(m_model_object->sla_drain_holes.size()) <= m_hover_id) + if (int(m_c->m_model_object->sla_drain_holes.size()) <= m_hover_id) m_hover_id = -1; } void on_start_dragging() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index b10f571089..85d57a211e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -22,8 +22,8 @@ namespace Slic3r { namespace GUI { -GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd) + : GLGizmoBase(parent, icon_filename, sprite_id, cd) , m_quadric(nullptr) , m_its(nullptr) { @@ -64,23 +64,23 @@ bool GLGizmoSlaSupports::on_init() void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) { if (! model_object || selection.is_empty()) { - m_model_object = nullptr; + m_c->m_model_object = nullptr; return; } - if (m_model_object != model_object || m_model_object_id != model_object->id()) { - m_model_object = model_object; - m_print_object_idx = -1; + if (m_c->m_model_object != model_object || m_c->m_model_object_id != model_object->id()) { + m_c->m_model_object = model_object; + m_c->m_print_object_idx = -1; } - m_active_instance = selection.get_instance_idx(); + m_c->m_active_instance = selection.get_instance_idx(); if (model_object && selection.is_from_single_instance()) { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. //FIXME calling ModelObject::instance_bounding_box() is expensive! - m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); + m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); if (is_mesh_update_necessary()) { update_mesh(); @@ -88,12 +88,12 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S } // If we triggered autogeneration before, check backend and fetch results if they are there - if (m_model_object->sla_points_status == sla::PointsStatus::Generating) + if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -106,16 +106,16 @@ void GLGizmoSlaSupports::on_render() const { const Selection& selection = m_parent.get_selection(); - // If current m_model_object does not match selection, ask GLCanvas3D to turn us off + // If current m_c->m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On - && (m_model_object != selection.get_model()->objects[selection.get_object_idx()] - || m_active_instance != selection.get_instance_idx() - || m_model_object_id != m_model_object->id())) { + && (m_c->m_model_object != selection.get_model()->objects[selection.get_object_idx()] + || m_c->m_active_instance != selection.get_instance_idx() + || m_c->m_model_object_id != m_c->m_model_object->id())) { m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS)); return; } - if (! m_its || ! m_mesh) + if (! m_its || ! m_c->m_mesh) const_cast(this)->update_mesh(); glsafe(::glEnable(GL_BLEND)); @@ -136,7 +136,7 @@ void GLGizmoSlaSupports::on_render() const void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f || m_mesh->empty()) + if (m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) return; // Get transformation of the instance @@ -154,66 +154,66 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const 1.)); // Now initialize the TMS for the object, perform the cut and save the result. - if (! m_object_clipper) { - m_object_clipper.reset(new MeshClipper); - m_object_clipper->set_mesh(*m_mesh); + if (! m_c->m_object_clipper) { + m_c->m_object_clipper.reset(new MeshClipper); + m_c->m_object_clipper->set_mesh(*m_c->mesh()); } - m_object_clipper->set_plane(*m_clipping_plane); - m_object_clipper->set_transformation(trafo); + m_c->m_object_clipper->set_plane(*m_clipping_plane); + m_c->m_object_clipper->set_transformation(trafo); // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. // First we need a pointer to the respective SLAPrintObject. The index into objects vector is // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_print_objects_count) { - m_print_objects_count = m_parent.sla_print()->objects().size(); - m_print_object_idx = -1; + if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { + m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); + m_c->m_print_object_idx = -1; for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - ++m_print_object_idx; - if (po->model_object()->id() == m_model_object->id()) + ++m_c->m_print_object_idx; + if (po->model_object()->id() == m_c->m_model_object->id()) break; } } - if (m_print_object_idx >= 0) { - const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_print_object_idx]; + if (m_c->m_print_object_idx >= 0) { + const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; if (print_object->is_step_done(slaposSupportTree) && !print_object->get_mesh(slaposSupportTree).empty()) { // If the supports are already calculated, save the timestamp of the respective step // so we can later tell they were recalculated. size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp; - if (! m_supports_clipper || (int)timestamp != m_old_timestamp) { + if (! m_c->m_supports_clipper || (int)timestamp != m_c->m_old_timestamp) { // The timestamp has changed. - m_supports_clipper.reset(new MeshClipper); + m_c->m_supports_clipper.reset(new MeshClipper); // The mesh should already have the shared vertices calculated. - m_supports_clipper->set_mesh(print_object->support_mesh()); - m_old_timestamp = timestamp; + m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); + m_c->m_old_timestamp = timestamp; } - m_supports_clipper->set_plane(*m_clipping_plane); - m_supports_clipper->set_transformation(supports_trafo); + m_c->m_supports_clipper->set_plane(*m_clipping_plane); + m_c->m_supports_clipper->set_transformation(supports_trafo); } else // The supports are not valid. We better dump the cached data. - m_supports_clipper.reset(); + m_c->m_supports_clipper.reset(); } // At this point we have the triangulated cuts for both the object and supports - let's render. - if (! m_object_clipper->get_triangles().empty()) { + if (! m_c->m_object_clipper->get_triangles().empty()) { ::glPushMatrix(); ::glColor3f(1.0f, 0.37f, 0.0f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_object_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_object_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); } - if (m_supports_clipper && ! m_supports_clipper->get_triangles().empty() && !m_editing_mode) { + if (m_c->m_supports_clipper && ! m_c->m_supports_clipper->get_triangles().empty() && !m_editing_mode) { // The supports are hidden in the editing mode, so it makes no sense to render the cuts. ::glPushMatrix(); ::glColor3f(1.0f, 0.f, 0.37f); ::glBegin(GL_TRIANGLES); - for (const Vec3f& point : m_supports_clipper->get_triangles()) + for (const Vec3f& point : m_c->m_supports_clipper->get_triangles()) ::glVertex3f(point(0), point(1), point(2)); ::glEnd(); ::glPopMatrix(); @@ -298,7 +298,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) if (m_editing_mode) { // in case the normal is not yet cached, find and cache it if (m_editing_cache[i].normal == Vec3f::Zero()) - m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); + m_c->m_mesh_raycaster->get_closest_point(m_editing_cache[i].support_point.pos, &m_editing_cache[i].normal); Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); @@ -333,7 +333,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) render_color[2] = 0.7f; render_color[3] = 0.7f; glsafe(::glColor4fv(render_color)); - for (const sla::DrainHole& drain_hole : m_model_object->sla_drain_holes) { + for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); @@ -377,7 +377,7 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const if (m_clipping_plane_distance == 0.f) return false; - Vec3d transformed_point = m_model_object->instances[m_active_instance]->get_transformation().get_matrix() * point; + Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; return m_clipping_plane->is_point_clipped(transformed_point); } @@ -386,28 +386,28 @@ bool GLGizmoSlaSupports::is_mesh_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_model_object_id) || m_its == nullptr); + return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) + && ((m_c->m_model_object->id() != m_c->m_model_object_id) || m_its == nullptr); } void GLGizmoSlaSupports::update_mesh() { - if (! m_model_object) + if (! m_c->m_model_object) return; wxBusyCursor wait; // 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(); - m_its = &m_mesh->its; + m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); + m_its = &m_c->m_mesh->its; // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. - if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster) - m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); + if (m_c->m_model_object_id != m_c->m_model_object->id() || ! m_c->m_mesh_raycaster) + m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); - m_model_object_id = m_model_object->id(); + m_c->m_model_object_id = m_c->m_model_object->id(); disable_editing_mode(); } @@ -417,7 +417,7 @@ void GLGizmoSlaSupports::update_mesh() bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { // if the gizmo doesn't have the V, F structures for igl, calculate them first: - if (! m_mesh_raycaster) + if (! m_c->m_mesh_raycaster) update_mesh(); const Camera& camera = m_parent.get_camera(); @@ -429,10 +429,10 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairunproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { // Check whether the hit is in a hole bool in_hole = false; - for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { + for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { if (hole.is_inside(hit)) { in_hole = true; break; @@ -505,7 +505,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state(); // First collect positions of all the points in world coordinates. - Geometry::Transformation trafo = m_model_object->instances[m_active_instance]->get_transformation(); + Geometry::Transformation trafo = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation(); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); std::vector points; for (unsigned int i=0; i()); // Only select/deselect points that are actually visible - for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -656,10 +656,10 @@ std::vector GLGizmoSlaSupports::get_config_options(const st { std::vector out; - if (!m_model_object) + if (!m_c->m_model_object) return out; - const DynamicPrintConfig& object_cfg = m_model_object->config; + const DynamicPrintConfig& object_cfg = m_c->m_model_object->config; const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; std::unique_ptr default_cfg = nullptr; @@ -682,7 +682,7 @@ std::vector GLGizmoSlaSupports::get_config_options(const st ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const { - if (!m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) return ClippingPlane::ClipsNothing(); else return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); @@ -711,7 +711,7 @@ void GLGizmoSlaSupports::find_intersecting_facets(const igl::AABBvolumes.front()->mesh); + TriangleMeshSlicer tms(&m_c->m_model_object->volumes.front()->mesh); Vec3f normal(0.f, 1.f, 1.f); double d = 0.; @@ -732,7 +732,7 @@ void GLGizmoSlaSupports::make_line_segments() const void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_model_object) + if (!m_c->m_model_object) return; bool first_run = true; // This is a hack to redraw the button when all points are removed, @@ -853,15 +853,15 @@ RENDER_AGAIN: m_density_stash = density; } if (slider_edited) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; } if (slider_released) { - m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)m_density_stash; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change"))); - m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; - m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; + m_c->m_model_object->config.opt("support_points_minimal_distance", true)->value = minimal_point_distance; + m_c->m_model_object->config.opt("support_points_density_relative", true)->value = (int)density; wxGetApp().obj_list()->update_and_show_object_settings_item(); } @@ -879,10 +879,10 @@ RENDER_AGAIN: m_imgui->disabled_end(); // m_imgui->text(""); - // m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::NoPoints ? _(L("No points (will be autogenerated)")) : - // (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) : - // (m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) : - // (m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS")))); + // m_imgui->text(m_c->m_model_object->sla_points_status == sla::PointsStatus::NoPoints ? _(L("No points (will be autogenerated)")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) : + // (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS")))); } @@ -920,7 +920,7 @@ RENDER_AGAIN: // is done on each refresh because the user can switch the editing mode // before background process finishes. force_refresh = m_parent.toggle_sla_auxiliaries_visibility( - ! m_editing_mode, m_model_object, m_active_instance); + ! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); if (remove_selected || remove_all) { force_refresh = false; @@ -978,12 +978,12 @@ std::string GLGizmoSlaSupports::on_get_name() const void GLGizmoSlaSupports::on_set_state() { - // m_model_object pointer can be invalid (for instance because of undo/redo action), + // m_c->m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id - m_model_object = nullptr; + m_c->m_model_object = nullptr; for (const auto mo : wxGetApp().model().objects) { - if (mo->id() == m_model_object_id) { - m_model_object = mo; + if (mo->id() == m_c->m_model_object_id) { + m_c->m_model_object = mo; break; } } @@ -997,19 +997,19 @@ void GLGizmoSlaSupports::on_set_state() update_mesh(); // we'll now reload support points: - if (m_model_object) + if (m_c->m_model_object) reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_model_object) - m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance); + if (m_c->m_model_object) + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off - bool will_ask = m_model_object && m_editing_mode && unsaved_changes(); + bool will_ask = m_c->m_model_object && m_editing_mode && unsaved_changes(); if (will_ask) { wxGetApp().CallAfter([this]() { // Following is called through CallAfter, because otherwise there was a problem @@ -1033,9 +1033,9 @@ void GLGizmoSlaSupports::on_set_state() m_clipping_plane_distance = 0.f; // Release clippers and the AABB raycaster. m_its = nullptr; - m_object_clipper.reset(); - m_supports_clipper.reset(); - m_mesh_raycaster.reset(); + m_c->m_object_clipper.reset(); + m_c->m_supports_clipper.reset(); + m_c->m_mesh_raycaster.reset(); } } m_old_state = m_state; @@ -1077,7 +1077,7 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) { ar(m_clipping_plane_distance, *m_clipping_plane, - m_model_object_id, + m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, m_editing_cache, @@ -1091,7 +1091,7 @@ void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const { ar(m_clipping_plane_distance, *m_clipping_plane, - m_model_object_id, + m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, m_editing_cache, @@ -1169,9 +1169,9 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() for (const CacheEntry& ce : m_editing_cache) m_normal_cache.push_back(ce.support_point); - m_model_object->sla_points_status = sla::PointsStatus::UserModified; - m_model_object->sla_support_points.clear(); - m_model_object->sla_support_points = m_normal_cache; + m_c->m_model_object->sla_points_status = sla::PointsStatus::UserModified; + m_c->m_model_object->sla_support_points.clear(); + m_c->m_model_object->sla_support_points = m_normal_cache; reslice_SLA_supports(); } @@ -1182,10 +1182,10 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() void GLGizmoSlaSupports::reload_cache() { m_normal_cache.clear(); - if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_model_object->sla_points_status == sla::PointsStatus::Generating) + if (m_c->m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); else - for (const sla::SupportPoint& point : m_model_object->sla_support_points) + for (const sla::SupportPoint& point : m_c->m_model_object->sla_support_points) m_normal_cache.emplace_back(point); } @@ -1194,7 +1194,7 @@ bool GLGizmoSlaSupports::has_backend_supports() const { // find SlaPrintObject with this ID for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_model_object->id()) + if (po->model_object()->id() == m_c->m_model_object->id()) return po->is_step_done(slaposSupportPoints); } return false; @@ -1202,7 +1202,7 @@ bool GLGizmoSlaSupports::has_backend_supports() const void GLGizmoSlaSupports::reslice_SLA_supports(bool postpone_error_messages) const { - wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_model_object, postpone_error_messages); }); + wxGetApp().CallAfter([this, postpone_error_messages]() { wxGetApp().plater()->reslice_SLA_supports(*m_c->m_model_object, postpone_error_messages); }); } void GLGizmoSlaSupports::get_data_from_backend() @@ -1212,14 +1212,14 @@ void GLGizmoSlaSupports::get_data_from_backend() // find the respective SLAPrintObject, we need a pointer to it for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - if (po->model_object()->id() == m_model_object->id()) { + if (po->model_object()->id() == m_c->m_model_object->id()) { m_normal_cache.clear(); const std::vector& points = po->get_support_points(); auto mat = po->trafo().inverse().cast(); for (unsigned int i=0; isla_points_status = sla::PointsStatus::AutoGenerated; + m_c->m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated; break; } } @@ -1236,10 +1236,10 @@ void GLGizmoSlaSupports::auto_generate() _(L("Are you sure you want to do it?")) + "\n", _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { + if (m_c->m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points"))); wxGetApp().CallAfter([this]() { reslice_SLA_supports(); }); - m_model_object->sla_points_status = sla::PointsStatus::Generating; + m_c->m_model_object->sla_points_status = sla::PointsStatus::Generating; } } @@ -1284,9 +1284,9 @@ void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); - const Vec3d& center = m_model_object->instances[m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); + const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); float dist = normal.dot(center); - *m_clipping_plane = ClippingPlane(normal, (dist - (-m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_active_instance_bb_radius)); + *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 1de241a53c..7700ad3a61 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -21,10 +21,10 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoSlaSupports : public GLGizmoBase { private: - ModelObject* m_model_object = nullptr; - ObjectID m_model_object_id = 0; - int m_active_instance = -1; - float m_active_instance_bb_radius; // to cache the bb + //ModelObject* m_model_object = nullptr; + //ObjectID m_model_object_id = 0; + //int m_active_instance = -1; + //float m_active_instance_bb_radius; // to cache the bb mutable double m_z_shift = 0.f; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); @@ -34,15 +34,12 @@ private: typedef Eigen::Map> MapMatrixXfUnaligned; typedef Eigen::Map> MapMatrixXiUnaligned; - std::unique_ptr m_mesh_raycaster; - const TriangleMesh* m_mesh; + //std::unique_ptr m_mesh_raycaster; + //const TriangleMesh* m_mesh; const indexed_triangle_set* m_its; - mutable const TriangleMesh* m_supports_mesh; - mutable std::vector m_triangles; - mutable std::vector m_supports_triangles; - mutable int m_old_timestamp = -1; - mutable int m_print_object_idx = -1; - mutable int m_print_objects_count = -1; + //mutable int m_old_timestamp = -1; + //mutable int m_print_object_idx = -1; + //mutable int m_print_objects_count = -1; class CacheEntry { public: @@ -72,7 +69,7 @@ private: }; public: - GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd); ~GLGizmoSlaSupports() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); @@ -120,8 +117,8 @@ private: bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - mutable std::unique_ptr m_object_clipper; - mutable std::unique_ptr m_supports_clipper; + //mutable std::unique_ptr m_object_clipper; + //mutable std::unique_ptr m_supports_clipper; std::vector get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 89a3134456..e089de3fc9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -88,13 +88,15 @@ bool GLGizmosManager::init() return false; } + m_common_gizmos_data.reset(new CommonGizmosData()); + m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, "move.svg", 0)); m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); - m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5)); - m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 6)); + m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5, m_common_gizmos_data.get())); + m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 6, m_common_gizmos_data.get())); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 2c4d713161..f816056a00 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -101,6 +101,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; bool m_serializing; + std::unique_ptr m_common_gizmos_data; public: explicit GLGizmosManager(GLCanvas3D& parent); From 17de6ff51a7a1cab9cb90ec877f57ea44729fe80 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 16:26:06 +0100 Subject: [PATCH 069/130] Fix linking of OpenVDB in debug mode on multi conf generators. --- cmake/modules/FindOpenVDB.cmake | 50 +++++++++++++++++++++++++++------ deps/CMakeLists.txt | 1 + 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 3400fa786b..e000b05dde 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -215,20 +215,44 @@ if(UNIX AND OPENVDB_USE_STATIC_LIBS) endif() set(OpenVDB_LIB_COMPONENTS "") +set(OpenVDB_DEBUG_SUFFIX "d" CACHE STRING "Suffix for the debug libraries") + +get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) set(LIB_NAME ${COMPONENT}) - find_library(OpenVDB_${COMPONENT}_LIBRARY ${LIB_NAME} lib${LIB_NAME} + + find_library(OpenVDB_${COMPONENT}_LIBRARY_RELEASE ${LIB_NAME} lib${LIB_NAME} PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} ) - list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) - if(OpenVDB_${COMPONENT}_LIBRARY) - set(OpenVDB_${COMPONENT}_FOUND TRUE) - else() - set(OpenVDB_${COMPONENT}_FOUND FALSE) - endif() + find_library(OpenVDB_${COMPONENT}_LIBRARY_DEBUG ${LIB_NAME}${OpenVDB_DEBUG_SUFFIX} lib${LIB_NAME}${OpenVDB_DEBUG_SUFFIX} + PATHS ${_OPENVDB_LIBRARYDIR_SEARCH_DIRS} + PATH_SUFFIXES ${OPENVDB_PATH_SUFFIXES} + ) + + if (_is_multi) + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) + + if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) + set(OpenVDB_${COMPONENT}_FOUND TRUE) + else() + set(OpenVDB_${COMPONENT}_FOUND FALSE) + endif() + else () + string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE) + + set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}}) + + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) + + if(OpenVDB_${COMPONENT}_LIBRARY) + set(OpenVDB_${COMPONENT}_FOUND TRUE) + else() + set(OpenVDB_${COMPONENT}_FOUND FALSE) + endif() + endif () endforeach() if(UNIX AND OPENVDB_USE_STATIC_LIBS) @@ -487,7 +511,6 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) if(NOT TARGET OpenVDB::${COMPONENT}) add_library(OpenVDB::${COMPONENT} UNKNOWN IMPORTED) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES - IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" INTERFACE_COMPILE_OPTIONS "${OpenVDB_DEFINITIONS}" INTERFACE_INCLUDE_DIRECTORIES "${OpenVDB_INCLUDE_DIR}" IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps @@ -495,6 +518,17 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) INTERFACE_COMPILE_FEATURES cxx_std_11 ) + if (_is_multi) + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" + IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" + ) + else () + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" + ) + endif () + if (OPENVDB_USE_STATIC_LIBS) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5e6d28b587..3e6f70e427 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -47,6 +47,7 @@ message(STATUS "PrusaSlicer deps debug build: ${DEP_DEBUG}") find_package(Git REQUIRED) +get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) From 558529146cae7b031bedb97cc389fdab8b9cef3d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 16:27:28 +0100 Subject: [PATCH 070/130] Fix opencsg example on Win32 --- sandboxes/opencsg/CMakeLists.txt | 12 ++++++++--- sandboxes/opencsg/GLScene.cpp | 35 ++++++++++++++++---------------- sandboxes/opencsg/GLScene.hpp | 3 +++ sandboxes/opencsg/main.cpp | 34 +++++++++++++++++++------------ 4 files changed, 51 insertions(+), 33 deletions(-) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index 651fbe82f7..cf66867a56 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(OpenCSG-example) -add_executable(opencsg_example main.cpp GLScene.hpp GLScene.cpp +add_executable(opencsg_example WIN32 main.cpp GLScene.hpp GLScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) @@ -11,11 +11,17 @@ find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenCSG REQUIRED) -find_package(GLUT REQUIRED) +# find_package(GLUT REQUIRED) include(${wxWidgets_USE_FILE}) target_link_libraries(opencsg_example libslic3r) target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS}) target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) -target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} GLEW::GLEW OpenCSG::opencsg GLUT::GLUT OpenGL::OpenGL -lXrandr -lXext -lX11) + +target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} + OpenCSG::opencsg + GLEW::GLEW + OpenGL::GL + #-lXrandr -lXext -lX11 + ) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 1cfccb3b19..2c3d1ffcd1 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -5,11 +5,11 @@ #include -#ifdef __APPLE__ -#include -#else -#include -#endif +//#ifdef __APPLE__ +//#include +//#else +//#include +//#endif #include @@ -63,7 +63,7 @@ void renderfps () { static int msec = 0; last = msec; - msec = glutGet(GLUT_ELAPSED_TIME); +// msec = glutGet(GLUT_ELAPSED_TIME); if (last / 1000 != msec / 1000) { float correctedFps = fps * 1000.0f / float(msec - ancient); @@ -82,9 +82,9 @@ void renderfps () { glRasterPos2f(-1.0f, -1.0f); glDisable(GL_LIGHTING); std::string s = fpsStream.str(); - for (unsigned int i=0; iset_screen(width, height); + set_screen_size(width, height); } void Display::set_screen_size(long width, long height) @@ -389,8 +392,6 @@ void Display::set_screen_size(long width, long height) m_camera->set_screen(width, height); m_size = {width, height}; - - repaint(); } void Display::repaint() diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 68cc59b01c..62863dc0b2 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -303,6 +303,8 @@ public: : m_camera(camera ? camera : std::make_shared()) {} + ~Display() override; + Camera * camera() { return m_camera.get(); } virtual void swap_buffers() = 0; @@ -370,5 +372,6 @@ public: void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); } }; + }} // namespace Slic3r::GL #endif // GLSCENE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index c2f8a74aa1..3f764fe626 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -8,7 +8,7 @@ // For compilers that support precompilation, includes "wx/wx.h". #include #ifndef WX_PRECOMP - #include +#include #endif #include @@ -31,7 +31,7 @@ using namespace Slic3r::GL; class Canvas: public wxGLCanvas, public Slic3r::GL::Display { - std::unique_ptr m_context; + shptr m_context; public: void set_active(long w, long h) override @@ -54,6 +54,12 @@ public: m_context.reset(ctx); } + + ~Canvas() override + { + m_scene_cache.clear(); + m_context.reset(); + } }; class MyFrame: public wxFrame @@ -95,11 +101,11 @@ private: void bind_canvas_events_to_controller(); - void OnExit(wxCommandEvent& /*event*/) + void OnClose(wxCloseEvent& /*event*/) { RemoveChild(m_canvas.get()); - m_canvas->Destroy(); - Close( true ); + m_canvas.reset(); + Destroy(); } void OnOpen(wxCommandEvent &/*evt*/) @@ -117,10 +123,7 @@ private: void OnShown(wxShowEvent&) { const wxSize ClientSize = GetClientSize(); - m_canvas->set_active(ClientSize.x, ClientSize.y); - - m_canvas->set_screen_size(ClientSize.x, ClientSize.y); - m_canvas->repaint(); + m_canvas->set_active(ClientSize.x, ClientSize.y); // Do the repaint continuously Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { @@ -179,8 +182,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); m_ctl->add_display(m_canvas); - + wxPanel *control_panel = new wxPanel(this); + auto controlsizer = new wxBoxSizer(wxHORIZONTAL); auto slider_sizer = new wxBoxSizer(wxVERTICAL); auto console_sizer = new wxBoxSizer(wxVERTICAL); @@ -247,7 +251,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): SetSizer(sizer); Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); - Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); + Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); + Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT); + Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { @@ -334,7 +340,7 @@ void MyFrame::bind_canvas_events_to_controller() m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { // This is required even though dc is not used otherwise. - wxPaintDC dc(this); + wxPaintDC dc(m_canvas.get()); // Set the OpenGL viewport according to the client size of this // canvas. This is done here rather than in a wxSizeEvent handler @@ -347,7 +353,7 @@ void MyFrame::bind_canvas_events_to_controller() m_canvas->set_screen_size(ClientSize.x, ClientSize.y); m_canvas->repaint(); - }); + }, m_canvas->GetId()); } void MyFrame::SLAJob::process() @@ -370,3 +376,5 @@ void MyFrame::SLAJob::process() m_print->process(); } + +//int main() {} From 4e27faa236ae3ac8eeb603e638ae3db5f6ee027f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 16:53:45 +0100 Subject: [PATCH 071/130] Only consider openvdb debug if necessary. Add REQUIRED to openvdb --- CMakeLists.txt | 3 +-- cmake/modules/FindOpenVDB.cmake | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f489e46c4b..26619e4594 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,8 +409,7 @@ if(SLIC3R_STATIC) set(USE_BLOSC TRUE) endif() - -find_package(OpenVDB 5.0 COMPONENTS openvdb) +find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb) if(OpenVDB_FOUND) slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) endif() diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index e000b05dde..84b5f41169 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -235,7 +235,9 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) if (_is_multi) list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE} ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) - if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND ${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}) + list(FIND CMAKE_CONFIGURATION_TYPES "Debug" _has_debug) + + if(OpenVDB_${COMPONENT}_LIBRARY_RELEASE AND (_has_debug LESS 0 OR OpenVDB_${COMPONENT}_LIBRARY_DEBUG)) set(OpenVDB_${COMPONENT}_FOUND TRUE) else() set(OpenVDB_${COMPONENT}_FOUND FALSE) From e25cd1ce1a2051d5ec61568120beb5fc340eafb7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 17:08:43 +0100 Subject: [PATCH 072/130] Grab the release in debug mode if there is no debug when not on msvc --- cmake/modules/FindOpenVDB.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 84b5f41169..7bc44bebb5 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -247,6 +247,10 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}}) + if (NOT MSVC AND NOT OpenVDB_${COMPONENT}_LIBRARY) + set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) + endif () + list(APPEND OpenVDB_LIB_COMPONENTS ${OpenVDB_${COMPONENT}_LIBRARY}) if(OpenVDB_${COMPONENT}_LIBRARY) From acfaff3741380f81bd67f1cffb2e307f5f1c5802 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 17 Dec 2019 18:39:01 +0100 Subject: [PATCH 073/130] Dont use glut for fps measure. --- sandboxes/opencsg/CMakeLists.txt | 3 +- sandboxes/opencsg/GLScene.cpp | 58 ++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index cf66867a56..145912431f 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -11,7 +11,7 @@ find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenCSG REQUIRED) -# find_package(GLUT REQUIRED) + find_package(GLUT REQUIRED) include(${wxWidgets_USE_FILE}) @@ -22,6 +22,7 @@ target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} OpenCSG::opencsg GLEW::GLEW + GLUT::GLUT OpenGL::GL #-lXrandr -lXext -lX11 ) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 2c3d1ffcd1..9df3b601ed 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -1,3 +1,5 @@ +#include + #include "GLScene.hpp" #include #include @@ -5,11 +7,11 @@ #include -//#ifdef __APPLE__ -//#include -//#else -//#include -//#endif +#ifdef __APPLE__ +#include +#else +#include +#endif #include @@ -56,23 +58,36 @@ Scene::Scene() = default; Scene::~Scene() = default; void renderfps () { - static std::ostringstream fpsStream; - static int fps = 0; - static int ancient = 0; - static int last = 0; - static int msec = 0; + using Clock = std::chrono::high_resolution_clock; + using Duration = Clock::duration; + using TimePoint = Clock::time_point; - last = msec; -// msec = glutGet(GLUT_ELAPSED_TIME); - if (last / 1000 != msec / 1000) { + static std::ostringstream fpsStream; + static int frames = 0; + static TimePoint last = Clock::now(); + + static const double resolution = 0.01; + static double fps = 0.; + + auto to_sec = [](Duration d) -> double { + return d.count() * double(Duration::period::num) / Duration::period::den; + }; + + ++frames; + + TimePoint msec = Clock::now(); + double seconds = to_sec(msec - last); + if (seconds >= resolution) { + last = msec; + + fps = 0.5 * (fps + frames / seconds); - float correctedFps = fps * 1000.0f / float(msec - ancient); fpsStream.str(""); - fpsStream << "fps: " << correctedFps << std::ends; + fpsStream << "fps: " << std::setprecision(4) << fps << std::ends; - ancient = msec; - fps = 0; + frames = 0; } + glDisable(GL_DEPTH_TEST); glLoadIdentity(); glMatrixMode(GL_PROJECTION); @@ -82,15 +97,14 @@ void renderfps () { glRasterPos2f(-1.0f, -1.0f); glDisable(GL_LIGHTING); std::string s = fpsStream.str(); -// for (unsigned int i=0; i Date: Wed, 18 Dec 2019 12:00:28 +0100 Subject: [PATCH 074/130] Separate fps counter and remove glut dependency --- sandboxes/opencsg/CMakeLists.txt | 3 -- sandboxes/opencsg/GLScene.cpp | 68 ++------------------------------ sandboxes/opencsg/GLScene.hpp | 64 ++++++++++++++++++++++++++++++ sandboxes/opencsg/main.cpp | 6 +++ 4 files changed, 73 insertions(+), 68 deletions(-) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index 145912431f..a6256250d5 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -11,10 +11,8 @@ find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html) find_package(OpenGL REQUIRED) find_package(GLEW REQUIRED) find_package(OpenCSG REQUIRED) - find_package(GLUT REQUIRED) include(${wxWidgets_USE_FILE}) - target_link_libraries(opencsg_example libslic3r) target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS}) target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) @@ -22,7 +20,6 @@ target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} OpenCSG::opencsg GLEW::GLEW - GLUT::GLUT OpenGL::GL #-lXrandr -lXext -lX11 ) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 9df3b601ed..5744e8be74 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -1,5 +1,3 @@ -#include - #include "GLScene.hpp" #include #include @@ -7,12 +5,6 @@ #include -#ifdef __APPLE__ -#include -#else -#include -#endif - #include #ifndef NDEBUG @@ -54,60 +46,8 @@ inline void glAssertRecentCall() { } namespace Slic3r { namespace GL { Scene::Scene() = default; - Scene::~Scene() = default; -void renderfps () { - using Clock = std::chrono::high_resolution_clock; - using Duration = Clock::duration; - using TimePoint = Clock::time_point; - - static std::ostringstream fpsStream; - static int frames = 0; - static TimePoint last = Clock::now(); - - static const double resolution = 0.01; - static double fps = 0.; - - auto to_sec = [](Duration d) -> double { - return d.count() * double(Duration::period::num) / Duration::period::den; - }; - - ++frames; - - TimePoint msec = Clock::now(); - double seconds = to_sec(msec - last); - if (seconds >= resolution) { - last = msec; - - fps = 0.5 * (fps + frames / seconds); - - fpsStream.str(""); - fpsStream << "fps: " << std::setprecision(4) << fps << std::ends; - - frames = 0; - } - - glDisable(GL_DEPTH_TEST); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glColor3f(0.0f, 0.0f, 0.0f); - glRasterPos2f(-1.0f, -1.0f); - glDisable(GL_LIGHTING); - std::string s = fpsStream.str(); - for (unsigned int i=0; iview(); render_scene(); - renderfps(); +// renderfps(); + m_fps_counter.update(); swap_buffers(); } diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 62863dc0b2..4fd524af23 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -211,6 +212,60 @@ public: void set_clip_z(double z) { m_clip_z = z; } }; +class FpsCounter { + vector> m_listeners; + + using Clock = std::chrono::high_resolution_clock; + using Duration = Clock::duration; + using TimePoint = Clock::time_point; + + int m_frames = 0; + TimePoint m_last = Clock::now(), m_window = m_last; + + double m_resolution = 0.1, m_window_size = 1.0; + double m_fps = 0.; + + static double to_sec(Duration d) + { + return d.count() * double(Duration::period::num) / Duration::period::den; + } + +public: + + void update() + { + ++m_frames; + + TimePoint msec = Clock::now(); + + double seconds_window = to_sec(msec - m_window); + m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window); + + if (to_sec(msec - m_last) >= m_resolution) { + m_last = msec; + for (auto &l : m_listeners) l(m_fps); + } + + if (seconds_window >= m_window_size) { + m_frames = 0; + m_window = msec; + } + } + + void add_listener(std::function lst) + { + m_listeners.emplace_back(lst); + } + + void clear_listeners() { m_listeners = {}; } + + void set_notification_interval(double seconds); + void set_measure_window_size(double seconds); + + double get_notification_interval() const { return m_resolution; } + double get_mesure_window_size() const { return m_window_size; } +}; + class PerspectiveCamera: public Camera { public: @@ -296,6 +351,7 @@ protected: } m_scene_cache; shptr m_camera; + FpsCounter m_fps_counter; public: @@ -323,6 +379,14 @@ public: virtual void clear_screen(); virtual void render_scene(); + + template void set_fps_counter(_FpsCounter &&fpsc) + { + m_fps_counter = std::forward<_FpsCounter>(fpsc); + } + + const FpsCounter &get_fps_counter() const { return m_fps_counter; } + FpsCounter &get_fps_counter() { return m_fps_counter; } }; class Controller : public std::enable_shared_from_this, diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 3f764fe626..28aa89f6d0 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -240,6 +240,12 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); depth_select->Disable(); + auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); + console_sizer->Add(fpstext, 0, wxALL, 5); + m_canvas->get_fps_counter().add_listener([fpstext](double fps) { + fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); + }); + controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); From fafc2a35100ca4bb63b6f153da8d7c20bac58625 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 18 Dec 2019 16:24:41 +0100 Subject: [PATCH 075/130] Recording and playback works --- sandboxes/opencsg/GLScene.cpp | 21 ++- sandboxes/opencsg/GLScene.hpp | 45 ++---- sandboxes/opencsg/main.cpp | 287 ++++++++++++++++++++++++++-------- 3 files changed, 254 insertions(+), 99 deletions(-) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/GLScene.cpp index 5744e8be74..dcd1217092 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/GLScene.cpp @@ -352,7 +352,6 @@ void Display::repaint() m_camera->view(); render_scene(); -// renderfps(); m_fps_counter.update(); swap_buffers(); @@ -508,4 +507,24 @@ bool enable_multisampling(bool e) MouseInput::Listener::~Listener() = default; +void FpsCounter::update() +{ + ++m_frames; + + TimePoint msec = Clock::now(); + + double seconds_window = to_sec(msec - m_window); + m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window); + + if (to_sec(msec - m_last) >= m_resolution) { + m_last = msec; + for (auto &l : m_listeners) l(m_fps); + } + + if (seconds_window >= m_window_size) { + m_frames = 0; + m_window = msec; + } +} + }} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/GLScene.hpp index 4fd524af23..d996528970 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/GLScene.hpp @@ -165,7 +165,6 @@ public: }; bool enable_multisampling(bool e = true); -void renderfps(); class Primitive : public OpenCSG::Primitive { @@ -190,8 +189,6 @@ public: } }; -class Scene; - class Camera { protected: Vec2f m_rot = {0., 0.}; @@ -212,6 +209,20 @@ public: void set_clip_z(double z) { m_clip_z = z; } }; +inline void reset(Camera &cam) +{ + cam.set_rotation({0., 0.}); + cam.set_zoom(0.); + cam.set_reference_point({0., 0., 0.}); + cam.set_clip_z(0.); +} + +class PerspectiveCamera: public Camera { +public: + + void set_screen(long width, long height) override; +}; + class FpsCounter { vector> m_listeners; @@ -232,25 +243,7 @@ class FpsCounter { public: - void update() - { - ++m_frames; - - TimePoint msec = Clock::now(); - - double seconds_window = to_sec(msec - m_window); - m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window); - - if (to_sec(msec - m_last) >= m_resolution) { - m_last = msec; - for (auto &l : m_listeners) l(m_fps); - } - - if (seconds_window >= m_window_size) { - m_frames = 0; - m_window = msec; - } - } + void update(); void add_listener(std::function lst) { @@ -266,12 +259,6 @@ public: double get_mesure_window_size() const { return m_window_size; } }; -class PerspectiveCamera: public Camera { -public: - - void set_screen(long width, long height) override; -}; - class CSGSettings { public: static const constexpr unsigned DEFAULT_CONVEXITY = 10; @@ -397,7 +384,7 @@ class Controller : public std::enable_shared_from_this, Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev; bool m_left_btn = false, m_right_btn = false; - shptr m_scene; + shptr m_scene; vector> m_displays; // Call a method of Camera on all the cameras of the attached displays diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 28aa89f6d0..8050ae792e 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -53,6 +53,23 @@ public: } m_context.reset(ctx); + + Bind(wxEVT_PAINT, [this](wxPaintEvent &) { + // This is required even though dc is not used otherwise. + wxPaintDC dc(this); + + // Set the OpenGL viewport according to the client size of this + // canvas. This is done here rather than in a wxSizeEvent handler + // because our OpenGL rendering context (and thus viewport + // setting) is used with multiple canvases: If we updated the + // viewport in the wxSizeEvent handler, changing the size of one + // canvas causes a viewport setting that is wrong when next + // another canvas is repainted. + const wxSize ClientSize = GetClientSize(); + + set_screen_size(ClientSize.x, ClientSize.y); + repaint(); + }); } ~Canvas() override @@ -62,6 +79,93 @@ public: } }; +enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV }; +struct Event +{ + EEvents type; + long a, b; + Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {} +}; + +class RecorderMouseInput: public MouseInput { + std::vector m_events; + bool m_recording = false, m_playing = false; + +public: + void left_click_down() override + { + if (m_recording) m_events.emplace_back(LCLK_D); + if (!m_playing) MouseInput::left_click_down(); + } + void left_click_up() override + { + if (m_recording) m_events.emplace_back(LCLK_U); + if (!m_playing) MouseInput::left_click_up(); + } + void right_click_down() override + { + if (m_recording) m_events.emplace_back(RCLK_D); + if (!m_playing) MouseInput::right_click_down(); + } + void right_click_up() override + { + if (m_recording) m_events.emplace_back(RCLK_U); + if (!m_playing) MouseInput::right_click_up(); + } + void double_click() override + { + if (m_recording) m_events.emplace_back(DDCLK); + if (!m_playing) MouseInput::double_click(); + } + void scroll(long v, long d, WheelAxis wa) override + { + if (m_recording) m_events.emplace_back(SCRL, v, d); + if (!m_playing) MouseInput::scroll(v, d, wa); + } + void move_to(long x, long y) override + { + if (m_recording) m_events.emplace_back(MV, x, y); + if (!m_playing) MouseInput::move_to(x, y); + } + + void save(std::ostream &stream) + { + for (const Event &evt : m_events) + stream << evt.type << " " << evt.a << " " << evt.b << std::endl; + } + + void load(std::istream &stream) + { + m_events.clear(); + while (stream.good()) { + int type; long a, b; + stream >> type >> a >> b; + m_events.emplace_back(EEvents(type), a, b); + } + } + + void record(bool r) { m_recording = r; if (r) m_events.clear(); } + + void play() + { + m_playing = true; + for (const Event &evt : m_events) { + switch (evt.type) { + case LCLK_U: MouseInput::left_click_up(); break; + case LCLK_D: MouseInput::left_click_down(); break; + case RCLK_U: MouseInput::right_click_up(); break; + case RCLK_D: MouseInput::right_click_down(); break; + case DDCLK: MouseInput::double_click(); break; + case SCRL: MouseInput::scroll(evt.a, evt.b, WheelAxis::waVertical); break; + case MV: MouseInput::move_to(evt.a, evt.b); break; + } + + wxSafeYield(); + } + m_playing = false; + } +}; + class MyFrame: public wxFrame { shptr m_scene; // Model @@ -69,12 +173,14 @@ class MyFrame: public wxFrame shptr m_ctl; // Controller shptr m_stbar; - uqptr m_ui_job; + + RecorderMouseInput m_mouse; class SLAJob: public Slic3r::GUI::Job { MyFrame *m_parent; std::unique_ptr m_print; std::string m_fname; + public: SLAJob(MyFrame *frame, const std::string &fname) : Slic3r::GUI::Job{frame->m_stbar} @@ -84,8 +190,9 @@ class MyFrame: public wxFrame void process() override; - protected: + const std::string & get_project_fname() const { return m_fname; } + protected: void finalize() override { m_parent->m_scene->set_print(std::move(m_print)); @@ -94,52 +201,53 @@ class MyFrame: public wxFrame } }; + uqptr m_ui_job; + public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); -private: - - void bind_canvas_events_to_controller(); - - void OnClose(wxCloseEvent& /*event*/) - { - RemoveChild(m_canvas.get()); - m_canvas.reset(); - Destroy(); + void load_model(const std::string &fname) { + m_ui_job = std::make_unique(this, fname); + m_ui_job->start(); } - void OnOpen(wxCommandEvent &/*evt*/) + void play_back_mouse(const std::string &events_fname) { - wxFileDialog dlg(this, "Select project file", - wxEmptyString, wxEmptyString, "*.3mf"); - - if (dlg.ShowModal() == wxID_OK) - { - m_ui_job = std::make_unique(this, dlg.GetPath().ToStdString()); - m_ui_job->start(); + std::fstream stream(events_fname, std::fstream::in); + + if (stream.good()) { + std::string model_name; + std::getline(stream, model_name); + load_model(model_name); + m_mouse.load(stream); + m_mouse.play(); } } - void OnShown(wxShowEvent&) - { - const wxSize ClientSize = GetClientSize(); - m_canvas->set_active(ClientSize.x, ClientSize.y); - - // Do the repaint continuously - Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - m_canvas->repaint(); - evt.RequestMore(); - }); - } + void bind_canvas_events(MouseInput &msinput); }; class App : public wxApp { MyFrame *m_frame; + public: bool OnInit() override { + + std::string fname; + std::string command; + + if (argc > 2) { + command = argv[1]; + fname = argv[2]; + } + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768)); - m_frame->Show( true ); + if (command == "play") { + m_frame->Show( true ); + m_frame->play_back_mouse(fname); + m_frame->Close( true ); + } else m_frame->Show( true ); return true; } @@ -246,6 +354,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); }); + auto record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); + console_sizer->Add(record_btn, 0, wxALL | wxEXPAND, 5); + controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); @@ -256,11 +367,26 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): sizer->Add(control_panel, 0, wxEXPAND); SetSizer(sizer); - Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); - Bind(wxEVT_CLOSE_WINDOW, &MyFrame::OnClose, this); + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &){ + RemoveChild(m_canvas.get()); + m_canvas.reset(); + Destroy(); + }); + + Bind(wxEVT_MENU, [this](wxCommandEvent &) { + wxFileDialog dlg(this, "Select project file", wxEmptyString, + wxEmptyString, "*.3mf", wxFD_OPEN|wxFD_FILE_MUST_EXIST); + + if (dlg.ShowModal() == wxID_OK) load_model(dlg.GetPath().ToStdString()); + }, wxID_OPEN); + Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT); - Bind(wxEVT_SHOW, &MyFrame::OnShown, this, GetId()); + Bind(wxEVT_SHOW, [this, ms_toggle](wxShowEvent &) { + const wxSize ClientSize = GetClientSize(); + m_canvas->set_active(ClientSize.x, ClientSize.y); + enable_multisampling(ms_toggle->GetValue()); + }); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { m_ctl->move_clip_plane(double(slider->GetValue())); @@ -312,54 +438,73 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): } }); - bind_canvas_events_to_controller(); + record_btn->Bind(wxEVT_TOGGLEBUTTON, [this, record_btn](wxCommandEvent &) { + if (!m_ui_job) { + m_stbar->set_status_text("No project loaded!"); + return; + } + + if (record_btn->GetValue()) { + if (m_canvas->camera()) reset(*m_canvas->camera()); + m_ctl->on_scene_updated(*m_scene); + m_mouse.record(true); + } else { + m_mouse.record(false); + wxFileDialog dlg(this, "Select output file", + wxEmptyString, wxEmptyString, "*.events", + wxFD_SAVE|wxFD_OVERWRITE_PROMPT); + + if (dlg.ShowModal() == wxID_OK) { + std::fstream stream(dlg.GetPath().ToStdString(), + std::fstream::out); + + if (stream.good()) { + stream << m_ui_job->get_project_fname() << "\n"; + m_mouse.save(stream); + } + } + } + }); + + // Do the repaint continuously + m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + if (m_canvas->IsShown()) m_canvas->repaint(); + evt.RequestMore(); + }); + + bind_canvas_events(m_mouse); } -void MyFrame::bind_canvas_events_to_controller() +void MyFrame::bind_canvas_events(MouseInput &ms) { - m_canvas->Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent &evt) { - m_ctl->on_scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), - evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? - Slic3r::GL::MouseInput::waVertical : - Slic3r::GL::MouseInput::waHorizontal); + m_canvas->Bind(wxEVT_MOUSEWHEEL, [&ms](wxMouseEvent &evt) { + ms.scroll(evt.GetWheelRotation(), evt.GetWheelDelta(), + evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ? + Slic3r::GL::MouseInput::waVertical : + Slic3r::GL::MouseInput::waHorizontal); }); - m_canvas->Bind(wxEVT_MOTION, [this](wxMouseEvent &evt) { - m_ctl->on_moved_to(evt.GetPosition().x, evt.GetPosition().y); + m_canvas->Bind(wxEVT_MOTION, [&ms](wxMouseEvent &evt) { + ms.move_to(evt.GetPosition().x, evt.GetPosition().y); }); - m_canvas->Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_right_click_down(); + m_canvas->Bind(wxEVT_RIGHT_DOWN, [&ms](wxMouseEvent & /*evt*/) { + ms.right_click_down(); }); - m_canvas->Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_right_click_up(); + m_canvas->Bind(wxEVT_RIGHT_UP, [&ms](wxMouseEvent & /*evt*/) { + ms.right_click_up(); }); - m_canvas->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_left_click_down(); + m_canvas->Bind(wxEVT_LEFT_DOWN, [&ms](wxMouseEvent & /*evt*/) { + ms.left_click_down(); }); - m_canvas->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent & /*evt*/) { - m_ctl->on_left_click_up(); + m_canvas->Bind(wxEVT_LEFT_UP, [&ms](wxMouseEvent & /*evt*/) { + ms.left_click_up(); }); - - m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { - // This is required even though dc is not used otherwise. - wxPaintDC dc(m_canvas.get()); - - // Set the OpenGL viewport according to the client size of this - // canvas. This is done here rather than in a wxSizeEvent handler - // because our OpenGL rendering context (and thus viewport setting) is - // used with multiple canvases: If we updated the viewport in the - // wxSizeEvent handler, changing the size of one canvas causes a - // viewport setting that is wrong when next another canvas is - // repainted. - const wxSize ClientSize = m_canvas->GetClientSize(); - - m_canvas->set_screen_size(ClientSize.x, ClientSize.y); - m_canvas->repaint(); - }, m_canvas->GetId()); + + ms.add_listener(m_ctl); } void MyFrame::SLAJob::process() @@ -380,7 +525,11 @@ void MyFrame::SLAJob::process() update_status(status.percent, status.text); }); - m_print->process(); + try { + m_print->process(); + } catch(std::exception &e) { + update_status(0, wxString("Exception during processing: ") + e.what()); + } } //int main() {} From bf44da0e37a2ffafa8a2a40fac79e9820b6cdd18 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 Dec 2019 01:55:46 +0100 Subject: [PATCH 076/130] Make it work with mesa sw renderer --- sandboxes/opencsg/main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 8050ae792e..c7be068559 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -2,6 +2,8 @@ #include #include +#include "GLScene.hpp" + #include #include @@ -18,8 +20,6 @@ #include #include -#include "GLScene.hpp" - #include "libslic3r/Model.hpp" #include "libslic3r/Format/3mf.hpp" #include "libslic3r/SLAPrint.hpp" @@ -68,7 +68,7 @@ public: const wxSize ClientSize = GetClientSize(); set_screen_size(ClientSize.x, ClientSize.y); - repaint(); +// repaint(); }); } @@ -280,7 +280,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): // glReadPixels would not return the alpha channel on NVIDIA if // not requested when the GL context is created. WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, - WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; + /*WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4,*/ 0}; m_scene = std::make_shared(); m_ctl = std::make_shared(); @@ -509,7 +509,7 @@ void MyFrame::bind_canvas_events(MouseInput &ms) void MyFrame::SLAJob::process() { - using Status = Slic3r::PrintBase::SlicingStatus; + using SlStatus = Slic3r::PrintBase::SlicingStatus; Slic3r::DynamicPrintConfig cfg; auto model = Slic3r::Model::read_from_file(m_fname, &cfg); @@ -521,7 +521,7 @@ void MyFrame::SLAJob::process() params.to_object_step = Slic3r::slaposHollowing; m_print->set_task(params); - m_print->set_status_callback([this](const Status &status) { + m_print->set_status_callback([this](const SlStatus &status) { update_status(status.percent, status.text); }); From 8126cdd50762c43b632c243e8a875c0c00c66aff Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 Dec 2019 14:51:38 +0100 Subject: [PATCH 077/130] Add command line options. Rename GLScene to Engine --- sandboxes/opencsg/CMakeLists.txt | 2 +- sandboxes/opencsg/{GLScene.cpp => Engine.cpp} | 20 +- sandboxes/opencsg/{GLScene.hpp => Engine.hpp} | 55 ++--- sandboxes/opencsg/main.cpp | 195 ++++++++++++------ 4 files changed, 175 insertions(+), 97 deletions(-) rename sandboxes/opencsg/{GLScene.cpp => Engine.cpp} (97%) rename sandboxes/opencsg/{GLScene.hpp => Engine.hpp} (98%) diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index a6256250d5..600ef7884a 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) project(OpenCSG-example) -add_executable(opencsg_example WIN32 main.cpp GLScene.hpp GLScene.cpp +add_executable(opencsg_example WIN32 main.cpp Engine.hpp Engine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) diff --git a/sandboxes/opencsg/GLScene.cpp b/sandboxes/opencsg/Engine.cpp similarity index 97% rename from sandboxes/opencsg/GLScene.cpp rename to sandboxes/opencsg/Engine.cpp index dcd1217092..590faa2967 100644 --- a/sandboxes/opencsg/GLScene.cpp +++ b/sandboxes/opencsg/Engine.cpp @@ -1,4 +1,4 @@ -#include "GLScene.hpp" +#include "Engine.hpp" #include #include #include @@ -48,7 +48,7 @@ namespace Slic3r { namespace GL { Scene::Scene() = default; Scene::~Scene() = default; -void Display::render_scene() +void CSGDisplay::render_scene() { GLfloat color[] = {1.f, 1.f, 0.f, 0.f}; glsafe(::glColor4fv(color)); @@ -95,14 +95,14 @@ BoundingBoxf3 Scene::get_bounding_box() const return m_print->model().bounding_box(); } -void Display::SceneCache::clear() +void CSGDisplay::SceneCache::clear() { primitives_csg.clear(); primitives_free.clear(); primitives.clear(); } -shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) +shptr CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh) { auto p = std::make_shared(); p->load_mesh(mesh); @@ -111,9 +111,9 @@ shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh) return p; } -shptr Display::SceneCache::add_mesh(const TriangleMesh &mesh, - OpenCSG::Operation o, - unsigned c) +shptr CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation o, + unsigned c) { auto p = std::make_shared(o, c); p->load_mesh(mesh); @@ -388,7 +388,7 @@ void Controller::on_moved_to(long x, long y) m_mouse_pos = {x, y}; } -void Display::apply_csgsettings(const CSGSettings &settings) +void CSGDisplay::apply_csgsettings(const CSGSettings &settings) { using namespace OpenCSG; @@ -404,11 +404,9 @@ void Display::apply_csgsettings(const CSGSettings &settings) if (p->getConvexity() > 1) p->setConvexity(m_csgsettings.get_convexity()); } - - repaint(); } -void Display::on_scene_updated(const Scene &scene) +void CSGDisplay::on_scene_updated(const Scene &scene) { const SLAPrint *print = scene.get_print(); if (!print) return; diff --git a/sandboxes/opencsg/GLScene.hpp b/sandboxes/opencsg/Engine.hpp similarity index 98% rename from sandboxes/opencsg/GLScene.hpp rename to sandboxes/opencsg/Engine.hpp index d996528970..5e70305236 100644 --- a/sandboxes/opencsg/GLScene.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -1,5 +1,5 @@ -#ifndef GLSCENE_HPP -#define GLSCENE_HPP +#ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP +#define SLIC3R_OCSG_EXMP_ENGINE_HPP_HPP #include #include @@ -322,21 +322,6 @@ protected: Vec2i m_size; bool m_initialized = false; - CSGSettings m_csgsettings; - - struct SceneCache { - vector> primitives; - vector primitives_free; - vector primitives_csg; - - void clear(); - - shptr add_mesh(const TriangleMesh &mesh); - shptr add_mesh(const TriangleMesh &mesh, - OpenCSG::Operation op, - unsigned covexity); - } m_scene_cache; - shptr m_camera; FpsCounter m_fps_counter; @@ -359,13 +344,8 @@ public: bool is_initialized() const { return m_initialized; } - const CSGSettings & get_csgsettings() const { return m_csgsettings; } - void apply_csgsettings(const CSGSettings &settings); - - void on_scene_updated(const Scene &scene) override; - virtual void clear_screen(); - virtual void render_scene(); + virtual void render_scene() {} template void set_fps_counter(_FpsCounter &&fpsc) { @@ -376,6 +356,33 @@ public: FpsCounter &get_fps_counter() { return m_fps_counter; } }; +class CSGDisplay : public Display { +protected: + CSGSettings m_csgsettings; + + struct SceneCache { + vector> primitives; + vector primitives_free; + vector primitives_csg; + + void clear(); + + shptr add_mesh(const TriangleMesh &mesh); + shptr add_mesh(const TriangleMesh &mesh, + OpenCSG::Operation op, + unsigned covexity); + } m_scene_cache; + +public: + + const CSGSettings & get_csgsettings() const { return m_csgsettings; } + void apply_csgsettings(const CSGSettings &settings); + + void render_scene() override; + + void on_scene_updated(const Scene &scene) override; +}; + class Controller : public std::enable_shared_from_this, public MouseInput::Listener, public Scene::Listener @@ -425,4 +432,4 @@ public: }; }} // namespace Slic3r::GL -#endif // GLSCENE_HPP +#endif // SLIC3R_OCSG_EXMP_ENGINE_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index c7be068559..1352e1ba0a 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -2,7 +2,7 @@ #include #include -#include "GLScene.hpp" +#include "Engine.hpp" #include @@ -19,6 +19,7 @@ #include #include #include +#include #include "libslic3r/Model.hpp" #include "libslic3r/Format/3mf.hpp" @@ -29,31 +30,47 @@ using namespace Slic3r::GL; -class Canvas: public wxGLCanvas, public Slic3r::GL::Display +class Canvas: public wxGLCanvas { - shptr m_context; + class OCSGRenderer: public Slic3r::GL::CSGDisplay { + Canvas *m_canvas; + shptr m_context; + public: + + OCSGRenderer(Canvas *c): m_canvas{c} { + auto ctx = new wxGLContext(m_canvas); + if (!ctx || !ctx->IsOK()) { + wxMessageBox("Could not create OpenGL context.", "Error", + wxOK | wxICON_ERROR); + return; + } + + m_context.reset(ctx); + } + + void set_active(long w, long h) override + { + m_canvas->SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + wxGLContext * context() { return m_context.get(); } + const wxGLContext * context() const { return m_context.get(); } + + void swap_buffers() override { m_canvas->SwapBuffers(); } + + ~OCSGRenderer() override { m_scene_cache.clear(); } + }; + + shptr m_ocsgdisplay = std::make_shared(this); + + shptr m_display = m_ocsgdisplay; + public: - void set_active(long w, long h) override - { - SetCurrent(*m_context); - Slic3r::GL::Display::set_active(w, h); - } - - void swap_buffers() override { SwapBuffers(); } - template Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) { - auto ctx = new wxGLContext(this); - if (!ctx || !ctx->IsOK()) { - wxMessageBox("Could not create OpenGL context.", "Error", - wxOK | wxICON_ERROR); - return; - } - - m_context.reset(ctx); - Bind(wxEVT_PAINT, [this](wxPaintEvent &) { // This is required even though dc is not used otherwise. wxPaintDC dc(this); @@ -67,16 +84,14 @@ public: // another canvas is repainted. const wxSize ClientSize = GetClientSize(); - set_screen_size(ClientSize.x, ClientSize.y); -// repaint(); + m_display->set_screen_size(ClientSize.x, ClientSize.y); }); } - - ~Canvas() override - { - m_scene_cache.clear(); - m_context.reset(); - } + + shptr get_display() const { return m_display; } + void set_display(shptr d) { m_display = d; } + + shptr get_ocsg_display() const { return m_ocsgdisplay; } }; enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV }; @@ -204,7 +219,7 @@ class MyFrame: public wxFrame uqptr m_ui_job; public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const Slic3r::GL::CSGSettings &settings); void load_model(const std::string &fname) { m_ui_job = std::make_unique(this, fname); @@ -224,29 +239,76 @@ public: } } + Canvas * canvas() { return m_canvas.get(); } + const Canvas * canvas() const { return m_canvas.get(); } + void bind_canvas_events(MouseInput &msinput); }; +static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS"}; +static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; +static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; + class App : public wxApp { - MyFrame *m_frame; + MyFrame *m_frame = nullptr; public: bool OnInit() override { - std::string fname; - std::string command; + wxCmdLineParser parser(argc, argv); - if (argc > 2) { - command = argv[1]; - fname = argv[2]; - } - - m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768)); + parser.AddOption("p", "play", "play back file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("a", "algorithm", "OpenCSG algorithm [Auto|Goldfeather|SCS]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("d", "depth", "OpenCSG depth strategy [Off|OcclusionQuery|On]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("o", "optimization", "OpenCSG optimization strategy [Default|ForceOn|On|Off]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddOption("c", "convexity", "OpenCSG convexity parameter for generic meshes", wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL); + parser.AddSwitch("", "disable-csg", "Disable csg rendering", wxCMD_LINE_PARAM_OPTIONAL); - if (command == "play") { + parser.Parse(); + + wxString fname; + bool is_play = parser.Found("play", &fname); + + wxString alg; + parser.Found("algorithm", &alg); + + wxString depth; + parser.Found("depth", &depth); + + wxString opt; + parser.Found("optimization", &opt); + + long convexity = 1; + parser.Found("convexity", &convexity); + + bool csg_off = parser.Found("disable-csg"); + + auto get_idx = [](const wxString &a, const std::vector &v) { + auto it = std::find(v.begin(), v.end(), a.ToStdString()); + return it - v.begin(); + }; + + Slic3r::GL::CSGSettings settings; + + if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) + settings.set_algo(OpenCSG::Algorithm(a)); + + if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); + + if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) + settings.set_optimization(OpenCSG::Optimization(a)); + + settings.set_convexity(unsigned(convexity)); + settings.enable_csg(!csg_off); + + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), settings); + + if (is_play) { m_frame->Show( true ); - m_frame->play_back_mouse(fname); + m_frame->play_back_mouse(fname.ToStdString()); m_frame->Close( true ); + } else m_frame->Show( true ); return true; @@ -255,7 +317,8 @@ public: wxIMPLEMENT_APP(App); -MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): +MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, + const Slic3r::GL::CSGSettings &settings): wxFrame(nullptr, wxID_ANY, title, pos, size) { wxMenu *menuFile = new wxMenu; @@ -289,7 +352,10 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): m_canvas = std::make_shared(this, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - m_ctl->add_display(m_canvas); + + m_canvas->get_ocsg_display()->apply_csgsettings(settings); + + m_ctl->add_display(m_canvas->get_display()); wxPanel *control_panel = new wxPanel(this); @@ -310,7 +376,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); auto add_combobox = [control_panel, console_sizer] - (const wxString &label, std::vector &&list) + (const wxString &label, const std::vector &list) { auto widget = new wxComboBox(control_panel, wxID_ANY, list[0], wxDefaultPosition, wxDefaultSize, @@ -343,14 +409,14 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); - auto alg_select = add_combobox("Algorithm", {"Auto", "Goldfeather", "SCS"}); - auto depth_select = add_combobox("Depth Complexity", {"Off", "OcclusionQuery", "On"}); - auto optimization_select = add_combobox("Optimization", { "Default", "ForceOn", "On", "Off" }); + auto alg_select = add_combobox("Algorithm", CSG_ALGS); + auto depth_select = add_combobox("Depth Complexity", CSG_DEPTH); + auto optimization_select = add_combobox("Optimization", CSG_OPT); depth_select->Disable(); auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); console_sizer->Add(fpstext, 0, wxALL, 5); - m_canvas->get_fps_counter().add_listener([fpstext](double fps) { + m_canvas->get_ocsg_display()->get_fps_counter().add_listener([fpstext](double fps) { fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); }); @@ -367,6 +433,13 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): sizer->Add(control_panel, 0, wxEXPAND); SetSizer(sizer); + if (settings.get_algo() > 0) depth_select->Enable(true); + alg_select->SetSelection(settings.get_algo()); + depth_select->SetSelection(settings.get_depth_algo()); + optimization_select->SetSelection(settings.get_optimization()); + convexity_spin->SetValue(int(settings.get_convexity())); + csg_toggle->SetValue(settings.is_enabled()); + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &){ RemoveChild(m_canvas.get()); m_canvas.reset(); @@ -384,7 +457,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): Bind(wxEVT_SHOW, [this, ms_toggle](wxShowEvent &) { const wxSize ClientSize = GetClientSize(); - m_canvas->set_active(ClientSize.x, ClientSize.y); + m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); enable_multisampling(ms_toggle->GetValue()); }); @@ -394,13 +467,13 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ enable_multisampling(ms_toggle->GetValue()); - m_canvas->repaint(); + m_canvas->get_display()->repaint(); }); csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.enable_csg(csg_toggle->GetValue()); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); alg_select->Bind(wxEVT_COMBOBOX, @@ -408,33 +481,33 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): { int sel = alg_select->GetSelection(); depth_select->Enable(sel > 0); - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); optimization_select->Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); }); convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_csgsettings(); + CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); int c = convexity_spin->GetValue(); if (c > 0) { settings.set_convexity(unsigned(c)); - m_canvas->apply_csgsettings(settings); + m_canvas->get_ocsg_display()->apply_csgsettings(settings); } }); @@ -445,7 +518,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): } if (record_btn->GetValue()) { - if (m_canvas->camera()) reset(*m_canvas->camera()); + if (auto c = m_canvas->get_display()->camera()) reset(*c); m_ctl->on_scene_updated(*m_scene); m_mouse.record(true); } else { @@ -468,7 +541,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size): // Do the repaint continuously m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - if (m_canvas->IsShown()) m_canvas->repaint(); + if (m_canvas->IsShown()) m_canvas->get_display()->repaint(); evt.RequestMore(); }); From bb8a6b898f9a3f1becc3e6b9e841dfd6f0653a01 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 Dec 2019 15:26:04 +0100 Subject: [PATCH 078/130] Save and load window size. Add fps average output. --- sandboxes/opencsg/main.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 1352e1ba0a..95e00b3664 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -218,6 +218,8 @@ class MyFrame: public wxFrame uqptr m_ui_job; + double m_fps_avg = 0.; + public: MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const Slic3r::GL::CSGSettings &settings); @@ -234,6 +236,11 @@ public: std::string model_name; std::getline(stream, model_name); load_model(model_name); + + int w, h; + stream >> w >> h; + SetSize(w, h); + m_mouse.load(stream); m_mouse.play(); } @@ -243,6 +250,8 @@ public: const Canvas * canvas() const { return m_canvas.get(); } void bind_canvas_events(MouseInput &msinput); + + double get_fps_average() const { return m_fps_avg; } }; static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS"}; @@ -308,6 +317,7 @@ public: m_frame->Show( true ); m_frame->play_back_mouse(fname.ToStdString()); m_frame->Close( true ); + std::cout << m_frame->get_fps_average() << std::endl; } else m_frame->Show( true ); @@ -416,8 +426,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); console_sizer->Add(fpstext, 0, wxALL, 5); - m_canvas->get_ocsg_display()->get_fps_counter().add_listener([fpstext](double fps) { - fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); + m_canvas->get_ocsg_display()->get_fps_counter().add_listener([this, fpstext](double fps) { + fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); + m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; }); auto record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); @@ -533,6 +544,8 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, if (stream.good()) { stream << m_ui_job->get_project_fname() << "\n"; + wxSize winsize = GetSize(); + stream << winsize.x << " " << winsize.y << "\n"; m_mouse.save(stream); } } From d3925abb13aa6e43257eafa0638d94e39b41e3a5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 19 Dec 2019 16:10:34 +0100 Subject: [PATCH 079/130] Add some comments. --- sandboxes/opencsg/Engine.hpp | 54 ++++++++++++++++++++++++++------ sandboxes/opencsg/main.cpp | 60 +++++++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp index 5e70305236..2b58e9b75c 100644 --- a/sandboxes/opencsg/Engine.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -17,33 +17,36 @@ class SLAPrint; namespace GL { +// Simple shorthands for smart pointers template using shptr = std::shared_ptr; template using uqptr = std::unique_ptr; template using wkptr = std::weak_ptr; -template> -using vector = std::vector; +template> using vector = std::vector; +// remove empty weak pointers from a vector template void cleanup(vector> &listeners) { auto it = std::remove_if(listeners.begin(), listeners.end(), [](auto &l) { return !l.lock(); }); listeners.erase(it, listeners.end()); } +// Call a class method on each element of a vector of objects (weak pointers) +// of the same type. template void call(F &&f, vector> &listeners, Args&&... args) { for (auto &l : listeners) if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); } +// A representation of a mouse input for the engine. class MouseInput { public: + enum WheelAxis { waVertical, waHorizontal }; - enum WheelAxis { - waVertical, waHorizontal - }; - + // Interface to implement if an object wants to receive notifications + // about mouse events. class Listener { public: virtual ~Listener(); @@ -99,6 +102,7 @@ public: } }; +// This is a stripped down version of Slic3r::IndexedVertexArray class IndexedVertexArray { public: ~IndexedVertexArray() { release_geometry(); } @@ -164,8 +168,11 @@ public: void shrink_to_fit(); }; +// Try to enable or disable multisampling. bool enable_multisampling(bool e = true); +// A primitive that can be used with OpenCSG rendering algorithms. +// Does a similar job to GLVolume. class Primitive : public OpenCSG::Primitive { IndexedVertexArray m_geom; @@ -176,19 +183,21 @@ public: Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} - void render(); + void render() override; void translation(const Vec3d &offset) { m_trafo.set_offset(offset); } void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); } void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); } void scale(double s) { scale({s, s, s}); } - inline void load_mesh(const TriangleMesh &mesh) { + inline void load_mesh(const TriangleMesh &mesh) + { m_geom.load_mesh(mesh); m_geom.finalize_geometry(); } }; +// A simple representation of a camera in a 3D scene class Camera { protected: Vec2f m_rot = {0., 0.}; @@ -209,6 +218,7 @@ public: void set_clip_z(double z) { m_clip_z = z; } }; +// Reset a camera object inline void reset(Camera &cam) { cam.set_rotation({0., 0.}); @@ -217,12 +227,15 @@ inline void reset(Camera &cam) cam.set_clip_z(0.); } +// Specialization of a camera which shows in perspective projection class PerspectiveCamera: public Camera { public: void set_screen(long width, long height) override; }; +// A simple counter of FPS. Subscribed objects will receive updates of the +// current fps. class FpsCounter { vector> m_listeners; @@ -259,6 +272,7 @@ public: double get_mesure_window_size() const { return m_window_size; } }; +// Collection of the used OpenCSG library settings. class CSGSettings { public: static const constexpr unsigned DEFAULT_CONVEXITY = 10; @@ -286,12 +300,19 @@ public: unsigned get_convexity() const { return m_convexity; } void set_convexity(unsigned c) { m_convexity = c; } }; - + +// The scene is a wrapper around SLAPrint which holds the data to be visualized. class Scene { uqptr m_print; public: + // Subscribers will be notified if the model is changed. This might be a + // display which will have to load the meshes and repaint itself when + // the scene data changes. + // eg. We load a new 3mf through the UI, this will notify the controller + // associated with the scene and all the displays that the controller is + // connected with. class Listener { public: virtual ~Listener() = default; @@ -316,6 +337,11 @@ private: vector> m_listeners; }; +// The basic Display. This is almost just an interface but will do all the +// initialization and show the fps values. Overriding the render_scene is +// needed to show the scene content. The specific method of displaying the +// scene is up the the particular implementation (OpenCSG or other screen space +// boolean algorithms) class Display : public Scene::Listener { protected: @@ -356,10 +382,13 @@ public: FpsCounter &get_fps_counter() { return m_fps_counter; } }; +// Special dispaly using OpenCSG for rendering the scene. class CSGDisplay : public Display { protected: CSGSettings m_csgsettings; + // Cache the renderable primitives. These will be fetched when the scene + // is modified. struct SceneCache { vector> primitives; vector primitives_free; @@ -375,6 +404,7 @@ protected: public: + // Receive or apply the new settings. const CSGSettings & get_csgsettings() const { return m_csgsettings; } void apply_csgsettings(const CSGSettings &settings); @@ -383,6 +413,11 @@ public: void on_scene_updated(const Scene &scene) override; }; + +// The controller is a hub which dispatches mouse events to the connected +// displays. It keeps track of the mouse wheel position, the states whether +// the mouse is being held, dragged, etc... All the connected displays will +// mirror the camera movement (if there is more than one display). class Controller : public std::enable_shared_from_this, public MouseInput::Listener, public Scene::Listener @@ -404,6 +439,7 @@ class Controller : public std::enable_shared_from_this, public: + // Set the scene that will be controlled. void set_scene(shptr scene) { m_scene = scene; diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 95e00b3664..b609657c01 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -30,8 +30,11 @@ using namespace Slic3r::GL; +// The opengl rendering facility. Here we implement the rendering objects. class Canvas: public wxGLCanvas { + + // Tell the CSGDisplay how to swap buffers and set the gl context. class OCSGRenderer: public Slic3r::GL::CSGDisplay { Canvas *m_canvas; shptr m_context; @@ -62,8 +65,10 @@ class Canvas: public wxGLCanvas ~OCSGRenderer() override { m_scene_cache.clear(); } }; + // Create the OCSGDisplay for rendering with OpenCSG algorithms shptr m_ocsgdisplay = std::make_shared(this); + // One display is active at a time, the OCSGRenderer by default. shptr m_display = m_ocsgdisplay; public: @@ -94,6 +99,7 @@ public: shptr get_ocsg_display() const { return m_ocsgdisplay; } }; +// Enumerate possible mouse events, we will record them. enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV }; struct Event { @@ -102,6 +108,8 @@ struct Event Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {} }; +// Create a special mouse input adapter, which can store (record) the received +// mouse signals into a file and play back the stored events later. class RecorderMouseInput: public MouseInput { std::vector m_events; bool m_recording = false, m_playing = false; @@ -181,16 +189,20 @@ public: } }; +// The top level frame of the application. class MyFrame: public wxFrame { + // Instantiate the 3D engine. shptr m_scene; // Model shptr m_canvas; // View shptr m_ctl; // Controller - + + // Add a status bar with progress indication. shptr m_stbar; RecorderMouseInput m_mouse; + // When loading a Model from 3mf and preparing it, we use a separate thread. class SLAJob: public Slic3r::GUI::Job { MyFrame *m_parent; std::unique_ptr m_print; @@ -202,12 +214,15 @@ class MyFrame: public wxFrame , m_parent{frame} , m_fname{fname} {} - + + // Runs in separate thread void process() override; const std::string & get_project_fname() const { return m_fname; } protected: + + // Runs in the UI thread. void finalize() override { m_parent->m_scene->set_print(std::move(m_print)); @@ -218,16 +233,22 @@ class MyFrame: public wxFrame uqptr m_ui_job; + // To keep track of the running average of measured fps values. double m_fps_avg = 0.; public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size, const Slic3r::GL::CSGSettings &settings); + MyFrame(const wxString & title, + const wxPoint & pos, + const wxSize & size, + const Slic3r::GL::CSGSettings &settings); + // Grab a 3mf and load (hollow it out) within the UI job. void load_model(const std::string &fname) { m_ui_job = std::make_unique(this, fname); m_ui_job->start(); } + // Load a previously stored mouse event log and play it back. void play_back_mouse(const std::string &events_fname) { std::fstream stream(events_fname, std::fstream::in); @@ -249,11 +270,14 @@ public: Canvas * canvas() { return m_canvas.get(); } const Canvas * canvas() const { return m_canvas.get(); } + // Bind the canvas mouse events to a class implementing MouseInput interface void bind_canvas_events(MouseInput &msinput); double get_fps_average() const { return m_fps_avg; } }; +// Possible OpenCSG configuration values. Will be used on the command line and +// on the UI widgets. static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS"}; static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; @@ -482,9 +506,9 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, }); csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.enable_csg(csg_toggle->GetValue()); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.enable_csg(csg_toggle->GetValue()); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); alg_select->Bind(wxEVT_COMBOBOX, @@ -492,33 +516,33 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, { int sel = alg_select->GetSelection(); depth_select->Enable(sel > 0); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_algo(OpenCSG::Algorithm(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { int sel = depth_select->GetSelection(); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); optimization_select->Bind(wxEVT_COMBOBOX, [this, optimization_select](wxCommandEvent &) { int sel = optimization_select->GetSelection(); - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); - settings.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); + stt.set_optimization(OpenCSG::Optimization(sel)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); }); convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings settings = m_canvas->get_ocsg_display()->get_csgsettings(); + CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); int c = convexity_spin->GetValue(); if (c > 0) { - settings.set_convexity(unsigned(c)); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + stt.set_convexity(unsigned(c)); + m_canvas->get_ocsg_display()->apply_csgsettings(stt); } }); From 4f97a7122f160986d0675d09586310f64671291e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 20 Dec 2019 01:21:13 +0100 Subject: [PATCH 080/130] Fix closing while playback --- sandboxes/opencsg/main.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index b609657c01..ff7e36b8be 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -183,10 +183,15 @@ public: case MV: MouseInput::move_to(evt.a, evt.b); break; } - wxSafeYield(); + wxYield(); + if (!m_playing) + break; } m_playing = false; } + + void stop() { m_playing = false; } + bool is_playing() const { return m_playing; } }; // The top level frame of the application. @@ -236,6 +241,8 @@ class MyFrame: public wxFrame // To keep track of the running average of measured fps values. double m_fps_avg = 0.; + wxToggleButton *m_record_btn; + public: MyFrame(const wxString & title, const wxPoint & pos, @@ -263,6 +270,7 @@ public: SetSize(w, h); m_mouse.load(stream); + if (m_record_btn) m_record_btn->Disable(); m_mouse.play(); } } @@ -284,7 +292,6 @@ static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Of class App : public wxApp { MyFrame *m_frame = nullptr; - public: bool OnInit() override { @@ -340,9 +347,11 @@ public: if (is_play) { m_frame->Show( true ); m_frame->play_back_mouse(fname.ToStdString()); - m_frame->Close( true ); + std::cout << m_frame->get_fps_average() << std::endl; + m_frame->Destroy(); + } else m_frame->Show( true ); return true; @@ -455,8 +464,8 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; }); - auto record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); - console_sizer->Add(record_btn, 0, wxALL | wxEXPAND, 5); + m_record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); + console_sizer->Add(m_record_btn, 0, wxALL | wxEXPAND, 5); controlsizer->Add(slider_sizer, 0, wxEXPAND); controlsizer->Add(console_sizer, 1, wxEXPAND); @@ -475,10 +484,11 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, convexity_spin->SetValue(int(settings.get_convexity())); csg_toggle->SetValue(settings.is_enabled()); - Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &){ - RemoveChild(m_canvas.get()); + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt){ + if (m_canvas) RemoveChild(m_canvas.get()); m_canvas.reset(); - Destroy(); + if (!m_mouse.is_playing()) evt.Skip(); + else m_mouse.stop(); }); Bind(wxEVT_MENU, [this](wxCommandEvent &) { @@ -546,13 +556,13 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, } }); - record_btn->Bind(wxEVT_TOGGLEBUTTON, [this, record_btn](wxCommandEvent &) { + m_record_btn->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &) { if (!m_ui_job) { m_stbar->set_status_text("No project loaded!"); return; } - if (record_btn->GetValue()) { + if (m_record_btn->GetValue()) { if (auto c = m_canvas->get_display()->camera()) reset(*c); m_ctl->on_scene_updated(*m_scene); m_mouse.record(true); From d1f86e0a802eb5bea511ec06c2b7852093781fdc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 20 Dec 2019 10:17:59 +0100 Subject: [PATCH 081/130] SLA supports gizmo now uses hollowed mesh when available Bugfix: SLA support tree is is now given the hollowed mesh, not the original Added ImGui separators and change background alpha in hollowing gizmo dialog (to match the other gizmos) --- src/libslic3r/SLAPrintSteps.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 35 ++++++--- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 77 ++++++++++++-------- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index f16aa7cfa6..e0c92a71c5 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -225,8 +225,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) { - po.m_supportdata.reset( - new SLAPrintObject::SupportData(po.transformed_mesh()) ); + po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f8f2e125fc..75a21057af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -112,10 +112,21 @@ void GLGizmoHollow::on_render() const glsafe(::glEnable(GL_DEPTH_TEST)); if (m_c->m_volume_with_cavity) { + m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); m_parent.get_shader().start_using(); - m_c->m_volume_with_cavity->render(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; + glcheck(); + m_c->m_volume_with_cavity->set_render_color(); + m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } + // Show/hide the original object + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); @@ -602,22 +613,22 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) hole_mesh.transform(m.cast()); hole_mesh.translate(hole.pos); holes_mesh.merge(hole_mesh); - //MeshBoolean::minus(*m_c->m_cavity_mesh.get(), hole_mesh); + holes_mesh.repair(); } MeshBoolean::minus(*m_c->m_cavity_mesh.get(), holes_mesh); } - // create a new GLVolume that only has the cavity inside Geometry::Transformation volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); - m_c->m_volume_with_cavity.reset(new GLVolume(1.f, 0.f, 0.f, 0.5f)); + m_c->m_volume_with_cavity.reset(new GLVolume(GLVolume::MODEL_COLOR[2])); m_c->m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_c->m_cavity_mesh.get()); m_c->m_volume_with_cavity->finalize_geometry(true); m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); + m_c->m_volume_with_cavity->force_transparent = false; } - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + if (m_clipping_plane_distance == 0.f) { m_clipping_plane_distance = 0.5f; update_clipping_plane(); @@ -672,7 +683,7 @@ RENDER_AGAIN: const float approx_height = m_imgui->scaled(20.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->set_next_window_bg_alpha(0.5f); + m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: @@ -756,7 +767,8 @@ RENDER_AGAIN: bool remove_selected = false; bool remove_all = false; - m_imgui->text(" "); // vertical gap + // m_imgui->text(" "); // vertical gap + ImGui::Separator(); float diameter_upper_cap = 20.f; //static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; if (m_new_hole_radius > diameter_upper_cap) @@ -824,7 +836,8 @@ RENDER_AGAIN: m_imgui->disabled_end(); // Following is rendered in both editing and non-editing mode: - m_imgui->text(""); + // m_imgui->text(""); + ImGui::Separator(); if (m_clipping_plane_distance == 0.f) m_imgui->text(m_desc.at("clipping_of_view")); else { @@ -940,9 +953,9 @@ void GLGizmoHollow::on_set_state() // Release clippers and the AABB raycaster. m_c->m_object_clipper.reset(); m_c->m_supports_clipper.reset(); - m_c->m_mesh_raycaster.reset(); - m_c->m_cavity_mesh.reset(); - m_c->m_volume_with_cavity.reset(); + //m_c->m_mesh_raycaster.reset(); + //m_c->m_cavity_mesh.reset(); + //m_c->m_volume_with_cavity.reset(); } m_old_state = m_state; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 85d57a211e..ddde79c521 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -121,6 +121,23 @@ void GLGizmoSlaSupports::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); + if (m_c->m_volume_with_cavity) { + m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); + m_parent.get_shader().start_using(); + + GLint current_program_id; + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id)); + GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; + GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; + glcheck(); + m_c->m_volume_with_cavity->set_render_color(); + m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); + m_parent.get_shader().stop_using(); + } + // Show/hide the original object + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); if (m_quadric != nullptr && selection.is_from_single_instance()) @@ -328,41 +345,41 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: - render_color[0] = 0.7f; - render_color[1] = 0.7f; - render_color[2] = 0.7f; - render_color[3] = 0.7f; - glsafe(::glColor4fv(render_color)); - for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { - // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. - glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); - glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); +// render_color[0] = 0.7f; +// render_color[1] = 0.7f; +// render_color[2] = 0.7f; +// render_color[3] = 0.7f; +// glsafe(::glColor4fv(render_color)); +// for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { +// // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. +// glsafe(::glPushMatrix()); +// glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); +// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); - if (vol->is_left_handed()) - glFrontFace(GL_CW); +// if (vol->is_left_handed()) +// glFrontFace(GL_CW); - // Matrices set, we can render the point mark now. +// // Matrices set, we can render the point mark now. - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); - Eigen::AngleAxisd aa(q); - glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.height)); - ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.height)); - glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); - glsafe(::glPopMatrix()); +// Eigen::Quaterniond q; +// q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); +// Eigen::AngleAxisd aa(q); +// glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); +// glsafe(::glPushMatrix()); +// glsafe(::glTranslated(0., 0., -drain_hole.height)); +// ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); +// glsafe(::glTranslated(0., 0., drain_hole.height)); +// ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); +// glsafe(::glTranslated(0., 0., -drain_hole.height)); +// glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); +// ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); +// glsafe(::glPopMatrix()); - if (vol->is_left_handed()) - glFrontFace(GL_CCW); - glsafe(::glPopMatrix()); +// if (vol->is_left_handed()) +// glFrontFace(GL_CCW); +// glsafe(::glPopMatrix()); - } +// } if (!picking) glsafe(::glDisable(GL_LIGHTING)); From 4d182a57480ed680d2b5fbfa77539c84da60d8ad Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 20 Dec 2019 11:33:14 +0100 Subject: [PATCH 082/130] Fixed scaling of the holes and instance showing/hiding --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 13 ++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 75a21057af..f0b71f2608 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -83,7 +83,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Select if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -607,10 +607,18 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) TriangleMesh holes_mesh; for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/8); + + // Rotate the cylinder appropriately Eigen::Quaternionf q; Transform3f m = Transform3f::Identity(); m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3f::UnitZ(), hole.normal).toRotationMatrix(); hole_mesh.transform(m.cast()); + + // If the instance is scaled, undo the scaling of the hole + Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + hole_mesh.scale(Vec3d(1/scaling(0), 1/scaling(1), 1/scaling(2))); + + // Translate the hole into position and merge with the others hole_mesh.translate(hole.pos); holes_mesh.merge(hole_mesh); holes_mesh.repair(); @@ -627,6 +635,9 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->force_transparent = false; + + // Reset raycaster so it works with the new mesh: + m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); } if (m_clipping_plane_distance == 0.f) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 350bc8c9e6..c689be2141 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -93,7 +93,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); From 93d0bbd7efe92cf9b4a0e9bc021d846a901c8812 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 20 Dec 2019 12:25:44 +0100 Subject: [PATCH 083/130] Add boilerplate for shader based csg --- sandboxes/opencsg/CMakeLists.txt | 5 +- sandboxes/opencsg/Engine.cpp | 19 +-- sandboxes/opencsg/Engine.hpp | 45 +++-- sandboxes/opencsg/ShaderCSGDisplay.cpp | 81 +++++++++ sandboxes/opencsg/ShaderCSGDisplay.hpp | 26 +++ sandboxes/opencsg/main.cpp | 225 +++++++++++++++---------- src/slic3r/GUI/Job.hpp | 3 +- 7 files changed, 282 insertions(+), 122 deletions(-) create mode 100644 sandboxes/opencsg/ShaderCSGDisplay.cpp create mode 100644 sandboxes/opencsg/ShaderCSGDisplay.hpp diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index 600ef7884a..e9a51b0f4e 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -2,7 +2,10 @@ cmake_minimum_required(VERSION 3.0) project(OpenCSG-example) -add_executable(opencsg_example WIN32 main.cpp Engine.hpp Engine.cpp +add_executable(opencsg_example WIN32 + main.cpp + Engine.hpp Engine.cpp + ShaderCSGDisplay.hpp ShaderCSGDisplay.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp) diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp index 590faa2967..bd9da6540a 100644 --- a/sandboxes/opencsg/Engine.cpp +++ b/sandboxes/opencsg/Engine.cpp @@ -1,7 +1,6 @@ #include "Engine.hpp" #include #include -#include #include @@ -66,22 +65,6 @@ void CSGDisplay::render_scene() glFlush(); } -template::value_type> -std::vector transform_pts( - It from, It to, Trafo &&tr, GetPt &&point) -{ - auto ret = reserve_vector(to - from); - for(auto it = from; it != to; ++it) { - V v = *it; - v.pos = tr * point(*it); - ret.emplace_back(std::move(v)); - } - return ret; -} - void Scene::set_print(uqptr &&print) { m_print = std::move(print); @@ -287,7 +270,7 @@ void IndexedVertexArray::shrink_to_fit() { this->quad_indices.shrink_to_fit(); } -void Primitive::render() +void Volume::render() { glsafe(::glPushMatrix()); glsafe(::glMultMatrixd(m_trafo.get_matrix().data())); diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp index 2b58e9b75c..79bf9582bf 100644 --- a/sandboxes/opencsg/Engine.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -1,5 +1,5 @@ #ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP -#define SLIC3R_OCSG_EXMP_ENGINE_HPP_HPP +#define SLIC3R_OCSG_EXMP_ENGINE_HPP #include #include @@ -25,7 +25,7 @@ template using wkptr = std::weak_ptr; template> using vector = std::vector; // remove empty weak pointers from a vector -template void cleanup(vector> &listeners) { +template inline void cleanup(vector> &listeners) { auto it = std::remove_if(listeners.begin(), listeners.end(), [](auto &l) { return !l.lock(); }); listeners.erase(it, listeners.end()); @@ -34,7 +34,7 @@ template void cleanup(vector> &listeners) { // Call a class method on each element of a vector of objects (weak pointers) // of the same type. template -void call(F &&f, vector> &listeners, Args&&... args) { +inline void call(F &&f, vector> &listeners, Args&&... args) { for (auto &l : listeners) if (auto p = l.lock()) ((p.get())->*f)(std::forward(args)...); } @@ -171,19 +171,30 @@ public: // Try to enable or disable multisampling. bool enable_multisampling(bool e = true); -// A primitive that can be used with OpenCSG rendering algorithms. -// Does a similar job to GLVolume. -class Primitive : public OpenCSG::Primitive +template::value_type> +inline std::vector transform_pts( + It from, It to, Trafo &&tr, GetPt &&point) { + vector ret; + ret.reserve(to - from); + for(auto it = from; it != to; ++it) { + V v = *it; + v.pos = tr * point(*it); + ret.emplace_back(std::move(v)); + } + return ret; +} + +class Volume { IndexedVertexArray m_geom; Geometry::Transformation m_trafo; + public: - using OpenCSG::Primitive::Primitive; - - Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} - - void render() override; + void render(); void translation(const Vec3d &offset) { m_trafo.set_offset(offset); } void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); } @@ -197,6 +208,18 @@ public: } }; +// A primitive that can be used with OpenCSG rendering algorithms. +// Does a similar job to GLVolume. +class Primitive : public Volume, public OpenCSG::Primitive +{ +public: + using OpenCSG::Primitive::Primitive; + + Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {} + + void render() override { Volume::render(); } +}; + // A simple representation of a camera in a 3D scene class Camera { protected: diff --git a/sandboxes/opencsg/ShaderCSGDisplay.cpp b/sandboxes/opencsg/ShaderCSGDisplay.cpp new file mode 100644 index 0000000000..7339e408c5 --- /dev/null +++ b/sandboxes/opencsg/ShaderCSGDisplay.cpp @@ -0,0 +1,81 @@ +#include "ShaderCSGDisplay.hpp" +#include "libslic3r/SLAPrint.hpp" +#include + +namespace Slic3r { namespace GL { + +void ShaderCSGDisplay::add_mesh(const TriangleMesh &mesh) +{ + auto v = std::make_shared(); + v->load_mesh(mesh); + m_volumes.emplace_back(v); +} + +void ShaderCSGDisplay::render_scene() +{ + GLfloat color[] = {1.f, 1.f, 0.f, 0.f}; + glColor4fv(color); + glDepthFunc(GL_LESS); + for (auto &v : m_volumes) v->render(); + glFlush(); +} + +void ShaderCSGDisplay::on_scene_updated(const Scene &scene) +{ + // TriangleMesh mesh = print->objects().front()->hollowed_interior_mesh(); + // Look at CSGDisplay::on_scene_updated to see how its done there. + + const SLAPrint *print = scene.get_print(); + if (!print) return; + + m_volumes.clear(); + + for (const SLAPrintObject *po : print->objects()) { + const ModelObject *mo = po->model_object(); + TriangleMesh msh = mo->raw_mesh(); + + sla::DrainHoles holedata = mo->sla_drain_holes; + + for (const ModelInstance *mi : mo->instances) { + + TriangleMesh mshinst = msh; + auto interior = po->hollowed_interior_mesh(); + interior.transform(po->trafo().inverse()); + + mshinst.merge(interior); + mshinst.require_shared_vertices(); + + mi->transform_mesh(&mshinst); + + auto bb = mshinst.bounding_box(); + auto center = bb.center().cast(); + mshinst.translate(-center); + + mshinst.require_shared_vertices(); + add_mesh(mshinst); + + auto tr = Transform3f::Identity(); + tr.translate(-center); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.pos; + }); + + transform_pts(holedata.begin(), holedata.end(), tr, + [](const sla::DrainHole &dh) { + return dh.normal; + }); + } + + for (const sla::DrainHole &holept : holedata) { + TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh()); + holemesh.require_shared_vertices(); + add_mesh(holemesh); + } + } + + repaint(); +} + +}} // namespace Slic3r::GL diff --git a/sandboxes/opencsg/ShaderCSGDisplay.hpp b/sandboxes/opencsg/ShaderCSGDisplay.hpp new file mode 100644 index 0000000000..e1d96213e1 --- /dev/null +++ b/sandboxes/opencsg/ShaderCSGDisplay.hpp @@ -0,0 +1,26 @@ +#ifndef SHADERCSGDISPLAY_HPP +#define SHADERCSGDISPLAY_HPP + +#include "Engine.hpp" + +namespace Slic3r { namespace GL { + +class CSGVolume: public Volume +{ + // Extend... +}; + +class ShaderCSGDisplay: public Display { + vector> m_volumes; + + void add_mesh(const TriangleMesh &mesh); +public: + + void render_scene() override; + + void on_scene_updated(const Scene &scene) override; +}; + +}} + +#endif // SHADERCSGDISPLAY_HPP diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ff7e36b8be..b98d812ece 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -3,6 +3,7 @@ #include #include "Engine.hpp" +#include "ShaderCSGDisplay.hpp" #include @@ -30,41 +31,64 @@ using namespace Slic3r::GL; +class Renderer { +protected: + wxGLCanvas *m_canvas; + shptr m_context; +public: + + Renderer(wxGLCanvas *c): m_canvas{c} { + auto ctx = new wxGLContext(m_canvas); + if (!ctx || !ctx->IsOK()) { + wxMessageBox("Could not create OpenGL context.", "Error", + wxOK | wxICON_ERROR); + return; + } + + m_context.reset(ctx); + } + + wxGLContext * context() { return m_context.get(); } + const wxGLContext * context() const { return m_context.get(); } +}; + +// Tell the CSGDisplay how to swap buffers and set the gl context. +class OCSGRenderer: public Renderer, public Slic3r::GL::CSGDisplay { +public: + + OCSGRenderer(wxGLCanvas *c): Renderer{c} {} + + void set_active(long w, long h) override + { + m_canvas->SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + void swap_buffers() override { m_canvas->SwapBuffers(); } + + ~OCSGRenderer() override { m_scene_cache.clear(); } +}; + +// Tell the CSGDisplay how to swap buffers and set the gl context. +class ShaderCSGRenderer : public Renderer, + public Slic3r::GL::ShaderCSGDisplay +{ +public: + + ShaderCSGRenderer(wxGLCanvas *c): Renderer{c} {} + + void set_active(long w, long h) override + { + m_canvas->SetCurrent(*m_context); + Slic3r::GL::Display::set_active(w, h); + } + + void swap_buffers() override { m_canvas->SwapBuffers(); } +}; + // The opengl rendering facility. Here we implement the rendering objects. class Canvas: public wxGLCanvas { - - // Tell the CSGDisplay how to swap buffers and set the gl context. - class OCSGRenderer: public Slic3r::GL::CSGDisplay { - Canvas *m_canvas; - shptr m_context; - public: - - OCSGRenderer(Canvas *c): m_canvas{c} { - auto ctx = new wxGLContext(m_canvas); - if (!ctx || !ctx->IsOK()) { - wxMessageBox("Could not create OpenGL context.", "Error", - wxOK | wxICON_ERROR); - return; - } - - m_context.reset(ctx); - } - - void set_active(long w, long h) override - { - m_canvas->SetCurrent(*m_context); - Slic3r::GL::Display::set_active(w, h); - } - - wxGLContext * context() { return m_context.get(); } - const wxGLContext * context() const { return m_context.get(); } - - void swap_buffers() override { m_canvas->SwapBuffers(); } - - ~OCSGRenderer() override { m_scene_cache.clear(); } - }; - // Create the OCSGDisplay for rendering with OpenCSG algorithms shptr m_ocsgdisplay = std::make_shared(this); @@ -90,12 +114,14 @@ public: const wxSize ClientSize = GetClientSize(); m_display->set_screen_size(ClientSize.x, ClientSize.y); + m_display->repaint(); }); } shptr get_display() const { return m_display; } + void set_display(shptr d) { m_display = d; } - + shptr get_ocsg_display() const { return m_ocsgdisplay; } }; @@ -172,6 +198,7 @@ public: void play() { m_playing = true; + std::cout << "Mouse is playing back" << std::endl; for (const Event &evt : m_events) { switch (evt.type) { case LCLK_U: MouseInput::left_click_up(); break; @@ -233,6 +260,7 @@ class MyFrame: public wxFrame m_parent->m_scene->set_print(std::move(m_print)); m_parent->m_stbar->set_status_text( wxString::Format("Model %s loaded.", m_fname)); + std::cout << "Model loaded" << std::endl; } }; @@ -247,7 +275,7 @@ public: MyFrame(const wxString & title, const wxPoint & pos, const wxSize & size, - const Slic3r::GL::CSGSettings &settings); + const wxCmdLineParser &settings); // Grab a 3mf and load (hollow it out) within the UI job. void load_model(const std::string &fname) { @@ -264,6 +292,9 @@ public: std::string model_name; std::getline(stream, model_name); load_model(model_name); + while (!m_ui_job->is_finalized()) { + wxYield(); + } int w, h; stream >> w >> h; @@ -286,7 +317,7 @@ public: // Possible OpenCSG configuration values. Will be used on the command line and // on the UI widgets. -static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS"}; +static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS", "EnricoShader"}; static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; @@ -309,41 +340,8 @@ public: wxString fname; bool is_play = parser.Found("play", &fname); - wxString alg; - parser.Found("algorithm", &alg); - - wxString depth; - parser.Found("depth", &depth); - - wxString opt; - parser.Found("optimization", &opt); - - long convexity = 1; - parser.Found("convexity", &convexity); - - bool csg_off = parser.Found("disable-csg"); - - auto get_idx = [](const wxString &a, const std::vector &v) { - auto it = std::find(v.begin(), v.end(), a.ToStdString()); - return it - v.begin(); - }; - - Slic3r::GL::CSGSettings settings; - - if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) - settings.set_algo(OpenCSG::Algorithm(a)); - - if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); - - if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) - settings.set_optimization(OpenCSG::Optimization(a)); - - settings.set_convexity(unsigned(convexity)); - settings.enable_csg(!csg_off); - - m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), settings); - + m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), parser); + if (is_play) { m_frame->Show( true ); m_frame->play_back_mouse(fname.ToStdString()); @@ -361,7 +359,7 @@ public: wxIMPLEMENT_APP(App); MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, - const Slic3r::GL::CSGSettings &settings): + const wxCmdLineParser &parser): wxFrame(nullptr, wxID_ANY, title, pos, size) { wxMenu *menuFile = new wxMenu; @@ -396,9 +394,40 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + wxString alg; + parser.Found("algorithm", &alg); - m_ctl->add_display(m_canvas->get_display()); + wxString depth; + parser.Found("depth", &depth); + + wxString opt; + parser.Found("optimization", &opt); + + long convexity = 1; + parser.Found("convexity", &convexity); + + bool csg_off = parser.Found("disable-csg"); + + auto get_idx = [](const wxString &a, const std::vector &v) { + auto it = std::find(v.begin(), v.end(), a.ToStdString()); + return it - v.begin(); + }; + + Slic3r::GL::CSGSettings settings; + + if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) + settings.set_algo(OpenCSG::Algorithm(a)); + + if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) + settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); + + if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) + settings.set_optimization(OpenCSG::Optimization(a)); + + settings.set_convexity(unsigned(convexity)); + settings.enable_csg(!csg_off); + + m_canvas->get_ocsg_display()->apply_csgsettings(settings); wxPanel *control_panel = new wxPanel(this); @@ -459,10 +488,6 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); console_sizer->Add(fpstext, 0, wxALL, 5); - m_canvas->get_ocsg_display()->get_fps_counter().add_listener([this, fpstext](double fps) { - fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); - m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; - }); m_record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); console_sizer->Add(m_record_btn, 0, wxALL | wxEXPAND, 5); @@ -477,12 +502,28 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, sizer->Add(control_panel, 0, wxEXPAND); SetSizer(sizer); - if (settings.get_algo() > 0) depth_select->Enable(true); - alg_select->SetSelection(settings.get_algo()); - depth_select->SetSelection(settings.get_depth_algo()); - optimization_select->SetSelection(settings.get_optimization()); - convexity_spin->SetValue(int(settings.get_convexity())); - csg_toggle->SetValue(settings.is_enabled()); + if (alg != "EnricoShader") { + if (settings.get_algo() > 0) depth_select->Enable(true); + alg_select->SetSelection(settings.get_algo()); + depth_select->SetSelection(settings.get_depth_algo()); + optimization_select->SetSelection(settings.get_optimization()); + convexity_spin->SetValue(int(settings.get_convexity())); + csg_toggle->SetValue(settings.is_enabled()); + } else { + alg_select->SetSelection(int(get_idx("EnricoShader", CSG_ALGS))); + depth_select->Disable(); + optimization_select->Disable(); + convexity_spin->Disable(); + csg_toggle->Disable(); + auto renderer = std::make_shared(canvas()); + canvas()->set_display(renderer); + } + + m_ctl->add_display(m_canvas->get_display()); + m_canvas->get_display()->get_fps_counter().add_listener([this, fpstext](double fps) { + fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); + m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; + }); Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt){ if (m_canvas) RemoveChild(m_canvas.get()); @@ -504,6 +545,16 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, const wxSize ClientSize = GetClientSize(); m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); enable_multisampling(ms_toggle->GetValue()); + + // Do the repaint continuously + m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + if (IsShown() && m_canvas->IsShown()) + m_canvas->get_display()->repaint(); + + evt.RequestMore(); + }); + + bind_canvas_events(m_mouse); }); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { @@ -585,14 +636,6 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, } } }); - - // Do the repaint continuously - m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - if (m_canvas->IsShown()) m_canvas->get_display()->repaint(); - evt.RequestMore(); - }); - - bind_canvas_events(m_mouse); } void MyFrame::bind_canvas_events(MouseInput &ms) diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp index 9accd0ef39..ac31b9bdb0 100644 --- a/src/slic3r/GUI/Job.hpp +++ b/src/slic3r/GUI/Job.hpp @@ -62,7 +62,6 @@ protected: // Launched when the job is finished. It refreshes the 3Dscene by def. virtual void finalize() { m_finalized = true; } - bool is_finalized() const { return m_finalized; } public: Job(std::shared_ptr pri) : m_progress(pri) @@ -89,6 +88,8 @@ public: }); } + bool is_finalized() const { return m_finalized; } + Job(const Job &) = delete; Job(Job &&) = delete; Job &operator=(const Job &) = delete; From 083b557ec2c1d2222e9177d5b6552a97c3336e29 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 20 Dec 2019 15:12:54 +0100 Subject: [PATCH 084/130] Display switching --- sandboxes/opencsg/Engine.hpp | 28 ++- sandboxes/opencsg/ShaderCSGDisplay.hpp | 1 + sandboxes/opencsg/main.cpp | 314 ++++++++++++++----------- 3 files changed, 200 insertions(+), 143 deletions(-) diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp index 79bf9582bf..c078a39d86 100644 --- a/sandboxes/opencsg/Engine.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -309,13 +309,25 @@ private: public: int get_algo() const { return int(m_csgalg); } - void set_algo(OpenCSG::Algorithm alg) { m_csgalg = alg; } + void set_algo(int alg) + { + if (alg < OpenCSG::Algorithm::AlgorithmUnused) + m_csgalg = OpenCSG::Algorithm(alg); + } int get_depth_algo() const { return int(m_depth_algo); } - void set_depth_algo(OpenCSG::DepthComplexityAlgorithm alg) { m_depth_algo = alg; } + void set_depth_algo(int alg) + { + if (alg < OpenCSG::DepthComplexityAlgorithmUnused) + m_depth_algo = OpenCSG::DepthComplexityAlgorithm(alg); + } int get_optimization() const { return int(m_optim); } - void set_optimization(OpenCSG::Optimization o) { m_optim = o; } + void set_optimization(int o) + { + if (o < OpenCSG::Optimization::OptimizationUnused) + m_optim = OpenCSG::Optimization(o); + } void enable_csg(bool en = true) { m_enable = en; } bool is_enabled() const { return m_enable; } @@ -382,7 +394,9 @@ public: ~Display() override; - Camera * camera() { return m_camera.get(); } + shptr get_camera() const { return m_camera; } + shptr get_camera() { return m_camera; } + void set_camera(shptr cam) { m_camera = cam; } virtual void swap_buffers() = 0; virtual void set_active(long width, long height); @@ -456,8 +470,8 @@ class Controller : public std::enable_shared_from_this, template void call_cameras(F &&f, Args&&... args) { for (wkptr &l : m_displays) - if (auto disp = l.lock()) if (disp->camera()) - (disp->camera()->*f)(std::forward(args)...); + if (auto disp = l.lock()) if (auto cam = disp->get_camera()) + (cam.get()->*f)(std::forward(args)...); } public: @@ -477,6 +491,8 @@ public: cleanup(m_displays); } + void remove_displays() { m_displays = {}; } + void on_scene_updated(const Scene &scene) override; void on_left_click_down() override { m_left_btn = true; } diff --git a/sandboxes/opencsg/ShaderCSGDisplay.hpp b/sandboxes/opencsg/ShaderCSGDisplay.hpp index e1d96213e1..bf0c3a4240 100644 --- a/sandboxes/opencsg/ShaderCSGDisplay.hpp +++ b/sandboxes/opencsg/ShaderCSGDisplay.hpp @@ -11,6 +11,7 @@ class CSGVolume: public Volume }; class ShaderCSGDisplay: public Display { +protected: vector> m_volumes; void add_mesh(const TriangleMesh &mesh); diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index b98d812ece..c82ff2f485 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -65,14 +65,10 @@ public: } void swap_buffers() override { m_canvas->SwapBuffers(); } - - ~OCSGRenderer() override { m_scene_cache.clear(); } }; // Tell the CSGDisplay how to swap buffers and set the gl context. -class ShaderCSGRenderer : public Renderer, - public Slic3r::GL::ShaderCSGDisplay -{ +class ShaderCSGRenderer : public Renderer, public Slic3r::GL::ShaderCSGDisplay { public: ShaderCSGRenderer(wxGLCanvas *c): Renderer{c} {} @@ -88,12 +84,9 @@ public: // The opengl rendering facility. Here we implement the rendering objects. class Canvas: public wxGLCanvas -{ - // Create the OCSGDisplay for rendering with OpenCSG algorithms - shptr m_ocsgdisplay = std::make_shared(this); - +{ // One display is active at a time, the OCSGRenderer by default. - shptr m_display = m_ocsgdisplay; + shptr m_display; public: @@ -121,8 +114,6 @@ public: shptr get_display() const { return m_display; } void set_display(shptr d) { m_display = d; } - - shptr get_ocsg_display() const { return m_ocsgdisplay; } }; // Enumerate possible mouse events, we will record them. @@ -198,7 +189,6 @@ public: void play() { m_playing = true; - std::cout << "Mouse is playing back" << std::endl; for (const Event &evt : m_events) { switch (evt.type) { case LCLK_U: MouseInput::left_click_up(); break; @@ -225,10 +215,12 @@ public: class MyFrame: public wxFrame { // Instantiate the 3D engine. - shptr m_scene; // Model - shptr m_canvas; // View - shptr m_ctl; // Controller - + shptr m_scene; // Model + shptr m_canvas; // Views store + shptr m_ocsgdisplay; // View + shptr m_shadercsg_display; // Another view + shptr m_ctl; // Controller + // Add a status bar with progress indication. shptr m_stbar; @@ -260,7 +252,6 @@ class MyFrame: public wxFrame m_parent->m_scene->set_print(std::move(m_print)); m_parent->m_stbar->set_status_text( wxString::Format("Model %s loaded.", m_fname)); - std::cout << "Model loaded" << std::endl; } }; @@ -269,14 +260,30 @@ class MyFrame: public wxFrame // To keep track of the running average of measured fps values. double m_fps_avg = 0.; + // We need the record button across methods wxToggleButton *m_record_btn; + wxComboBox * m_alg_select; + wxComboBox * m_depth_select; + wxComboBox * m_optimization_select; + wxSpinCtrl * m_convexity_spin; + wxToggleButton *m_csg_toggle; + wxToggleButton *m_ms_toggle; + wxStaticText *m_fpstext; + + CSGSettings m_csg_settings; + + void read_csg_settings(const wxCmdLineParser &parser); + + void set_renderer_algorithm(const wxString &alg); + + void activate_canvas_display(); public: - MyFrame(const wxString & title, - const wxPoint & pos, - const wxSize & size, - const wxCmdLineParser &settings); - + MyFrame(const wxString & title, + const wxPoint & pos, + const wxSize & size, + const wxCmdLineParser &parser); + // Grab a 3mf and load (hollow it out) within the UI job. void load_model(const std::string &fname) { m_ui_job = std::make_unique(this, fname); @@ -321,6 +328,12 @@ static const std::vector CSG_ALGS = {"Auto", "Goldfeather", "SCS", "E static const std::vector CSG_DEPTH = {"Off", "OcclusionQuery", "On"}; static const std::vector CSG_OPT = { "Default", "ForceOn", "On", "Off" }; +inline long get_idx(const wxString &a, const std::vector &v) +{ + auto it = std::find(v.begin(), v.end(), a.ToStdString()); + return it - v.begin(); +}; + class App : public wxApp { MyFrame *m_frame = nullptr; public: @@ -358,6 +371,105 @@ public: wxIMPLEMENT_APP(App); +void MyFrame::read_csg_settings(const wxCmdLineParser &parser) +{ + wxString alg; + parser.Found("algorithm", &alg); + + wxString depth; + parser.Found("depth", &depth); + + wxString opt; + parser.Found("optimization", &opt); + + long convexity = 1; + parser.Found("convexity", &convexity); + + bool csg_off = parser.Found("disable-csg"); + + if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) + m_csg_settings.set_algo(OpenCSG::Algorithm(a)); + + if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) + m_csg_settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); + + if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) + m_csg_settings.set_optimization(OpenCSG::Optimization(a)); + + m_csg_settings.set_convexity(unsigned(convexity)); + m_csg_settings.enable_csg(!csg_off); + + if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings); +} + +void MyFrame::set_renderer_algorithm(const wxString &alg) +{ + long alg_idx = get_idx("EnricoShader", CSG_ALGS); + if (alg_idx < 0 || alg_idx >= CSG_ALGS.size()) return; + + // If there is a valid display in place, save its camera. + auto cam = m_canvas->get_display() ? + m_canvas->get_display()->get_camera() : nullptr; + + if (alg == "EnricoShader") { + m_alg_select->SetSelection(int(alg_idx)); + m_depth_select->Disable(); + m_optimization_select->Disable(); + m_csg_toggle->Disable(); + + m_ocsgdisplay.reset(); + canvas()->set_display(nullptr); + m_shadercsg_display = std::make_shared(canvas()); + canvas()->set_display(m_shadercsg_display); + } else { + if (m_csg_settings.get_algo() > 0) m_depth_select->Enable(true); + m_alg_select->SetSelection(m_csg_settings.get_algo()); + m_depth_select->SetSelection(m_csg_settings.get_depth_algo()); + m_optimization_select->SetSelection(m_csg_settings.get_optimization()); + m_convexity_spin->SetValue(int(m_csg_settings.get_convexity())); + m_csg_toggle->SetValue(m_csg_settings.is_enabled()); + m_optimization_select->Enable(); + m_csg_toggle->Enable(); + + m_shadercsg_display.reset(); + canvas()->set_display(nullptr); + m_ocsgdisplay = std::make_shared(canvas()); + m_ocsgdisplay->apply_csgsettings(m_csg_settings); + canvas()->set_display(m_ocsgdisplay); + } + + if (cam) m_canvas->get_display()->set_camera(cam); + + m_ctl->remove_displays(); + m_ctl->add_display(m_canvas->get_display()); + m_canvas->get_display()->get_fps_counter().add_listener([this](double fps) { + m_fpstext->SetLabel(wxString::Format("fps: %.2f", fps)); + m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; + }); + + if (IsShown()) { + activate_canvas_display(); + m_canvas->get_display()->on_scene_updated(*m_scene); + } +} + +void MyFrame::activate_canvas_display() +{ + const wxSize ClientSize = m_canvas->GetClientSize(); + m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); + enable_multisampling(m_ms_toggle->GetValue()); + + // Do the repaint continuously + m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { + if (IsShown() && m_canvas->IsShown()) + m_canvas->get_display()->repaint(); + + evt.RequestMore(); + }); + + bind_canvas_events(m_mouse); +} + MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, const wxCmdLineParser &parser): wxFrame(nullptr, wxID_ANY, title, pos, size) @@ -384,7 +496,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, // glReadPixels would not return the alpha channel on NVIDIA if // not requested when the GL context is created. WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8, - /*WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4,*/ 0}; + WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0}; m_scene = std::make_shared(); m_ctl = std::make_shared(); @@ -394,40 +506,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE); - wxString alg; - parser.Found("algorithm", &alg); - - wxString depth; - parser.Found("depth", &depth); - - wxString opt; - parser.Found("optimization", &opt); - - long convexity = 1; - parser.Found("convexity", &convexity); - - bool csg_off = parser.Found("disable-csg"); - - auto get_idx = [](const wxString &a, const std::vector &v) { - auto it = std::find(v.begin(), v.end(), a.ToStdString()); - return it - v.begin(); - }; - - Slic3r::GL::CSGSettings settings; - - if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) - settings.set_algo(OpenCSG::Algorithm(a)); - - if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) - settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a)); - - if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) - settings.set_optimization(OpenCSG::Optimization(a)); - - settings.set_convexity(unsigned(convexity)); - settings.enable_csg(!csg_off); - - m_canvas->get_ocsg_display()->apply_csgsettings(settings); + read_csg_settings(parser); wxPanel *control_panel = new wxPanel(this); @@ -440,12 +519,12 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, wxSL_VERTICAL); slider_sizer->Add(slider, 1, wxEXPAND); - auto ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); - console_sizer->Add(ms_toggle, 0, wxALL | wxEXPAND, 5); + m_ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling"); + console_sizer->Add(m_ms_toggle, 0, wxALL | wxEXPAND, 5); - auto csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); - csg_toggle->SetValue(true); - console_sizer->Add(csg_toggle, 0, wxALL | wxEXPAND, 5); + m_csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG"); + m_csg_toggle->SetValue(true); + console_sizer->Add(m_csg_toggle, 0, wxALL | wxEXPAND, 5); auto add_combobox = [control_panel, console_sizer] (const wxString &label, const std::vector &list) @@ -479,15 +558,14 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, return widget; }; - auto convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); + m_convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100); - auto alg_select = add_combobox("Algorithm", CSG_ALGS); - auto depth_select = add_combobox("Depth Complexity", CSG_DEPTH); - auto optimization_select = add_combobox("Optimization", CSG_OPT); - depth_select->Disable(); + m_alg_select = add_combobox("Algorithm", CSG_ALGS); + m_depth_select = add_combobox("Depth Complexity", CSG_DEPTH); + m_optimization_select = add_combobox("Optimization", CSG_OPT); - auto fpstext = new wxStaticText(control_panel, wxID_ANY, ""); - console_sizer->Add(fpstext, 0, wxALL, 5); + m_fpstext = new wxStaticText(control_panel, wxID_ANY, ""); + console_sizer->Add(m_fpstext, 0, wxALL, 5); m_record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record"); console_sizer->Add(m_record_btn, 0, wxALL | wxEXPAND, 5); @@ -502,28 +580,10 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, sizer->Add(control_panel, 0, wxEXPAND); SetSizer(sizer); - if (alg != "EnricoShader") { - if (settings.get_algo() > 0) depth_select->Enable(true); - alg_select->SetSelection(settings.get_algo()); - depth_select->SetSelection(settings.get_depth_algo()); - optimization_select->SetSelection(settings.get_optimization()); - convexity_spin->SetValue(int(settings.get_convexity())); - csg_toggle->SetValue(settings.is_enabled()); - } else { - alg_select->SetSelection(int(get_idx("EnricoShader", CSG_ALGS))); - depth_select->Disable(); - optimization_select->Disable(); - convexity_spin->Disable(); - csg_toggle->Disable(); - auto renderer = std::make_shared(canvas()); - canvas()->set_display(renderer); - } + wxString alg; + if (!parser.Found("algorithm", &alg)) alg = "Auto"; - m_ctl->add_display(m_canvas->get_display()); - m_canvas->get_display()->get_fps_counter().add_listener([this, fpstext](double fps) { - fpstext->SetLabel(wxString::Format("fps: %.2f", fps) ); - m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps; - }); + set_renderer_algorithm(alg); Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt){ if (m_canvas) RemoveChild(m_canvas.get()); @@ -541,69 +601,49 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT); - Bind(wxEVT_SHOW, [this, ms_toggle](wxShowEvent &) { - const wxSize ClientSize = GetClientSize(); - m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); - enable_multisampling(ms_toggle->GetValue()); - - // Do the repaint continuously - m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - if (IsShown() && m_canvas->IsShown()) - m_canvas->get_display()->repaint(); - - evt.RequestMore(); - }); - - bind_canvas_events(m_mouse); + Bind(wxEVT_SHOW, [this](wxShowEvent &) { + activate_canvas_display(); }); Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) { m_ctl->move_clip_plane(double(slider->GetValue())); }); - ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, ms_toggle](wxCommandEvent &){ - enable_multisampling(ms_toggle->GetValue()); + m_ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){ + enable_multisampling(m_ms_toggle->GetValue()); m_canvas->get_display()->repaint(); }); - csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this, csg_toggle](wxCommandEvent &){ - CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); - stt.enable_csg(csg_toggle->GetValue()); - m_canvas->get_ocsg_display()->apply_csgsettings(stt); + m_csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){ + CSGSettings stt = m_ocsgdisplay->get_csgsettings(); + stt.enable_csg(m_csg_toggle->GetValue()); + m_ocsgdisplay->apply_csgsettings(stt); }); - alg_select->Bind(wxEVT_COMBOBOX, - [this, alg_select, depth_select](wxCommandEvent &) - { - int sel = alg_select->GetSelection(); - depth_select->Enable(sel > 0); - CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); - stt.set_algo(OpenCSG::Algorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(stt); + m_alg_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { + wxString alg = m_alg_select->GetValue(); + int sel = m_alg_select->GetSelection(); + m_csg_settings.set_algo(sel); + set_renderer_algorithm(alg); }); - depth_select->Bind(wxEVT_COMBOBOX, [this, depth_select](wxCommandEvent &) { - int sel = depth_select->GetSelection(); - CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); - stt.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(stt); + m_depth_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { + int sel = m_depth_select->GetSelection(); + m_csg_settings.set_depth_algo(sel); + if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings); }); - optimization_select->Bind(wxEVT_COMBOBOX, - [this, optimization_select](wxCommandEvent &) { - int sel = optimization_select->GetSelection(); - CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); - stt.set_optimization(OpenCSG::Optimization(sel)); - m_canvas->get_ocsg_display()->apply_csgsettings(stt); + m_optimization_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { + int sel = m_optimization_select->GetSelection(); + m_csg_settings.set_optimization(sel); + if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings); }); - convexity_spin->Bind(wxEVT_SPINCTRL, [this, convexity_spin](wxSpinEvent &) { - CSGSettings stt = m_canvas->get_ocsg_display()->get_csgsettings(); - int c = convexity_spin->GetValue(); - + m_convexity_spin->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent &) { + int c = m_convexity_spin->GetValue(); if (c > 0) { - stt.set_convexity(unsigned(c)); - m_canvas->get_ocsg_display()->apply_csgsettings(stt); + m_csg_settings.set_convexity(unsigned(c)); + if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings); } }); @@ -614,7 +654,7 @@ MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, } if (m_record_btn->GetValue()) { - if (auto c = m_canvas->get_display()->camera()) reset(*c); + if (auto c = m_canvas->get_display()->get_camera()) reset(*c); m_ctl->on_scene_updated(*m_scene); m_mouse.record(true); } else { From 3a185d7f574ec512c1f9e172b3bf90e2938a552c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 20 Dec 2019 19:57:34 +0100 Subject: [PATCH 085/130] fix windows widgets while playback --- sandboxes/opencsg/main.cpp | 39 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index c82ff2f485..c1bda6b398 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -109,6 +109,13 @@ public: m_display->set_screen_size(ClientSize.x, ClientSize.y); m_display->repaint(); }); + + Bind(wxEVT_SIZE, [this](wxSizeEvent &) { + const wxSize ClientSize = GetClientSize(); + + m_display->set_screen_size(ClientSize.x, ClientSize.y); + m_display->repaint(); + }); } shptr get_display() const { return m_display; } @@ -200,7 +207,7 @@ public: case MV: MouseInput::move_to(evt.a, evt.b); break; } - wxYield(); + wxTheApp->Yield(); if (!m_playing) break; } @@ -299,9 +306,9 @@ public: std::string model_name; std::getline(stream, model_name); load_model(model_name); - while (!m_ui_job->is_finalized()) { - wxYield(); - } + + while (!m_ui_job->is_finalized()) + wxTheApp->Yield();; int w, h; stream >> w >> h; @@ -336,6 +343,7 @@ inline long get_idx(const wxString &a, const std::vector &v) class App : public wxApp { MyFrame *m_frame = nullptr; + wxString m_fname; public: bool OnInit() override { @@ -350,23 +358,23 @@ public: parser.Parse(); - wxString fname; - bool is_play = parser.Found("play", &fname); + bool is_play = parser.Found("play", &m_fname); m_frame = new MyFrame("PrusaSlicer OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), parser); if (is_play) { + Bind(wxEVT_IDLE, &App::Play, this); m_frame->Show( true ); - m_frame->play_back_mouse(fname.ToStdString()); - - std::cout << m_frame->get_fps_average() << std::endl; - - m_frame->Destroy(); - } else m_frame->Show( true ); return true; } + + void Play(wxIdleEvent &) { + Unbind(wxEVT_IDLE, &App::Play, this); + m_frame->play_back_mouse(m_fname.ToStdString()); + m_frame->Destroy(); + } }; wxIMPLEMENT_APP(App); @@ -438,7 +446,8 @@ void MyFrame::set_renderer_algorithm(const wxString &alg) canvas()->set_display(m_ocsgdisplay); } - if (cam) m_canvas->get_display()->set_camera(cam); + if (cam) + m_canvas->get_display()->set_camera(cam); m_ctl->remove_displays(); m_ctl->add_display(m_canvas->get_display()); @@ -461,9 +470,7 @@ void MyFrame::activate_canvas_display() // Do the repaint continuously m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { - if (IsShown() && m_canvas->IsShown()) - m_canvas->get_display()->repaint(); - + m_canvas->get_display()->repaint(); evt.RequestMore(); }); From 451f04b590f3455eb2400e4553d790fa4c8d4164 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 20 Dec 2019 20:18:23 +0100 Subject: [PATCH 086/130] Fix linux assertion --- sandboxes/opencsg/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index c1bda6b398..3922706e74 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -110,11 +110,9 @@ public: m_display->repaint(); }); - Bind(wxEVT_SIZE, [this](wxSizeEvent &) { + Bind(wxEVT_SIZE, [this](wxSizeEvent &) { const wxSize ClientSize = GetClientSize(); - m_display->set_screen_size(ClientSize.x, ClientSize.y); - m_display->repaint(); }); } From 6a870ef8bb94beea865c11a4fe6bba56894776e6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 6 Jan 2020 16:02:55 +0100 Subject: [PATCH 087/130] Fixed incorrect z-shift when showing hollowed object --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 4 +++- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index f0b71f2608..44894a0fb7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -84,6 +84,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Select if (m_state == On) { m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -127,6 +128,7 @@ void GLGizmoHollow::on_render() const } // Show/hide the original object m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); @@ -628,7 +630,7 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) // create a new GLVolume that only has the cavity inside Geometry::Transformation volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); - volume_trafo.set_offset(volume_trafo.get_offset() + Vec3d(0., 0., m_z_shift)); + volume_trafo.set_offset(volume_trafo.get_offset()); m_c->m_volume_with_cavity.reset(new GLVolume(GLVolume::MODEL_COLOR[2])); m_c->m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_c->m_cavity_mesh.get()); m_c->m_volume_with_cavity->finalize_geometry(true); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index c689be2141..cb9dc27539 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -94,6 +94,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S if (m_state == On) { m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -137,6 +138,7 @@ void GLGizmoSlaSupports::on_render() const } // Show/hide the original object m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); From 7d55df052fe99908664744bb9b1340b4c7b9d718 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jan 2020 09:39:48 +0100 Subject: [PATCH 088/130] repaint causes crash on linux --- sandboxes/opencsg/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 3922706e74..82efb8ada5 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -107,12 +107,12 @@ public: const wxSize ClientSize = GetClientSize(); m_display->set_screen_size(ClientSize.x, ClientSize.y); - m_display->repaint(); }); Bind(wxEVT_SIZE, [this](wxSizeEvent &) { const wxSize ClientSize = GetClientSize(); m_display->set_screen_size(ClientSize.x, ClientSize.y); + m_display->repaint(); }); } From f874b618817f0a9e965e8f2f3919643984462658 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jan 2020 10:10:24 +0100 Subject: [PATCH 089/130] Transform the position AND the normals of drainhole points --- src/libslic3r/SLAPrint.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 897963f199..1e00e6edb1 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1165,30 +1165,29 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const { return m_transformed_rmesh.get(); } -template::value_type> -std::vector transform_pts(It from, It to, Trafo &&tr) -{ - auto ret = reserve_vector(to - from); - for(auto it = from; it != to; ++it) { - V v = *it; - v.pos = tr * it->pos; - ret.emplace_back(std::move(v)); - } - return ret; -} - sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); - auto& spts = m_model_object->sla_support_points; - return transform_pts(spts.begin(), spts.end(), trafo().cast()); + auto spts = m_model_object->sla_support_points; + auto tr = trafo().cast(); + for (sla::SupportPoint& suppt : spts) { + suppt.pos = tr * suppt.pos; + } + + return spts; } sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const { assert(m_model_object != nullptr); - auto& spts = m_model_object->sla_drain_holes; - return transform_pts(spts.begin(), spts.end(), trafo().cast()); + auto pts = m_model_object->sla_drain_holes; + auto tr = trafo().cast(); + for (sla::DrainHole &hl : pts) { + hl.pos = tr * hl.pos; + hl.normal = tr * hl.normal; + } + + return pts; } DynamicConfig SLAPrintStatistics::config() const From a3a99d7a076df8b2dad39c8e22e83b0e60f500e8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jan 2020 10:49:54 +0100 Subject: [PATCH 090/130] Do not translate the normal of drainhole points. --- src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 1e00e6edb1..4d34c09c7b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1184,7 +1184,7 @@ sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const auto tr = trafo().cast(); for (sla::DrainHole &hl : pts) { hl.pos = tr * hl.pos; - hl.normal = tr * hl.normal; + hl.normal = tr * hl.normal - tr.translation(); } return pts; From bb62f36df3915f125c7460c6c12ab88a47efe798 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jan 2020 17:10:11 +0100 Subject: [PATCH 091/130] Add tests for EigenMesh3D raycaster with hole support. Tests fail! Supports are intersecting the object when holes are added. --- src/libslic3r/SLA/Hollowing.cpp | 37 +++ src/libslic3r/SLA/Hollowing.hpp | 7 + src/libslic3r/SLAPrintSteps.cpp | 37 --- tests/sla_print/CMakeLists.txt | 5 +- tests/sla_print/sla_print_tests.cpp | 364 +------------------------- tests/sla_print/sla_raycast_tests.cpp | 61 +++++ tests/sla_print/sla_test_utils.cpp | 297 +++++++++++++++++++++ tests/sla_print/sla_test_utils.hpp | 112 ++++++++ tests/test_utils.hpp | 21 ++ 9 files changed, 541 insertions(+), 400 deletions(-) create mode 100644 tests/sla_print/sla_raycast_tests.cpp create mode 100644 tests/sla_print/sla_test_utils.cpp create mode 100644 tests/sla_print/sla_test_utils.hpp create mode 100644 tests/test_utils.hpp diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 2b35722478..8dc2d30929 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -247,4 +248,40 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, return true; } +void cut_drainholes(std::vector & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function thr) +{ + TriangleMesh mesh; + for (const sla::DrainHole &holept : holes) { + auto r = double(holept.radius); + auto h = double(holept.height); + sla::Contour3D hole = sla::cylinder(r, h); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, holept.normal.cast()); + for(auto& p : hole.points) p = q * p + holept.pos.cast(); + mesh.merge(sla::to_triangle_mesh(hole)); + } + + if (mesh.empty()) return; + + mesh.require_shared_vertices(); + + TriangleMeshSlicer slicer(&mesh); + + std::vector hole_slices; + slicer.slice(slicegrid, closing_radius, &hole_slices, thr); + + if (obj_slices.size() != hole_slices.size()) + BOOST_LOG_TRIVIAL(warning) + << "Sliced object and drain-holes layer count does not match!"; + + size_t until = std::min(obj_slices.size(), hole_slices.size()); + + for (size_t i = 0; i < until; ++i) + obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); +} + }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index ba1eb2d622..b3375ed1ae 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -17,6 +17,7 @@ struct HollowingConfig double min_thickness = 2.; double quality = 0.5; double closing_distance = 0.5; + bool enabled = true; }; struct DrainHole @@ -57,6 +58,12 @@ std::unique_ptr generate_interior(const TriangleMesh &mesh, const HollowingConfig & = {}, const JobController &ctl = {}); +void cut_drainholes(std::vector & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function thr); + } } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index e0c92a71c5..0ae0e66a44 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -79,7 +79,6 @@ SLAPrint::Steps::Steps(SLAPrint *print) void SLAPrint::Steps::hollow_model(SLAPrintObject &po) { - if (!po.m_config.hollowing_enable.getBool()) { BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; po.m_hollowing_data.reset(); @@ -102,42 +101,6 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; } -static void cut_drainholes(std::vector & obj_slices, - const std::vector &slicegrid, - float closing_radius, - const sla::DrainHoles & holes, - std::function thr) -{ - TriangleMesh mesh; - for (const sla::DrainHole &holept : holes) { - auto r = double(holept.radius); - auto h = double(holept.height); - sla::Contour3D hole = sla::cylinder(r, h); - Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, holept.normal.cast()); - for(auto& p : hole.points) p = q * p + holept.pos.cast(); - mesh.merge(sla::to_triangle_mesh(hole)); - } - - if (mesh.empty()) return; - - mesh.require_shared_vertices(); - - TriangleMeshSlicer slicer(&mesh); - - std::vector hole_slices; - slicer.slice(slicegrid, closing_radius, &hole_slices, thr); - - if (obj_slices.size() != hole_slices.size()) - BOOST_LOG_TRIVIAL(warning) - << "Sliced object and drain-holes layer count does not match!"; - - size_t until = std::min(obj_slices.size(), hole_slices.size()); - - for (size_t i = 0; i < until; ++i) - obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); -} - // The slicing will be performed on an imaginary 1D grid which starts from // the bottom of the bounding box created around the supported model. So // the first layer which is usually thicker will be part of the supports diff --git a/tests/sla_print/CMakeLists.txt b/tests/sla_print/CMakeLists.txt index ecc68db0a4..9d47f3ae4d 100644 --- a/tests/sla_print/CMakeLists.txt +++ b/tests/sla_print/CMakeLists.txt @@ -1,5 +1,8 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) -add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp sla_print_tests.cpp) +add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests_main.cpp + sla_print_tests.cpp + sla_test_utils.hpp sla_test_utils.cpp + sla_raycast_tests.cpp) target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 1cc959c3a7..4fefeb6bbd 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -2,369 +2,9 @@ #include #include -#include +#include "sla_test_utils.hpp" -// Debug -#include - -#include "libslic3r/libslic3r.h" -#include "libslic3r/Format/OBJ.hpp" -#include "libslic3r/SLAPrint.hpp" -#include "libslic3r/TriangleMesh.hpp" -#include "libslic3r/SLA/Pad.hpp" -#include "libslic3r/SLA/SupportTreeBuilder.hpp" -#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" -#include "libslic3r/SLA/SupportPointGenerator.hpp" -#include "libslic3r/SLA/Raster.hpp" -#include "libslic3r/SLA/ConcaveHull.hpp" -#include "libslic3r/MTUtils.hpp" - -#include "libslic3r/SVG.hpp" -#include "libslic3r/Format/OBJ.hpp" - -#if defined(WIN32) || defined(_WIN32) -#define PATH_SEPARATOR R"(\)" -#else -#define PATH_SEPARATOR R"(/)" -#endif - -namespace { -using namespace Slic3r; - -TriangleMesh load_model(const std::string &obj_filename) -{ - TriangleMesh mesh; - auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; - load_obj(fpath.c_str(), &mesh); - return mesh; -} - -enum e_validity { - ASSUME_NO_EMPTY = 1, - ASSUME_MANIFOLD = 2, - ASSUME_NO_REPAIR = 4 -}; - -void check_validity(const TriangleMesh &input_mesh, - int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD | - ASSUME_NO_REPAIR) -{ - TriangleMesh mesh{input_mesh}; - - if (flags & ASSUME_NO_EMPTY) { - REQUIRE_FALSE(mesh.empty()); - } else if (mesh.empty()) - return; // If it can be empty and it is, there is nothing left to do. - - REQUIRE(stl_validate(&mesh.stl)); - - bool do_update_shared_vertices = false; - mesh.repair(do_update_shared_vertices); - - if (flags & ASSUME_NO_REPAIR) { - REQUIRE_FALSE(mesh.needed_repair()); - } - - if (flags & ASSUME_MANIFOLD) { - mesh.require_shared_vertices(); - if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj"); - REQUIRE(mesh.is_manifold()); - } -} - -struct PadByproducts -{ - ExPolygons model_contours; - ExPolygons support_contours; - TriangleMesh mesh; -}; - -void _test_concave_hull(const Polygons &hull, const ExPolygons &polys) -{ - REQUIRE(polys.size() >=hull.size()); - - double polys_area = 0; - for (const ExPolygon &p : polys) polys_area += p.area(); - - double cchull_area = 0; - for (const Slic3r::Polygon &p : hull) cchull_area += p.area(); - - REQUIRE(cchull_area >= Approx(polys_area)); - - size_t cchull_holes = 0; - for (const Slic3r::Polygon &p : hull) - cchull_holes += p.is_clockwise() ? 1 : 0; - - REQUIRE(cchull_holes == 0); - - Polygons intr = diff(to_polygons(polys), hull); - REQUIRE(intr.empty()); -} - -void test_concave_hull(const ExPolygons &polys) { - sla::PadConfig pcfg; - - Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}}; - - _test_concave_hull(cchull.polygons(), polys); - - coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance()); - ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta); - Polygons waffl = sla::offset_waffle_style(cchull, delta); - - _test_concave_hull(to_polygons(wafflex), polys); - _test_concave_hull(waffl, polys); -} - -void test_pad(const std::string & obj_filename, - const sla::PadConfig &padcfg, - PadByproducts & out) -{ - REQUIRE(padcfg.validate().empty()); - - TriangleMesh mesh = load_model(obj_filename); - - REQUIRE_FALSE(mesh.empty()); - - // Create pad skeleton only from the model - Slic3r::sla::pad_blueprint(mesh, out.model_contours); - - test_concave_hull(out.model_contours); - - REQUIRE_FALSE(out.model_contours.empty()); - - // Create the pad geometry for the model contours only - Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); - - check_validity(out.mesh); - - auto bb = out.mesh.bounding_box(); - REQUIRE(bb.max.z() - bb.min.z() == Approx(padcfg.full_height())); -} - -void test_pad(const std::string & obj_filename, - const sla::PadConfig &padcfg = {}) -{ - PadByproducts byproducts; - test_pad(obj_filename, padcfg, byproducts); -} - -struct SupportByproducts -{ - std::string obj_fname; - std::vector slicegrid; - std::vector model_slices; - sla::SupportTreeBuilder supporttree; - TriangleMesh input_mesh; -}; - -const constexpr float CLOSING_RADIUS = 0.005f; - -void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, - const sla::SupportConfig &cfg) -{ - double gnd = stree.ground_level; - double H1 = cfg.max_solo_pillar_height_mm; - double H2 = cfg.max_dual_pillar_height_mm; - - for (const sla::Head &head : stree.heads()) { - REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || - head.bridge_id != sla::ID_UNSET)); - } - - for (const sla::Pillar &pillar : stree.pillars()) { - if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { - double h = pillar.height; - - if (h > H1) REQUIRE(pillar.links >= 1); - else if(h > H2) { REQUIRE(pillar.links >= 2); } - } - - REQUIRE(pillar.links <= cfg.pillar_cascade_neighbors); - REQUIRE(pillar.bridges <= cfg.max_bridges_on_pillar); - } - - double max_bridgelen = 0.; - auto chck_bridge = [&cfg](const sla::Bridge &bridge, double &max_brlen) { - Vec3d n = bridge.endp - bridge.startp; - double d = sla::distance(n); - max_brlen = std::max(d, max_brlen); - - double z = n.z(); - double polar = std::acos(z / d); - double slope = -polar + PI / 2.; - REQUIRE(std::abs(slope) >= cfg.bridge_slope - EPSILON); - }; - - for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); - REQUIRE(max_bridgelen <= cfg.max_bridge_length_mm); - - max_bridgelen = 0; - for (auto &bridge : stree.crossbridges()) chck_bridge(bridge, max_bridgelen); - - double md = cfg.max_pillar_link_distance_mm / std::cos(-cfg.bridge_slope); - REQUIRE(max_bridgelen <= md); -} - -void test_supports(const std::string & obj_filename, - const sla::SupportConfig &supportcfg, - SupportByproducts & out) -{ - using namespace Slic3r; - TriangleMesh mesh = load_model(obj_filename); - - REQUIRE_FALSE(mesh.empty()); - - TriangleMeshSlicer slicer{&mesh}; - - auto bb = mesh.bounding_box(); - double zmin = bb.min.z(); - double zmax = bb.max.z(); - double gnd = zmin - supportcfg.object_elevation_mm; - auto layer_h = 0.05f; - - out.slicegrid = grid(float(gnd), float(zmax), layer_h); - slicer.slice(out.slicegrid , CLOSING_RADIUS, &out.model_slices, []{}); - - // Create the special index-triangle mesh with spatial indexing which - // is the input of the support point and support mesh generators - sla::EigenMesh3D emesh{mesh}; - - // Create the support point generator - sla::SupportPointGenerator::Config autogencfg; - autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); - sla::SupportPointGenerator point_gen{emesh, out.model_slices, - out.slicegrid, autogencfg, - [] {}, [](int) {}}; - - // Get the calculated support points. - std::vector support_points = point_gen.output(); - - int validityflags = ASSUME_NO_REPAIR; - - // If there is no elevation, support points shall be removed from the - // bottom of the object. - if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { - sla::remove_bottom_points(support_points, zmin, - supportcfg.base_height_mm); - } else { - // Should be support points at least on the bottom of the model - REQUIRE_FALSE(support_points.empty()); - - // Also the support mesh should not be empty. - validityflags |= ASSUME_NO_EMPTY; - } - - // Generate the actual support tree - sla::SupportTreeBuilder treebuilder; - treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); - - check_support_tree_integrity(treebuilder, supportcfg); - - const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); - - check_validity(output_mesh, validityflags); - - // Quick check if the dimensions and placement of supports are correct - auto obb = output_mesh.bounding_box(); - - double allowed_zmin = zmin - supportcfg.object_elevation_mm; - - if (std::abs(supportcfg.object_elevation_mm) < EPSILON) - allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; - - REQUIRE(obb.min.z() >= allowed_zmin); - REQUIRE(obb.max.z() <= zmax); - - // Move out the support tree into the byproducts, we can examine it further - // in various tests. - out.obj_fname = std::move(obj_filename); - out.supporttree = std::move(treebuilder); - out.input_mesh = std::move(mesh); -} - -void test_supports(const std::string & obj_filename, - const sla::SupportConfig &supportcfg = {}) -{ - SupportByproducts byproducts; - test_supports(obj_filename, supportcfg, byproducts); -} - -void export_failed_case(const std::vector &support_slices, - const SupportByproducts &byproducts) -{ - for (size_t n = 0; n < support_slices.size(); ++n) { - const ExPolygons &sup_slice = support_slices[n]; - const ExPolygons &mod_slice = byproducts.model_slices[n]; - Polygons intersections = intersection(sup_slice, mod_slice); - - std::stringstream ss; - if (!intersections.empty()) { - ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; - SVG svg(ss.str()); - svg.draw(sup_slice, "green"); - svg.draw(mod_slice, "blue"); - svg.draw(intersections, "red"); - svg.Close(); - } - } - - TriangleMesh m; - byproducts.supporttree.retrieve_full_mesh(m); - m.merge(byproducts.input_mesh); - m.repair(); - m.require_shared_vertices(); - m.WriteOBJFile(byproducts.obj_fname.c_str()); -} - -void test_support_model_collision( - const std::string & obj_filename, - const sla::SupportConfig &input_supportcfg = {}) -{ - SupportByproducts byproducts; - - sla::SupportConfig supportcfg = input_supportcfg; - - // Set head penetration to a small negative value which should ensure that - // the supports will not touch the model body. - supportcfg.head_penetration_mm = -0.15; - - // TODO: currently, the tailheads penetrating into the model body do not - // respect the penetration parameter properly. No issues were reported so - // far but we should definitely fix this. - supportcfg.ground_facing_only = true; - - test_supports(obj_filename, supportcfg, byproducts); - - // Slice the support mesh given the slice grid of the model. - std::vector support_slices = - byproducts.supporttree.slice(byproducts.slicegrid, CLOSING_RADIUS); - - // The slices originate from the same slice grid so the numbers must match - - bool support_mesh_is_empty = - byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && - byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); - - if (support_mesh_is_empty) - REQUIRE(support_slices.empty()); - else - REQUIRE(support_slices.size() == byproducts.model_slices.size()); - - bool notouch = true; - for (size_t n = 0; notouch && n < support_slices.size(); ++n) { - const ExPolygons &sup_slice = support_slices[n]; - const ExPolygons &mod_slice = byproducts.model_slices[n]; - - Polygons intersections = intersection(sup_slice, mod_slice); - - notouch = notouch && intersections.empty(); - } - - if (!notouch) export_failed_case(support_slices, byproducts); - - REQUIRE(notouch); -} +namespace { const char *const BELOW_PAD_TEST_OBJECTS[] = { "20mm_cube.obj", diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp new file mode 100644 index 0000000000..c50aa1f2f2 --- /dev/null +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include +#include + +#include "sla_test_utils.hpp" + +using namespace Slic3r; + +// Create a simple scene with a 20mm cube and a big hole in the front wall +// with 5mm radius. Then shoot rays from interesting positions and see where +// they land. +TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]") +{ + // Load the cube and make it hollow. + TriangleMesh cube = load_model("20mm_cube.obj"); + sla::HollowingConfig hcfg; + std::unique_ptr cube_inside = sla::generate_interior(cube, hcfg); + REQUIRE(cube_inside); + + // Helper bb + auto boxbb = cube.bounding_box(); + + // Create the big 10mm long drainhole in the front wall. + Vec3f center = boxbb.center().cast(); + Vec3f p = {center.x(), 0., center.z()}; + Vec3f normal = {0.f, 1.f, 0.f}; + float radius = 5.f; + float hole_length = 10.; + sla::DrainHoles holes = { sla::DrainHole{p, normal, radius, hole_length} }; + + cube.merge(*cube_inside); + cube.require_shared_vertices(); + + sla::EigenMesh3D emesh{cube}; + emesh.load_holes(holes); + + Vec3d s = center.cast(); + SECTION("Fire from center, should hit the interior wall") { + auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(boxbb.size().x() / 2 - hcfg.min_thickness)); + } + + SECTION("Fire upward from hole center, hit distance equals the radius") { + s.y() = hcfg.min_thickness / 2; + auto hit = emesh.query_ray_hit(s, {0, 0., 1.}); + REQUIRE(hit.distance() == Approx(radius)); + } + + // Shouldn't this hit the inside wall through the hole? + SECTION("Fire from outside, hit the back side of the hole cylinder.") { + s.y() = -1.; + auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(hole_length + 1.f)); + } + + SECTION("Check for support tree correctness") { + test_support_model_collision("20mm_cube.obj", {}, hcfg, holes); + } +} diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp new file mode 100644 index 0000000000..3da28a50e6 --- /dev/null +++ b/tests/sla_print/sla_test_utils.cpp @@ -0,0 +1,297 @@ +#include "sla_test_utils.hpp" + +void test_support_model_collision(const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes) +{ + SupportByproducts byproducts; + + sla::SupportConfig supportcfg = input_supportcfg; + + // Set head penetration to a small negative value which should ensure that + // the supports will not touch the model body. + supportcfg.head_penetration_mm = -0.15; + + // TODO: currently, the tailheads penetrating into the model body do not + // respect the penetration parameter properly. No issues were reported so + // far but we should definitely fix this. + supportcfg.ground_facing_only = true; + + test_supports(obj_filename, supportcfg, hollowingcfg, drainholes, byproducts); + + // Slice the support mesh given the slice grid of the model. + std::vector support_slices = + byproducts.supporttree.slice(byproducts.slicegrid, CLOSING_RADIUS); + + // The slices originate from the same slice grid so the numbers must match + + bool support_mesh_is_empty = + byproducts.supporttree.retrieve_mesh(sla::MeshType::Pad).empty() && + byproducts.supporttree.retrieve_mesh(sla::MeshType::Support).empty(); + + if (support_mesh_is_empty) + REQUIRE(support_slices.empty()); + else + REQUIRE(support_slices.size() == byproducts.model_slices.size()); + + bool notouch = true; + for (size_t n = 0; notouch && n < support_slices.size(); ++n) { + const ExPolygons &sup_slice = support_slices[n]; + const ExPolygons &mod_slice = byproducts.model_slices[n]; + + Polygons intersections = intersection(sup_slice, mod_slice); + + notouch = notouch && intersections.empty(); + } + + /*if (!notouch) */export_failed_case(support_slices, byproducts); + + REQUIRE(notouch); +} + +void export_failed_case(const std::vector &support_slices, const SupportByproducts &byproducts) +{ + for (size_t n = 0; n < support_slices.size(); ++n) { + const ExPolygons &sup_slice = support_slices[n]; + const ExPolygons &mod_slice = byproducts.model_slices[n]; + Polygons intersections = intersection(sup_slice, mod_slice); + + std::stringstream ss; + if (!intersections.empty()) { + ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; + SVG svg(ss.str()); + svg.draw(sup_slice, "green"); + svg.draw(mod_slice, "blue"); + svg.draw(intersections, "red"); + svg.Close(); + } + } + + TriangleMesh m; + byproducts.supporttree.retrieve_full_mesh(m); + m.merge(byproducts.input_mesh); + m.repair(); + m.require_shared_vertices(); + m.WriteOBJFile(byproducts.obj_fname.c_str()); +} + +void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes, + SupportByproducts &out) +{ + using namespace Slic3r; + TriangleMesh mesh = load_model(obj_filename); + + REQUIRE_FALSE(mesh.empty()); + + if (hollowingcfg.enabled) { + auto inside = sla::generate_interior(mesh, hollowingcfg); + REQUIRE(inside); + mesh.merge(*inside); + mesh.require_shared_vertices(); + } + + TriangleMeshSlicer slicer{&mesh}; + + auto bb = mesh.bounding_box(); + double zmin = bb.min.z(); + double zmax = bb.max.z(); + double gnd = zmin - supportcfg.object_elevation_mm; + auto layer_h = 0.05f; + + out.slicegrid = grid(float(gnd), float(zmax), layer_h); + slicer.slice(out.slicegrid , CLOSING_RADIUS, &out.model_slices, []{}); + sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{}); + + // Create the special index-triangle mesh with spatial indexing which + // is the input of the support point and support mesh generators + sla::EigenMesh3D emesh{mesh}; + if (hollowingcfg.enabled) + emesh.load_holes(drainholes); + + // Create the support point generator + sla::SupportPointGenerator::Config autogencfg; + autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); + sla::SupportPointGenerator point_gen{emesh, out.model_slices, out.slicegrid, + autogencfg, [] {}, [](int) {}}; + + // Get the calculated support points. + std::vector support_points = point_gen.output(); + + int validityflags = ASSUME_NO_REPAIR; + + // If there is no elevation, support points shall be removed from the + // bottom of the object. + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) { + sla::remove_bottom_points(support_points, zmin, + supportcfg.base_height_mm); + } else { + // Should be support points at least on the bottom of the model + REQUIRE_FALSE(support_points.empty()); + + // Also the support mesh should not be empty. + validityflags |= ASSUME_NO_EMPTY; + } + + // Generate the actual support tree + sla::SupportTreeBuilder treebuilder; + treebuilder.build(sla::SupportableMesh{emesh, support_points, supportcfg}); + + check_support_tree_integrity(treebuilder, supportcfg); + + const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); + + check_validity(output_mesh, validityflags); + + // Quick check if the dimensions and placement of supports are correct + auto obb = output_mesh.bounding_box(); + + double allowed_zmin = zmin - supportcfg.object_elevation_mm; + + if (std::abs(supportcfg.object_elevation_mm) < EPSILON) + allowed_zmin = zmin - 2 * supportcfg.head_back_radius_mm; + + REQUIRE(obb.min.z() >= allowed_zmin); + REQUIRE(obb.max.z() <= zmax); + + // Move out the support tree into the byproducts, we can examine it further + // in various tests. + out.obj_fname = std::move(obj_filename); + out.supporttree = std::move(treebuilder); + out.input_mesh = std::move(mesh); +} + +void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, + const sla::SupportConfig &cfg) +{ + double gnd = stree.ground_level; + double H1 = cfg.max_solo_pillar_height_mm; + double H2 = cfg.max_dual_pillar_height_mm; + + for (const sla::Head &head : stree.heads()) { + REQUIRE((!head.is_valid() || head.pillar_id != sla::ID_UNSET || + head.bridge_id != sla::ID_UNSET)); + } + + for (const sla::Pillar &pillar : stree.pillars()) { + if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { + double h = pillar.height; + + if (h > H1) REQUIRE(pillar.links >= 1); + else if(h > H2) { REQUIRE(pillar.links >= 2); } + } + + REQUIRE(pillar.links <= cfg.pillar_cascade_neighbors); + REQUIRE(pillar.bridges <= cfg.max_bridges_on_pillar); + } + + double max_bridgelen = 0.; + auto chck_bridge = [&cfg](const sla::Bridge &bridge, double &max_brlen) { + Vec3d n = bridge.endp - bridge.startp; + double d = sla::distance(n); + max_brlen = std::max(d, max_brlen); + + double z = n.z(); + double polar = std::acos(z / d); + double slope = -polar + PI / 2.; + REQUIRE(std::abs(slope) >= cfg.bridge_slope - EPSILON); + }; + + for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); + REQUIRE(max_bridgelen <= cfg.max_bridge_length_mm); + + max_bridgelen = 0; + for (auto &bridge : stree.crossbridges()) chck_bridge(bridge, max_bridgelen); + + double md = cfg.max_pillar_link_distance_mm / std::cos(-cfg.bridge_slope); + REQUIRE(max_bridgelen <= md); +} + +void test_pad(const std::string &obj_filename, const sla::PadConfig &padcfg, PadByproducts &out) +{ + REQUIRE(padcfg.validate().empty()); + + TriangleMesh mesh = load_model(obj_filename); + + REQUIRE_FALSE(mesh.empty()); + + // Create pad skeleton only from the model + Slic3r::sla::pad_blueprint(mesh, out.model_contours); + + test_concave_hull(out.model_contours); + + REQUIRE_FALSE(out.model_contours.empty()); + + // Create the pad geometry for the model contours only + Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); + + check_validity(out.mesh); + + auto bb = out.mesh.bounding_box(); + REQUIRE(bb.max.z() - bb.min.z() == Approx(padcfg.full_height())); +} + +static void _test_concave_hull(const Polygons &hull, const ExPolygons &polys) +{ + REQUIRE(polys.size() >=hull.size()); + + double polys_area = 0; + for (const ExPolygon &p : polys) polys_area += p.area(); + + double cchull_area = 0; + for (const Slic3r::Polygon &p : hull) cchull_area += p.area(); + + REQUIRE(cchull_area >= Approx(polys_area)); + + size_t cchull_holes = 0; + for (const Slic3r::Polygon &p : hull) + cchull_holes += p.is_clockwise() ? 1 : 0; + + REQUIRE(cchull_holes == 0); + + Polygons intr = diff(to_polygons(polys), hull); + REQUIRE(intr.empty()); +} + +void test_concave_hull(const ExPolygons &polys) { + sla::PadConfig pcfg; + + Slic3r::sla::ConcaveHull cchull{polys, pcfg.max_merge_dist_mm, []{}}; + + _test_concave_hull(cchull.polygons(), polys); + + coord_t delta = scaled(pcfg.brim_size_mm + pcfg.wing_distance()); + ExPolygons wafflex = sla::offset_waffle_style_ex(cchull, delta); + Polygons waffl = sla::offset_waffle_style(cchull, delta); + + _test_concave_hull(to_polygons(wafflex), polys); + _test_concave_hull(waffl, polys); +} + +void check_validity(const TriangleMesh &input_mesh, int flags) +{ + TriangleMesh mesh{input_mesh}; + + if (flags & ASSUME_NO_EMPTY) { + REQUIRE_FALSE(mesh.empty()); + } else if (mesh.empty()) + return; // If it can be empty and it is, there is nothing left to do. + + REQUIRE(stl_validate(&mesh.stl)); + + bool do_update_shared_vertices = false; + mesh.repair(do_update_shared_vertices); + + if (flags & ASSUME_NO_REPAIR) { + REQUIRE_FALSE(mesh.needed_repair()); + } + + if (flags & ASSUME_MANIFOLD) { + mesh.require_shared_vertices(); + if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj"); + REQUIRE(mesh.is_manifold()); + } +} diff --git a/tests/sla_print/sla_test_utils.hpp b/tests/sla_print/sla_test_utils.hpp new file mode 100644 index 0000000000..dcb4934ef6 --- /dev/null +++ b/tests/sla_print/sla_test_utils.hpp @@ -0,0 +1,112 @@ +#ifndef SLA_TEST_UTILS_HPP +#define SLA_TEST_UTILS_HPP + +#include +#include + +// Debug +#include + +#include "libslic3r/libslic3r.h" +#include "libslic3r/Format/OBJ.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/SLA/Pad.hpp" +#include "libslic3r/SLA/SupportTreeBuilder.hpp" +#include "libslic3r/SLA/SupportTreeBuildsteps.hpp" +#include "libslic3r/SLA/SupportPointGenerator.hpp" +#include "libslic3r/SLA/Raster.hpp" +#include "libslic3r/SLA/ConcaveHull.hpp" +#include "libslic3r/MTUtils.hpp" + +#include "libslic3r/SVG.hpp" +#include "libslic3r/Format/OBJ.hpp" + +using namespace Slic3r; + +enum e_validity { + ASSUME_NO_EMPTY = 1, + ASSUME_MANIFOLD = 2, + ASSUME_NO_REPAIR = 4 +}; + +void check_validity(const TriangleMesh &input_mesh, + int flags = ASSUME_NO_EMPTY | ASSUME_MANIFOLD | + ASSUME_NO_REPAIR); + +struct PadByproducts +{ + ExPolygons model_contours; + ExPolygons support_contours; + TriangleMesh mesh; +}; + +void test_concave_hull(const ExPolygons &polys); + +void test_pad(const std::string & obj_filename, + const sla::PadConfig &padcfg, + PadByproducts & out); + +inline void test_pad(const std::string & obj_filename, + const sla::PadConfig &padcfg = {}) +{ + PadByproducts byproducts; + test_pad(obj_filename, padcfg, byproducts); +} + +struct SupportByproducts +{ + std::string obj_fname; + std::vector slicegrid; + std::vector model_slices; + sla::SupportTreeBuilder supporttree; + TriangleMesh input_mesh; +}; + +const constexpr float CLOSING_RADIUS = 0.005f; + +void check_support_tree_integrity(const sla::SupportTreeBuilder &stree, + const sla::SupportConfig &cfg); + +void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes, + SupportByproducts &out); + +inline void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg, + SupportByproducts &out) +{ + sla::HollowingConfig hcfg; + hcfg.enabled = false; + test_supports(obj_filename, supportcfg, hcfg, {}, out); +} + +inline void test_supports(const std::string &obj_filename, + const sla::SupportConfig &supportcfg = {}) +{ + SupportByproducts byproducts; + test_supports(obj_filename, supportcfg, byproducts); +} + +void export_failed_case(const std::vector &support_slices, + const SupportByproducts &byproducts); + + +void test_support_model_collision( + const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg, + const sla::HollowingConfig &hollowingcfg, + const sla::DrainHoles &drainholes); + +inline void test_support_model_collision( + const std::string &obj_filename, + const sla::SupportConfig &input_supportcfg = {}) +{ + sla::HollowingConfig hcfg; + hcfg.enabled = false; + test_support_model_collision(obj_filename, input_supportcfg, hcfg, {}); +} + +#endif // SLA_TEST_UTILS_HPP diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp new file mode 100644 index 0000000000..b129cc79f1 --- /dev/null +++ b/tests/test_utils.hpp @@ -0,0 +1,21 @@ +#ifndef SLIC3R_TEST_UTILS +#define SLIC3R_TEST_UTILS + +#include +#include + +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR R"(\)" +#else +#define PATH_SEPARATOR R"(/)" +#endif + +inline Slic3r::TriangleMesh load_model(const std::string &obj_filename) +{ + Slic3r::TriangleMesh mesh; + auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename; + Slic3r::load_obj(fpath.c_str(), &mesh); + return mesh; +} + +#endif // SLIC3R_TEST_UTILS From 578fcbc37c116950cfb9a1d412fe761e1973f371 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 8 Jan 2020 17:12:06 +0100 Subject: [PATCH 092/130] Performance improvements in raycaster --- src/libslic3r/SLA/Common.cpp | 19 +++++++++++-------- src/libslic3r/SLA/EigenMesh3D.hpp | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index 3710bf3bed..a0fc2f3bb6 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -333,20 +333,23 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( bool entry; }; std::vector hole_isects; + hole_isects.reserve(m_holes.size()); + + auto sf = s.cast(); + auto dirf = dir.cast(); // Collect hits on all holes, preserve information about entry/exit for (const sla::DrainHole& hole : m_holes) { std::array, 2> isects; - if (hole.get_intersections(s.cast(), - dir.cast(), isects)) { - hole_isects.emplace_back(isects[0].first, isects[0].second, true); - hole_isects.emplace_back(isects[1].first, isects[1].second, false); + if (hole.get_intersections(sf, dirf, isects)) { + if (isects[0].first > 0.f) hole_isects.emplace_back(isects[0].first, isects[0].second, true); + if (isects[1].first > 0.f) hole_isects.emplace_back(isects[1].first, isects[1].second, false); } } - // Remove hole hits behind the source - for (int i=0; i Date: Tue, 7 Jan 2020 14:52:04 +0100 Subject: [PATCH 093/130] Switched order of hollowing and support points gizmos --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 15 ++++++++------- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 3 ++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 2501a9633b..8300d74794 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -85,13 +85,14 @@ bool GLGizmosManager::init() m_common_gizmos_data.reset(new CommonGizmosData()); + // Order of gizmos in the vector must match order in EType! m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, "move.svg", 0)); m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); - m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5, m_common_gizmos_data.get())); - m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 6, m_common_gizmos_data.get())); + m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", 5, m_common_gizmos_data.get())); + m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 6, m_common_gizmos_data.get())); for (auto& gizmo : m_gizmos) { if (! gizmo->init()) { @@ -524,10 +525,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) case Scale: { // Apply new temporary scale factors - TransformationType transformation_type(TransformationType::Local_Absolute_Joint); - if (evt.AltDown()) - transformation_type.set_independent(); - selection.scale(get_scale(), transformation_type); + TransformationType transformation_type(TransformationType::Local_Absolute_Joint); + if (evt.AltDown()) + transformation_type.set_independent(); + selection.scale(get_scale(), transformation_type); if (evt.ControlDown()) selection.translate(get_scale_offset(), true); wxGetApp().obj_manipul()->set_dirty(); @@ -706,7 +707,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) { if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ManualEditing)) processed = true; - + break; } case 'F': diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index bab084cd24..2110e7b69b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -54,13 +54,14 @@ public: enum EType : unsigned char { + // Order must match index in m_gizmos! Move, Scale, Rotate, Flatten, Cut, - SlaSupports, Hollow, + SlaSupports, Undefined }; From e159344ce50d5d59f8e594b3cd489873f0be7e59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 9 Jan 2020 11:22:24 +0100 Subject: [PATCH 094/130] further fixes for X window crashes. --- sandboxes/opencsg/main.cpp | 39 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index 82efb8ada5..ab379c196e 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -91,30 +91,7 @@ class Canvas: public wxGLCanvas public: template - Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) - { - Bind(wxEVT_PAINT, [this](wxPaintEvent &) { - // This is required even though dc is not used otherwise. - wxPaintDC dc(this); - - // Set the OpenGL viewport according to the client size of this - // canvas. This is done here rather than in a wxSizeEvent handler - // because our OpenGL rendering context (and thus viewport - // setting) is used with multiple canvases: If we updated the - // viewport in the wxSizeEvent handler, changing the size of one - // canvas causes a viewport setting that is wrong when next - // another canvas is repainted. - const wxSize ClientSize = GetClientSize(); - - m_display->set_screen_size(ClientSize.x, ClientSize.y); - }); - - Bind(wxEVT_SIZE, [this](wxSizeEvent &) { - const wxSize ClientSize = GetClientSize(); - m_display->set_screen_size(ClientSize.x, ClientSize.y); - m_display->repaint(); - }); - } + Canvas(Args &&...args): wxGLCanvas(std::forward(args)...) {} shptr get_display() const { return m_display; } @@ -466,6 +443,20 @@ void MyFrame::activate_canvas_display() m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y); enable_multisampling(m_ms_toggle->GetValue()); + m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { + // This is required even though dc is not used otherwise. + wxPaintDC dc(this); + const wxSize csize = GetClientSize(); + m_canvas->get_display()->set_screen_size(csize.x, csize.y); + m_canvas->get_display()->repaint(); + }); + + m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent &) { + const wxSize csize = GetClientSize(); + m_canvas->get_display()->set_screen_size(csize.x, csize.y); + m_canvas->get_display()->repaint(); + }); + // Do the repaint continuously m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) { m_canvas->get_display()->repaint(); From e9d340c87f846d1095b181bb55f740b163d5f1d2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Jan 2020 10:03:33 +0100 Subject: [PATCH 095/130] Fixed transformations of the hollowed GLVolume --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 7 +++---- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 3 +++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 44894a0fb7..048392b450 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -123,6 +123,9 @@ void GLGizmoHollow::on_render() const GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; glcheck(); m_c->m_volume_with_cavity->set_render_color(); + const Geometry::Transformation& volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); + m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } @@ -629,13 +632,9 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) } // create a new GLVolume that only has the cavity inside - Geometry::Transformation volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); - volume_trafo.set_offset(volume_trafo.get_offset()); m_c->m_volume_with_cavity.reset(new GLVolume(GLVolume::MODEL_COLOR[2])); m_c->m_volume_with_cavity->indexed_vertex_array.load_mesh(*m_c->m_cavity_mesh.get()); m_c->m_volume_with_cavity->finalize_geometry(true); - m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); - m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->force_transparent = false; // Reset raycaster so it works with the new mesh: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index cb9dc27539..2dc41edcac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -133,6 +133,9 @@ void GLGizmoSlaSupports::on_render() const GLint print_box_worldmatrix_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; glcheck(); m_c->m_volume_with_cavity->set_render_color(); + const Geometry::Transformation& volume_trafo = m_c->m_model_object->volumes.front()->get_transformation(); + m_c->m_volume_with_cavity->set_volume_transformation(volume_trafo); + m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } From f22961edaed244337f463fefad1634a0883c727f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 9 Jan 2020 14:06:39 +0100 Subject: [PATCH 096/130] Fixed a raycaster problem with handling duplicate hits from igl The duplicate hits confused winding number calculations in the raycaster, which in turn returned incorrect hit. --- src/libslic3r/SLA/Common.cpp | 14 +++++++++----- tests/sla_print/sla_raycast_tests.cpp | 3 +-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/SLA/Common.cpp b/src/libslic3r/SLA/Common.cpp index a0fc2f3bb6..3d31c55226 100644 --- a/src/libslic3r/SLA/Common.cpp +++ b/src/libslic3r/SLA/Common.cpp @@ -297,7 +297,14 @@ EigenMesh3D::query_ray_hits(const Vec3d &s, const Vec3d &dir) const // The sort is necessary, the hits are not always sorted. std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; }); - + + // Remove duplicates. They sometimes appear, for example when the ray is cast + // along an axis of a cube due to floating-point approximations in igl (?) + hits.erase(std::unique(hits.begin(), hits.end(), + [](const igl::Hit& a, const igl::Hit& b) + { return a.t == b.t; }), + hits.end()); + // Convert the igl::Hit into hit_result outs.reserve(hits.size()); for (const igl::Hit& hit : hits) { @@ -342,14 +349,11 @@ EigenMesh3D::hit_result EigenMesh3D::filter_hits( for (const sla::DrainHole& hole : m_holes) { std::array, 2> isects; if (hole.get_intersections(sf, dirf, isects)) { + // Ignore hole hits behind the source if (isects[0].first > 0.f) hole_isects.emplace_back(isects[0].first, isects[0].second, true); if (isects[1].first > 0.f) hole_isects.emplace_back(isects[1].first, isects[1].second, false); } } -// // Remove hole hits behind the source -// for (int i=0; i Date: Thu, 9 Jan 2020 16:57:11 +0100 Subject: [PATCH 097/130] more raycaster tests, without repeating the hollowing every time --- tests/sla_print/sla_raycast_tests.cpp | 40 +++++++++++++++------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp index c60f4c8ee0..4a994f2a93 100644 --- a/tests/sla_print/sla_raycast_tests.cpp +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -37,24 +37,28 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]") emesh.load_holes(holes); Vec3d s = center.cast(); - SECTION("Fire from center, should hit the interior wall") { - auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); - REQUIRE(hit.distance() == Approx(boxbb.size().x() / 2 - hcfg.min_thickness)); - } + // Fire from center, should hit the interior wall + auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(boxbb.size().x() / 2 - hcfg.min_thickness)); - SECTION("Fire upward from hole center, hit distance equals the radius") { - s.y() = hcfg.min_thickness / 2; - auto hit = emesh.query_ray_hit(s, {0, 0., 1.}); - REQUIRE(hit.distance() == Approx(radius)); - } + // Fire upward from hole center, hit distance equals the radius (hits the + // side of the hole cut. + s.y() = hcfg.min_thickness / 2; + hit = emesh.query_ray_hit(s, {0, 0., 1.}); + REQUIRE(hit.distance() == Approx(radius)); + + // Fire from outside, hit the back side of the cube interior + s.y() = -1.; + hit = emesh.query_ray_hit(s, {0, 1., 0.}); + REQUIRE(hit.distance() == Approx(boxbb.max.y() - hcfg.min_thickness - s.y())); - SECTION("Fire from outside, hit the back side of the hole cylinder.") { - s.y() = -1.; - auto hit = emesh.query_ray_hit(s, {0, 1., 0.}); - REQUIRE(hit.distance() == Approx(boxbb.size().y() - hcfg.min_thickness + 1.)); - } - - SECTION("Check for support tree correctness") { - test_support_model_collision("20mm_cube.obj", {}, hcfg, holes); - } + // Fire downwards from above the hole cylinder. Has to go through the cyl. + // as it was not there. + s = center.cast(); + s.z() = boxbb.max.z() - hcfg.min_thickness - 1.; + hit = emesh.query_ray_hit(s, {0, 0., -1.}); + REQUIRE(hit.distance() == Approx(s.z() - boxbb.min.z() - hcfg.min_thickness)); + + // Check for support tree correctness + test_support_model_collision("20mm_cube.obj", {}, hcfg, holes); } From d0d73e610979ec6774f38f9d7d2576e91ccf96bf Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 10:33:10 +0100 Subject: [PATCH 098/130] Hollowing config values contain min/max values, these are respected when setting through the gizmo Rendering and hole transformation fixes (still WIP, though) --- src/libslic3r/PrintConfig.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 91 +++++++++++----- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 20 +--- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 106 ++++++++++--------- 4 files changed, 128 insertions(+), 93 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 89d256a90a..00c33dcfba 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2887,8 +2887,9 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Minimum wall thickness of a hollowed model."); def->sidetext = L("mm"); def->min = 1; + def->max = 10; def->mode = comSimple; - def->set_default_value(new ConfigOptionFloat(4)); + def->set_default_value(new ConfigOptionFloat(3.)); def = this->add("hollowing_quality", coFloat); def->label = L("Hollowing accuracy"); @@ -2904,6 +2905,7 @@ void PrintConfigDef::init_sla_params() def->category = L("Hollowing"); def->tooltip = L(""); def->min = 0; + def->max = 10; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(2.0)); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 048392b450..efb1101995 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -443,7 +443,13 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos std::pair pos_and_normal; if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add drainage hole"))); - m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, + + Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + Vec3f normal_transformed(pos_and_normal.second(0)/scaling(0), + pos_and_normal.second(1)/scaling(1), + pos_and_normal.second(2)/scaling(2)); + + m_c->m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second/* normal_transformed.normalized()*/, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); m_selected.push_back(false); assert(m_selected.size() == m_c->m_model_object->sla_drain_holes.size()); @@ -580,10 +586,10 @@ std::pair GLGizmoHollow::get_hollowi { // FIXME this function is probably obsolete, caller could // get the data from model config himself - std::vector opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); - double offset = static_cast(opts[0])->value; - double quality = static_cast(opts[1])->value; - double closing_d = static_cast(opts[2])->value; + auto opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); + double offset = static_cast(opts[0].first)->value; + double quality = static_cast(opts[1].first)->value; + double closing_d = static_cast(opts[2].first)->value; return std::make_pair(m_c->m_mesh, sla::HollowingConfig{offset, quality, closing_d}); } @@ -611,16 +617,19 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) if (! m_c->m_model_object->sla_drain_holes.empty()) { TriangleMesh holes_mesh; for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { - TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/8); + TriangleMesh hole_mesh = make_cylinder(hole.radius, hole.height, 2*M_PI/32); + + Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); + Vec3d normal_transformed = Vec3d(hole.normal(0)/scaling(0), hole.normal(1)/scaling(1), hole.normal(2)/scaling(2)); + normal_transformed.normalize(); // Rotate the cylinder appropriately - Eigen::Quaternionf q; - Transform3f m = Transform3f::Identity(); - m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3f::UnitZ(), hole.normal).toRotationMatrix(); - hole_mesh.transform(m.cast()); + Eigen::Quaterniond q; + Transform3d m = Transform3d::Identity(); + m.matrix().block(0, 0, 3, 3) = q.setFromTwoVectors(Vec3d::UnitZ(), normal_transformed).toRotationMatrix(); + hole_mesh.transform(m); // If the instance is scaled, undo the scaling of the hole - Vec3d scaling = m_c->m_model_object->instances[m_c->m_active_instance]->get_scaling_factor(); hole_mesh.scale(Vec3d(1/scaling(0), 1/scaling(1), 1/scaling(2))); // Translate the hole into position and merge with the others @@ -647,9 +656,9 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) } } -std::vector GLGizmoHollow::get_config_options(const std::vector& keys) const +std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const { - std::vector out; + std::vector> out; if (!m_c->m_model_object) return out; @@ -660,14 +669,14 @@ std::vector GLGizmoHollow::get_config_options(const std::ve for (const std::string& key : keys) { if (object_cfg.has(key)) - out.push_back(object_cfg.option(key)); + out.emplace_back(object_cfg.option(key), &object_cfg.def()->options.at(key)); // at() needed for const map else if (print_cfg.has(key)) - out.push_back(print_cfg.option(key)); + out.emplace_back(print_cfg.option(key), &print_cfg.def()->options.at(key)); else { // we must get it from defaults if (default_cfg == nullptr) default_cfg.reset(DynamicPrintConfig::new_from_defaults_keys(keys)); - out.push_back(default_cfg->option(key)); + out.emplace_back(default_cfg->option(key), &default_cfg->def()->options.at(key)); } } @@ -714,8 +723,8 @@ RENDER_AGAIN: window_width = std::max(std::max(window_width, /*buttons_width_approx*/0.f), 0.f); { - std::vector opts = get_config_options({"hollowing_enable"}); - m_enable_hollowing = static_cast(opts[0])->value; + auto opts = get_config_options({"hollowing_enable"}); + m_enable_hollowing = static_cast(opts[0].first)->value; if (m_imgui->checkbox(m_desc["enable"], m_enable_hollowing)) { m_c->m_model_object->config.opt("hollowing_enable", true)->value = m_enable_hollowing; wxGetApp().obj_list()->update_and_show_object_settings_item(); @@ -727,29 +736,57 @@ RENDER_AGAIN: if (m_imgui->button(m_desc["preview"])) hollow_mesh(); - std::vector opts = get_config_options({"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}); - float offset = static_cast(opts[0])->value; - float quality = static_cast(opts[1])->value; - float closing_d = static_cast(opts[2])->value; + std::vector opts_keys = {"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}; + auto opts = get_config_options(opts_keys); + auto* offset_cfg = static_cast(opts[0].first); + float offset = offset_cfg->value; + double offset_min = opts[0].second->min; + double offset_max = opts[0].second->max; + + auto* quality_cfg = static_cast(opts[1].first); + float quality = quality_cfg->value; + double quality_min = opts[1].second->min; + double quality_max = opts[1].second->max; + + auto* closing_d_cfg = static_cast(opts[2].first); + float closing_d = closing_d_cfg->value; + double closing_d_min = opts[2].second->min; + double closing_d_max = opts[2].second->max; + m_imgui->text(m_desc.at("offset")); ImGui::SameLine(settings_sliders_left); ImGui::PushItemWidth(window_width - settings_sliders_left); - ImGui::SliderFloat(" ", &offset, 0.f, 5.f, "%.1f"); + ImGui::SliderFloat(" ", &offset, offset_min, offset_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[0].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider m_imgui->text(m_desc.at("quality")); ImGui::SameLine(settings_sliders_left); - ImGui::SliderFloat(" ", &quality, 0.f, 1.f, "%.1f"); + ImGui::SliderFloat(" ", &quality, quality_min, quality_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[1].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } slider_clicked |= ImGui::IsItemClicked(); slider_edited |= ImGui::IsItemEdited(); slider_released |= ImGui::IsItemDeactivatedAfterEdit(); m_imgui->text(m_desc.at("closing_distance")); ImGui::SameLine(settings_sliders_left); - ImGui::SliderFloat(" ", &closing_d, 0.f, 10.f, "%.1f"); + ImGui::SliderFloat(" ", &closing_d, closing_d_min, closing_d_max, "%.1f"); + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(_(opts[2].second->tooltip).ToUTF8()); + ImGui::EndTooltip(); + } slider_clicked |= ImGui::IsItemClicked(); slider_edited |= ImGui::IsItemEdited(); slider_released |= ImGui::IsItemDeactivatedAfterEdit(); @@ -955,8 +992,8 @@ void GLGizmoHollow::on_set_state() m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. - const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; - m_new_hole_radius = static_cast(cfg.option("support_head_front_diameter"))->value; + //const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; + //m_new_hole_radius = static_cast(cfg.option("support_head_front_diameter"))->value; } if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index bea3960973..ba2935a561 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -21,10 +21,6 @@ enum class SLAGizmoEventType : unsigned char; class GLGizmoHollow : public GLGizmoBase { private: - //ModelObject* m_model_object = nullptr; - //ObjectID m_model_object_id = 0; - //int m_active_instance = -1; - //float m_active_instance_bb_radius; // to cache the bb mutable double m_z_shift = 0.; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal); @@ -32,13 +28,6 @@ private: GLUquadricObj* m_quadric; - //std::unique_ptr m_mesh_raycaster; - //std::unique_ptr m_cavity_mesh; - //std::unique_ptr m_volume_with_cavity; - //const TriangleMesh* m_mesh; - //mutable int m_old_timestamp = -1; - //mutable int m_print_object_idx = -1; - //mutable int m_print_objects_count = -1; public: GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, CommonGizmosData* cd); @@ -69,7 +58,7 @@ private: bool unsaved_changes() const; bool m_show_supports = true; - float m_new_hole_radius; // Size of a new hole. + float m_new_hole_radius = 4.f; // Size of a new hole. float m_new_hole_height = 5.f; mutable std::vector m_selected; // which holes are currently selected @@ -77,7 +66,7 @@ private: // Stashes to keep data for undo redo. Is taken after the editing // is done, the data are updated continuously. - float m_offset_stash = 2.0f; + float m_offset_stash = 3.0f; float m_quality_stash = 0.5f; float m_closing_d_stash = 2.f; Vec3f m_hole_before_drag = Vec3f::Zero(); @@ -100,10 +89,7 @@ private: bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - //mutable std::unique_ptr m_object_clipper; - //mutable std::unique_ptr m_supports_clipper; - - std::vector get_config_options(const std::vector& keys) const; + std::vector> get_config_options(const std::vector& keys) const; bool is_mesh_point_clipped(const Vec3d& point) const; // Methods that do the model_object and editing cache synchronization, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 2dc41edcac..4e00b0f77f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -68,19 +68,31 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S return; } - if (m_c->m_model_object != model_object || m_c->m_model_object_id != model_object->id()) { + bool something_changed = false; + + if (m_c->m_model_object != model_object + || m_c->m_model_object_id != model_object->id() + || m_c->m_active_instance != selection.get_instance_idx()) { m_c->m_model_object = model_object; m_c->m_print_object_idx = -1; + m_c->m_active_instance = selection.get_instance_idx(); + something_changed = true; } - m_c->m_active_instance = selection.get_instance_idx(); - if (model_object && selection.is_from_single_instance()) { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. - //FIXME calling ModelObject::instance_bounding_box() is expensive! - m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); + if (something_changed) { + m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); + } + else + m_parent.toggle_model_objects_visibility(true, nullptr, -1); + } if (is_mesh_update_necessary()) { update_mesh(); @@ -90,14 +102,6 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S // If we triggered autogeneration before, check backend and fetch results if they are there if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); - - if (m_state == On) { - m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); - } - else - m_parent.toggle_model_objects_visibility(true, nullptr, -1); } } @@ -350,41 +354,42 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: -// render_color[0] = 0.7f; -// render_color[1] = 0.7f; -// render_color[2] = 0.7f; -// render_color[3] = 0.7f; -// glsafe(::glColor4fv(render_color)); -// for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { -// // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. -// glsafe(::glPushMatrix()); -// glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); -// glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); + if (! m_c->m_cavity_mesh) { + render_color[0] = 0.7f; + render_color[1] = 0.7f; + render_color[2] = 0.7f; + render_color[3] = 0.7f; + glsafe(::glColor4fv(render_color)); + for (const sla::DrainHole& drain_hole : m_c->m_model_object->sla_drain_holes) { + // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. + glsafe(::glPushMatrix()); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); + glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); -// if (vol->is_left_handed()) -// glFrontFace(GL_CW); + if (vol->is_left_handed()) + glFrontFace(GL_CW); -// // Matrices set, we can render the point mark now. + // Matrices set, we can render the point mark now. -// Eigen::Quaterniond q; -// q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); -// Eigen::AngleAxisd aa(q); -// glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); -// glsafe(::glPushMatrix()); -// glsafe(::glTranslated(0., 0., -drain_hole.height)); -// ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); -// glsafe(::glTranslated(0., 0., drain_hole.height)); -// ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); -// glsafe(::glTranslated(0., 0., -drain_hole.height)); -// glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); -// ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); -// glsafe(::glPopMatrix()); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); + Eigen::AngleAxisd aa(q); + glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); + glsafe(::glPushMatrix()); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glPopMatrix()); -// if (vol->is_left_handed()) -// glFrontFace(GL_CCW); -// glsafe(::glPopMatrix()); - -// } + if (vol->is_left_handed()) + glFrontFace(GL_CCW); + glsafe(::glPopMatrix()); + } + } if (!picking) glsafe(::glDisable(GL_LIGHTING)); @@ -454,10 +459,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { // Check whether the hit is in a hole bool in_hole = false; - for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { - if (hole.is_inside(hit)) { - in_hole = true; - break; + // In case the hollowed and drilled mesh is available, we can allow + // placing points in holes, because they should never end up + // on surface that's been drilled away. + if (! m_c->m_cavity_mesh) { + for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { + if (hole.is_inside(hit)) { + in_hole = true; + break; + } } } if (! in_hole) { From 45220e26c0783652abbc27fc93c7ef97f873301b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 10 Jan 2020 11:31:30 +0100 Subject: [PATCH 099/130] Fix zero elevation support maneuvers and comment to clarify the alg. --- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 63 ++++++++++----------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 18 ++++++ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index 68afb73919..b2570570dc 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -561,8 +561,8 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, long head_id) { // People were killed for this number (seriously) - static const double SQR2 = std::sqrt(2.0); static const Vec3d DOWN = {0.0, 0.0, -1.0}; + const double SLOPE = 1. / std::cos(m_cfg.bridge_slope); double gndlvl = m_builder.ground_level; Vec3d endp = {jp(X), jp(Y), gndlvl}; @@ -573,38 +573,47 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, bool can_add_base = true; bool normal_mode = true; + // If in zero elevation mode and the pillar is too close to the model body, + // the support pillar can not be placed in the gap between the model and + // the pad, and the pillar bases must not touch the model body either. + // To solve this, a corrector bridge is inserted between the starting point + // (jp) and the new pillar. if (m_cfg.object_elevation_mm < EPSILON && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) { // Get the distance from the mesh. This can be later optimized // to get the distance in 2D plane because we are dealing with // the ground level only. - - normal_mode = false; - double mind = min_dist - dist; - double azimuth = std::atan2(sourcedir(Y), sourcedir(X)); - double sinpolar = std::sin(PI - m_cfg.bridge_slope); - double cospolar = std::cos(PI - m_cfg.bridge_slope); - double cosazm = std::cos(azimuth); - double sinazm = std::sin(azimuth); - - auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar) - .normalized(); + + normal_mode = false; + + // The min distance needed to move away from the model in XY plane. + double mind = min_dist - dist; + + // get a suitable direction for the corrector bridge. It is the + // original sourcedir's azimuth but the polar angle is saturated to the + // configured bridge slope. + auto [polar, azimuth] = dir_to_spheric(sourcedir); + polar = PI - m_cfg.bridge_slope; + auto dir = spheric_to_dir(polar, azimuth).normalized(); using namespace libnest2d::opt; StopCriteria scr; scr.stop_score = min_dist; SubplexOptimizer solver(scr); + // Search for a distance along the corrector bridge to move the endpoint + // sufficiently away form the model body. The first few optimization + // cycles should succeed here. auto result = solver.optimize_max( [this, dir, jp, gndlvl](double mv) { - Vec3d endpt = jp + SQR2 * mv * dir; + Vec3d endpt = jp + mv * dir; endpt(Z) = gndlvl; return std::sqrt(m_mesh.squared_distance(endpt)); }, - initvals(mind), bound(0.0, 2 * min_dist)); + initvals(SLOPE * mind), bound(0.0, 2 * SLOPE * min_dist)); - mind = std::get<0>(result.optimum); - endp = jp + SQR2 * mind * dir; + mind = std::get<0>(result.optimum); + endp = jp + SLOPE * mind * dir; Vec3d pgnd = {endp(X), endp(Y), gndlvl}; can_add_base = result.score > min_dist; @@ -623,7 +632,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, else { // If the new endpoint is below ground, do not make a pillar if (endp(Z) < gndlvl) - endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off + endp = endp - SLOPE * (gndlvl - endp(Z)) * dir; // back off else { auto hit = bridge_mesh_intersect(endp, DOWN, radius); @@ -708,10 +717,7 @@ void SupportTreeBuildsteps::filter() // (Quaternion::FromTwoVectors) and apply the rotation to the // arrow head. - double z = n(2); - double r = 1.0; // for normalized vector - double polar = std::acos(z / r); - double azimuth = std::atan2(n(1), n(0)); + auto [polar, azimuth] = dir_to_spheric(n); // skip if the tilt is not sane if(polar >= PI - m_cfg.normal_cutoff_angle) { @@ -729,9 +735,7 @@ void SupportTreeBuildsteps::filter() double pin_r = double(m_support_pts[fidx].head_front_radius); // Reassemble the now corrected normal - auto nn = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); + auto nn = spheric_to_dir(polar, azimuth).normalized(); // check available distance EigenMesh3D::hit_result t @@ -757,9 +761,7 @@ void SupportTreeBuildsteps::filter() auto oresult = solver.optimize_max( [this, pin_r, w, hp](double plr, double azm) { - auto dir = Vec3d(std::cos(azm) * std::sin(plr), - std::sin(azm) * std::sin(plr), - std::cos(plr)).normalized(); + auto dir = spheric_to_dir(plr, azm).normalized(); double score = pinhead_mesh_intersect( hp, dir, pin_r, m_cfg.head_back_radius_mm, w); @@ -767,17 +769,14 @@ void SupportTreeBuildsteps::filter() return score; }, initvals(polar, azimuth), // start with what we have - bound(3 * PI / 4, - PI), // Must not exceed the tilt limit + bound(3 * PI / 4, PI), // Must not exceed the tilt limit bound(-PI, PI) // azimuth can be a full search ); if(oresult.score > w) { polar = std::get<0>(oresult.optimum); azimuth = std::get<1>(oresult.optimum); - nn = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); + nn = spheric_to_dir(polar, azimuth).normalized(); t = EigenMesh3D::hit_result(oresult.score); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index 3998f5a350..24e0116bda 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -20,6 +20,21 @@ inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; } +inline std::pair dir_to_spheric(const Vec3d &n, double norm = 1.) +{ + double z = n.z(); + double r = norm; + double polar = std::acos(z / r); + double azimuth = std::atan2(n(1), n(0)); + return {polar, azimuth}; +} + +inline Vec3d spheric_to_dir(double polar, double azimuth) +{ + return {std::cos(azimuth) * std::sin(polar), + std::sin(azimuth) * std::sin(polar), std::cos(polar)}; +} + // This function returns the position of the centroid in the input 'clust' // vector of point indices. template @@ -228,6 +243,9 @@ class SupportTreeBuildsteps { // This is a proxy function for pillar creation which will mind the gap // between the pad and the model bottom in zero elevation mode. + // jp is the starting junction point which needs to be routed down. + // sourcedir is the allowed direction of an optional bridge between the + // jp junction and the final pillar. void create_ground_pillar(const Vec3d &jp, const Vec3d &sourcedir, double radius, From 90fbbf401f9f7bac8df2c03a2075997b1ce46b3f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 Jan 2020 10:32:30 +0100 Subject: [PATCH 100/130] Refactor model facing support generation. Fix for touching junction when adding aux pillars. Fix issue with overly long support bridges. --- src/libslic3r/SLA/SupportTreeBuildsteps.cpp | 325 +++++++++----------- src/libslic3r/SLA/SupportTreeBuildsteps.hpp | 32 +- 2 files changed, 164 insertions(+), 193 deletions(-) diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index b2570570dc..45fef58cd0 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -7,6 +7,14 @@ namespace Slic3r { namespace sla { +static const Vec3d DOWN = {0.0, 0.0, -1.0}; + +using libnest2d::opt::initvals; +using libnest2d::opt::bound; +using libnest2d::opt::StopCriteria; +using libnest2d::opt::GeneticOptimizer; +using libnest2d::opt::SubplexOptimizer; + SupportTreeBuildsteps::SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm) : m_cfg(sm.cfg) @@ -560,8 +568,6 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, double radius, long head_id) { - // People were killed for this number (seriously) - static const Vec3d DOWN = {0.0, 0.0, -1.0}; const double SLOPE = 1. / std::cos(m_cfg.bridge_slope); double gndlvl = m_builder.ground_level; @@ -587,7 +593,8 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, normal_mode = false; // The min distance needed to move away from the model in XY plane. - double mind = min_dist - dist; + double current_d = min_dist - dist; + double current_bride_d = SLOPE * current_d; // get a suitable direction for the corrector bridge. It is the // original sourcedir's azimuth but the polar angle is saturated to the @@ -596,7 +603,6 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, polar = PI - m_cfg.bridge_slope; auto dir = spheric_to_dir(polar, azimuth).normalized(); - using namespace libnest2d::opt; StopCriteria scr; scr.stop_score = min_dist; SubplexOptimizer solver(scr); @@ -610,10 +616,10 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, endpt(Z) = gndlvl; return std::sqrt(m_mesh.squared_distance(endpt)); }, - initvals(SLOPE * mind), bound(0.0, 2 * SLOPE * min_dist)); + initvals(current_bride_d), + bound(0.0, m_cfg.max_bridge_length_mm - current_bride_d)); - mind = std::get<0>(result.optimum); - endp = jp + SLOPE * mind * dir; + endp = jp + std::get<0>(result.optimum) * dir; Vec3d pgnd = {endp(X), endp(Y), gndlvl}; can_add_base = result.score > min_dist; @@ -694,11 +700,6 @@ void SupportTreeBuildsteps::filter() // not be enough space for the pinhead. Filtering is applied for // these reasons. - using libnest2d::opt::bound; - using libnest2d::opt::initvals; - using libnest2d::opt::GeneticOptimizer; - using libnest2d::opt::StopCriteria; - ccr::SpinningMutex mutex; auto addfn = [&mutex](PtIndices &container, unsigned val) { std::lock_guard lk(mutex); @@ -836,16 +837,17 @@ void SupportTreeBuildsteps::classify() m_thr(); auto& head = m_builder.head(i); - Vec3d n(0, 0, -1); double r = head.r_back_mm; Vec3d headjp = head.junction_point(); // collision check - auto hit = bridge_mesh_intersect(headjp, n, r); + auto hit = bridge_mesh_intersect(headjp, DOWN, r); if(std::isinf(hit.distance())) ground_head_indices.emplace_back(i); else if(m_cfg.ground_facing_only) head.invalidate(); - else m_iheads_onmodel.emplace_back(std::make_pair(i, hit)); + else m_iheads_onmodel.emplace_back(i); + + m_head_to_ground_scans[i] = hit; } // We want to search for clusters of points that are far enough @@ -892,13 +894,14 @@ void SupportTreeBuildsteps::routing_to_ground() // get the current cluster centroid auto & thr = m_thr; const auto &points = m_points; - long lcid = cluster_centroid( + + long lcid = cluster_centroid( cl, [&points](size_t idx) { return points.row(long(idx)); }, [thr](const Vec3d &p1, const Vec3d &p2) { thr(); return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y))); }); - + assert(lcid >= 0); unsigned hid = cl[size_t(lcid)]; // Head ID @@ -943,192 +946,138 @@ void SupportTreeBuildsteps::routing_to_ground() } } +bool SupportTreeBuildsteps::connect_to_ground(Head &head, const Vec3d &dir) +{ + auto hjp = head.junction_point(); + double r = head.r_back_mm; + double t = bridge_mesh_intersect(hjp, dir, head.r_back_mm); + double d = 0, tdown = 0; + t = std::min(t, m_cfg.max_bridge_length_mm); + + while (d < t && !std::isinf(tdown = bridge_mesh_intersect(hjp + d * dir, DOWN, r))) + d += r; + + if(!std::isinf(tdown)) return false; + + Vec3d endp = hjp + d * dir; + m_builder.add_bridge(head.id, endp); + m_builder.add_junction(endp, head.r_back_mm); + + this->create_ground_pillar(endp, dir, head.r_back_mm); + + return true; +} + +bool SupportTreeBuildsteps::connect_to_ground(Head &head) +{ + if (connect_to_ground(head, head.dir)) return true; + + // Optimize bridge direction: + // Straight path failed so we will try to search for a suitable + // direction out of the cavity. + auto [polar, azimuth] = dir_to_spheric(head.dir); + + StopCriteria stc; + stc.max_iterations = m_cfg.optimizer_max_iterations; + stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; + stc.stop_score = 1e6; + GeneticOptimizer solver(stc); + solver.seed(0); // we want deterministic behavior + + double r_back = head.r_back_mm; + Vec3d hjp = head.junction_point(); + auto oresult = solver.optimize_max( + [this, hjp, r_back](double plr, double azm) { + Vec3d n = spheric_to_dir(plr, azm).normalized(); + return bridge_mesh_intersect(hjp, n, r_back); + }, + initvals(polar, azimuth), // let's start with what we have + bound(3*PI/4, PI), // Must not exceed the slope limit + bound(-PI, PI) // azimuth can be a full range search + ); + + Vec3d bridgedir = spheric_to_dir(oresult.optimum).normalized(); + return connect_to_ground(head, bridgedir); +} + +bool SupportTreeBuildsteps::connect_to_model_body(Head &head) +{ + if (head.id <= ID_UNSET) return false; + + auto it = m_head_to_ground_scans.find(unsigned(head.id)); + if (it == m_head_to_ground_scans.end()) return false; + + auto &hit = it->second; + Vec3d hjp = head.junction_point(); + double zangle = std::asin(hit.direction()(Z)); + zangle = std::max(zangle, PI/4); + double h = std::sin(zangle) * head.fullwidth(); + + // The width of the tail head that we would like to have... + h = std::min(hit.distance() - head.r_back_mm, h); + + if(h <= 0.) return false; + + Vec3d endp{hjp(X), hjp(Y), hjp(Z) - hit.distance() + h}; + auto center_hit = m_mesh.query_ray_hit(hjp, DOWN); + + double hitdiff = center_hit.distance() - hit.distance(); + Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm? + center_hit.position() : hit.position(); + + head.transform(); + + long pillar_id = m_builder.add_pillar(head.id, endp, head.r_back_mm); + Pillar &pill = m_builder.pillar(pillar_id); + + Vec3d taildir = endp - hitp; + double dist = distance(endp, hitp) + m_cfg.head_penetration_mm; + double w = dist - 2 * head.r_pin_mm - head.r_back_mm; + + if (w < 0.) { + BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; + w = 0.; + } + + Head tailhead(head.r_back_mm, head.r_pin_mm, w, + m_cfg.head_penetration_mm, taildir, hitp); + + tailhead.transform(); + pill.base = tailhead.mesh; + + m_pillar_index.guarded_insert(pill.endpoint(), pill.id); + + return true; +} + void SupportTreeBuildsteps::routing_to_model() { // We need to check if there is an easy way out to the bed surface. // If it can be routed there with a bridge shorter than // min_bridge_distance. - - // First we want to index the available pillars. The best is to connect - // these points to the available pillars - - auto routedown = [this](Head& head, const Vec3d& dir, double dist) - { - head.transform(); - Vec3d endp = head.junction_point() + dist * dir; - m_builder.add_bridge(head.id, endp); - m_builder.add_junction(endp, head.r_back_mm); - - this->create_ground_pillar(endp, dir, head.r_back_mm); - }; - - std::vector modelpillars; - ccr::SpinningMutex mutex; - auto onmodelfn = - [this, routedown, &modelpillars, &mutex] - (const std::pair &el, size_t) - { + ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), + [this] (const unsigned idx, size_t) { m_thr(); - unsigned idx = el.first; - EigenMesh3D::hit_result hit = el.second; auto& head = m_builder.head(idx); - Vec3d hjp = head.junction_point(); - // ///////////////////////////////////////////////////////////////// // Search nearby pillar - // ///////////////////////////////////////////////////////////////// - if(search_pillar_and_connect(head)) { head.transform(); return; } - // ///////////////////////////////////////////////////////////////// - // Try straight path - // ///////////////////////////////////////////////////////////////// - // Cannot connect to nearby pillar. We will try to search for // a route to the ground. + if(connect_to_ground(head)) { head.transform(); return; } - double t = bridge_mesh_intersect(hjp, head.dir, head.r_back_mm); - double d = 0, tdown = 0; - Vec3d dirdown(0.0, 0.0, -1.0); - - t = std::min(t, m_cfg.max_bridge_length_mm); - - while(d < t && !std::isinf(tdown = bridge_mesh_intersect( - hjp + d*head.dir, - dirdown, head.r_back_mm))) { - d += head.r_back_mm; - } - - if(std::isinf(tdown)) { // we heave found a route to the ground - routedown(head, head.dir, d); return; - } - - // ///////////////////////////////////////////////////////////////// - // Optimize bridge direction - // ///////////////////////////////////////////////////////////////// - - // Straight path failed so we will try to search for a suitable - // direction out of the cavity. - - // Get the spherical representation of the normal. its easier to - // work with. - double z = head.dir(Z); - double r = 1.0; // for normalized vector - double polar = std::acos(z / r); - double azimuth = std::atan2(head.dir(Y), head.dir(X)); - - using libnest2d::opt::bound; - using libnest2d::opt::initvals; - using libnest2d::opt::GeneticOptimizer; - using libnest2d::opt::StopCriteria; - - StopCriteria stc; - stc.max_iterations = m_cfg.optimizer_max_iterations; - stc.relative_score_difference = m_cfg.optimizer_rel_score_diff; - stc.stop_score = 1e6; - GeneticOptimizer solver(stc); - solver.seed(0); // we want deterministic behavior - - double r_back = head.r_back_mm; - - auto oresult = solver.optimize_max( - [this, hjp, r_back](double plr, double azm) - { - Vec3d n = Vec3d(std::cos(azm) * std::sin(plr), - std::sin(azm) * std::sin(plr), - std::cos(plr)).normalized(); - return bridge_mesh_intersect(hjp, n, r_back); - }, - initvals(polar, azimuth), // let's start with what we have - bound(3*PI/4, PI), // Must not exceed the slope limit - bound(-PI, PI) // azimuth can be a full range search - ); - - d = 0; t = oresult.score; - - polar = std::get<0>(oresult.optimum); - azimuth = std::get<1>(oresult.optimum); - Vec3d bridgedir = Vec3d(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)).normalized(); - - t = std::min(t, m_cfg.max_bridge_length_mm); - - while(d < t && !std::isinf(tdown = bridge_mesh_intersect( - hjp + d*bridgedir, - dirdown, - head.r_back_mm))) { - d += head.r_back_mm; - } - - if(std::isinf(tdown)) { // we heave found a route to the ground - routedown(head, bridgedir, d); return; - } - - // ///////////////////////////////////////////////////////////////// - // Route to model body - // ///////////////////////////////////////////////////////////////// - - double zangle = std::asin(hit.direction()(Z)); - zangle = std::max(zangle, PI/4); - double h = std::sin(zangle) * head.fullwidth(); - - // The width of the tail head that we would like to have... - h = std::min(hit.distance() - head.r_back_mm, h); - - if(h > 0) { - Vec3d endp{hjp(X), hjp(Y), hjp(Z) - hit.distance() + h}; - auto center_hit = m_mesh.query_ray_hit(hjp, dirdown); - - double hitdiff = center_hit.distance() - hit.distance(); - Vec3d hitp = std::abs(hitdiff) < 2*head.r_back_mm? - center_hit.position() : hit.position(); - - head.transform(); - - long pillar_id = m_builder.add_pillar(head.id, endp, head.r_back_mm); - Pillar &pill = m_builder.pillar(pillar_id); - - Vec3d taildir = endp - hitp; - double dist = distance(endp, hitp) + m_cfg.head_penetration_mm; - double w = dist - 2 * head.r_pin_mm - head.r_back_mm; - - if (w < 0.) { - BOOST_LOG_TRIVIAL(error) << "Pinhead width is negative!"; - w = 0.; - } - - Head tailhead(head.r_back_mm, - head.r_pin_mm, - w, - m_cfg.head_penetration_mm, - taildir, - hitp); - - tailhead.transform(); - pill.base = tailhead.mesh; - - // Experimental: add the pillar to the index for cascading - std::lock_guard lk(mutex); - modelpillars.emplace_back(unsigned(pill.id)); - return; - } + // No route to the ground, so connect to the model body as a last resort + if (connect_to_model_body(head)) { return; } // We have failed to route this head. BOOST_LOG_TRIVIAL(warning) - << "Failed to route model facing support point." - << " ID: " << idx; + << "Failed to route model facing support point. ID: " << idx; + head.invalidate(); - }; - - ccr::enumerate(m_iheads_onmodel.begin(), m_iheads_onmodel.end(), onmodelfn); - - for(auto pillid : modelpillars) { - auto& pillar = m_builder.pillar(pillid); - m_pillar_index.insert(pillar.endpoint(), pillid); - } + }); } void SupportTreeBuildsteps::interconnect_pillars() @@ -1279,7 +1228,8 @@ void SupportTreeBuildsteps::interconnect_pillars() spts[n] = s; // Check the path vertically down - auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); + Vec3d check_from = s + Vec3d{0., 0., pillar().r}; + auto hr = bridge_mesh_intersect(check_from, DOWN, pillar().r); Vec3d gndsp{s(X), s(Y), gnd}; // If the path is clear, check for pillar base collisions @@ -1359,12 +1309,11 @@ void SupportTreeBuildsteps::routing_headless() Vec3d n = m_support_nmls.row(i); // mesh outward normal Vec3d sp = sph - n * HWIDTH_MM; // stick head start point - Vec3d dir = {0, 0, -1}; Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sph, dir, R, true); - double realdist = ray_mesh_intersect(sj, dir); + double idist = bridge_mesh_intersect(sph, DOWN, R, true); + double realdist = ray_mesh_intersect(sj, DOWN); double dist = realdist; if (std::isinf(dist)) dist = sph(Z) - m_builder.ground_level; @@ -1377,7 +1326,7 @@ void SupportTreeBuildsteps::routing_headless() } bool use_endball = !std::isinf(realdist); - Vec3d ej = sj + (dist + HWIDTH_MM) * dir; + Vec3d ej = sj + (dist + HWIDTH_MM) * DOWN ; m_builder.add_compact_bridge(sp, ej, n, R, use_endball); } } diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp index 24e0116bda..9533049b60 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.hpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.hpp @@ -35,6 +35,17 @@ inline Vec3d spheric_to_dir(double polar, double azimuth) std::sin(azimuth) * std::sin(polar), std::cos(polar)}; } +inline Vec3d spheric_to_dir(const std::tuple &v) +{ + auto [plr, azm] = v; + return spheric_to_dir(plr, azm); +} + +inline Vec3d spheric_to_dir(const std::pair &v) +{ + return spheric_to_dir(v.first, v.second); +} + // This function returns the position of the centroid in the input 'clust' // vector of point indices. template @@ -166,10 +177,10 @@ class SupportTreeBuildsteps { using PtIndices = std::vector; PtIndices m_iheads; // support points with pinhead + PtIndices m_iheads_onmodel; PtIndices m_iheadless; // headless support points - - // supp. pts. connecting to model: point index and the ray hit data - std::vector> m_iheads_onmodel; + + std::map m_head_to_ground_scans; // normals for support points from model faces. PointSet m_support_nmls; @@ -238,9 +249,18 @@ class SupportTreeBuildsteps { // For connecting a head to a nearby pillar. bool connect_to_nearpillar(const Head& head, long nearpillar_id); - + + // Find route for a head to the ground. Inserts additional bridge from the + // head to the pillar if cannot create pillar directly. + // The optional dir parameter is the direction of the bridge which is the + // direction of the pinhead if omitted. + bool connect_to_ground(Head& head, const Vec3d &dir); + inline bool connect_to_ground(Head& head); + + bool connect_to_model_body(Head &head); + bool search_pillar_and_connect(const Head& head); - + // This is a proxy function for pillar creation which will mind the gap // between the pad and the model bottom in zero elevation mode. // jp is the starting junction point which needs to be routed down. @@ -250,6 +270,8 @@ class SupportTreeBuildsteps { const Vec3d &sourcedir, double radius, long head_id = ID_UNSET); + + public: SupportTreeBuildsteps(SupportTreeBuilder & builder, const SupportableMesh &sm); From 6205524d75e47ebb32ba999525121c3647cd151a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Jan 2020 13:57:39 +0100 Subject: [PATCH 101/130] Make support point generator deterministic. --- src/libslic3r/SLA/SupportPointGenerator.cpp | 79 ++++++++++++++++----- src/libslic3r/SLA/SupportPointGenerator.hpp | 14 +++- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 66b6ea3f41..55124d4fc6 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -49,18 +49,64 @@ float SupportPointGenerator::distance_limit(float angle) const return 1./(2.4*get_required_density(angle)); }*/ -SupportPointGenerator::SupportPointGenerator(const sla::EigenMesh3D & emesh, - const std::vector &slices, - const std::vector & heights, - const Config & config, - std::function throw_on_cancel, - std::function statusfn) +class SupportPointGenerator::RandomGen { + std::mt19937 m_; +public: + + using result_type = long; + + RandomGen() + { + std::random_device rd; + m_.seed(rd()); + } + + explicit RandomGen(long seedval) { seed(seedval); } + + void seed(long s) { m_.seed(std::mt19937::result_type(s)); } + long operator() () { return long(m_()); } + long min() const { return m_.min(); } + long max() const { return m_.max(); } +}; + +SupportPointGenerator::SupportPointGenerator( + const sla::EigenMesh3D &emesh, + const std::vector &slices, + const std::vector & heights, + const Config & config, + std::function throw_on_cancel, + std::function statusfn) + : SupportPointGenerator(emesh, config, throw_on_cancel, statusfn) +{ + execute(slices, heights); +} + +SupportPointGenerator::SupportPointGenerator( + const EigenMesh3D &emesh, + const SupportPointGenerator::Config &config, + std::function throw_on_cancel, + std::function statusfn) : m_config(config) , m_emesh(emesh) , m_throw_on_cancel(throw_on_cancel) , m_statusfn(statusfn) { - process(slices, heights); +} + +void SupportPointGenerator::execute(const std::vector &slices, + const std::vector & heights) +{ + RandomGen rng; + process(slices, heights, rng); + project_onto_mesh(m_output); +} + +void SupportPointGenerator::execute(const std::vector &slices, + const std::vector & heights, + long seed) +{ + RandomGen rng(seed); + process(slices, heights, rng); project_onto_mesh(m_output); } @@ -184,7 +230,7 @@ static std::vector make_layers( return layers; } -void SupportPointGenerator::process(const std::vector& slices, const std::vector& heights) +void SupportPointGenerator::process(const std::vector& slices, const std::vector& heights, RandomGen &rng) { #ifdef SLA_SUPPORTPOINTGEN_DEBUG std::vector> islands; @@ -239,15 +285,15 @@ void SupportPointGenerator::process(const std::vector& slices, const //float force_deficit = s.support_force_deficit(m_config.tear_pressure()); if (s.islands_below.empty()) { // completely new island - needs support no doubt - uniformly_cover({ *s.polygon }, s, point_grid, true); + uniformly_cover({ *s.polygon }, s, point_grid, rng, true); } else if (! s.dangling_areas.empty()) { // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(s.dangling_areas, s, point_grid); + uniformly_cover(s.dangling_areas, s, point_grid, rng); } else if (! s.overhangs_slopes.empty()) { //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(s.overhangs_slopes, s, point_grid); + uniformly_cover(s.overhangs_slopes, s, point_grid, rng); } } @@ -266,7 +312,7 @@ void SupportPointGenerator::process(const std::vector& slices, const } } -std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng) +std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, SupportPointGenerator::RandomGen &rng) { // Triangulate the polygon with holes into triplets of 3D points. std::vector triangles = Slic3r::triangulate_expolygon_2f(expoly); @@ -306,7 +352,7 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m return out; } -std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) +std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, SupportPointGenerator::RandomGen &rng) { std::vector out = sample_expolygon(expoly, samples_per_mm2, rng); double point_stepping_scaled = scale_(1.f) / samples_per_mm_boundary; @@ -319,7 +365,7 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float return out; } -std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) +std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, SupportPointGenerator::RandomGen &rng) { std::vector out; for (const ExPolygon &expoly : expolys) @@ -442,7 +488,7 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); std::vector poisson_samples; for (size_t iter = 0; iter < 4; ++ iter) { diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 3bbe3d7695..d7e322d066 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -29,6 +29,8 @@ public: SupportPointGenerator(const EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, const Config& config, std::function throw_on_cancel, std::function statusfn); + SupportPointGenerator(const EigenMesh3D& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); + const std::vector& output() { return m_output; } struct MyLayer; @@ -184,13 +186,21 @@ public: } }; + void execute(const std::vector &slices, + const std::vector & heights); + + void execute(const std::vector &slices, + const std::vector & heights, long seed); + + class RandomGen; + private: std::vector m_output; SupportPointGenerator::Config m_config; - void process(const std::vector& slices, const std::vector& heights); - void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); + void process(const std::vector& slices, const std::vector& heights, RandomGen&); + void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, RandomGen&, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector& points) const; #ifdef SLA_SUPPORTPOINTGEN_DEBUG From 256249fdaf065fd3bea1c95f277e9369e92d3862 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 13 Jan 2020 14:00:33 +0100 Subject: [PATCH 102/130] Make sla support pierce tests repeatable --- tests/sla_print/sla_test_utils.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 3da28a50e6..124fa3cf1b 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -115,8 +115,10 @@ void test_supports(const std::string &obj_filename, // Create the support point generator sla::SupportPointGenerator::Config autogencfg; autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); - sla::SupportPointGenerator point_gen{emesh, out.model_slices, out.slicegrid, - autogencfg, [] {}, [](int) {}}; + sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; + + long seed = 0; // Make the test repeatable + point_gen.execute(out.model_slices, out.slicegrid, seed); // Get the calculated support points. std::vector support_points = point_gen.output(); From 8af6890cab20e376d6bbe29ffacb46ee36afcb59 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 Jan 2020 10:35:52 +0100 Subject: [PATCH 103/130] Remove test restriction for on-model supports pierce test --- tests/sla_print/sla_test_utils.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 124fa3cf1b..ab1bbac0eb 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -13,11 +13,6 @@ void test_support_model_collision(const std::string &obj_filename, // the supports will not touch the model body. supportcfg.head_penetration_mm = -0.15; - // TODO: currently, the tailheads penetrating into the model body do not - // respect the penetration parameter properly. No issues were reported so - // far but we should definitely fix this. - supportcfg.ground_facing_only = true; - test_supports(obj_filename, supportcfg, hollowingcfg, drainholes, byproducts); // Slice the support mesh given the slice grid of the model. From ce49f0a294499e1e05ad356fda8dace163a5bf60 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 Jan 2020 10:41:42 +0100 Subject: [PATCH 104/130] Test sla support point generation determinism with seed parameter --- src/libslic3r/SLA/SupportPointGenerator.hpp | 3 +- tests/sla_print/sla_print_tests.cpp | 47 +++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index d7e322d066..37a831b344 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -31,7 +31,8 @@ public: SupportPointGenerator(const EigenMesh3D& emesh, const Config& config, std::function throw_on_cancel, std::function statusfn); - const std::vector& output() { return m_output; } + const std::vector& output() const { return m_output; } + std::vector& output() { return m_output; } struct MyLayer; diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 4fefeb6bbd..8f69b84342 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -85,6 +85,53 @@ TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") { test_pairhash(); } +TEST_CASE("Support point generator should be deterministic if seeded", + "[SLASupportGeneration], [SLAPointGen]") { + TriangleMesh mesh = load_model("A_upsidedown.obj"); + + sla::EigenMesh3D emesh{mesh}; + + sla::SupportConfig supportcfg; + sla::SupportPointGenerator::Config autogencfg; + autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); + sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; + + TriangleMeshSlicer slicer{&mesh}; + + auto bb = mesh.bounding_box(); + double zmin = bb.min.z(); + double zmax = bb.max.z(); + double gnd = zmin - supportcfg.object_elevation_mm; + auto layer_h = 0.05f; + + auto slicegrid = grid(float(gnd), float(zmax), layer_h); + std::vector slices; + slicer.slice(slicegrid, CLOSING_RADIUS, &slices, []{}); + + point_gen.execute(slices, slicegrid, 0); + + auto get_chksum = [](const std::vector &pts){ + long long chksum = 0; + for (auto &pt : pts) { + auto p = scaled(pt.pos); + chksum += p.x() + p.y() + p.z(); + } + + return chksum; + }; + + long long checksum = get_chksum(point_gen.output()); + size_t ptnum = point_gen.output().size(); + REQUIRE(point_gen.output().size() > 0); + + for (int i = 0; i < 20; ++i) { + point_gen.output().clear(); + point_gen.execute(slices, slicegrid, 0); + REQUIRE(point_gen.output().size() == ptnum); + REQUIRE(checksum == get_chksum(point_gen.output())); + } +} + TEST_CASE("Flat pad geometry is valid", "[SLASupportGeneration]") { sla::PadConfig padcfg; From e6244f7bdbcbd800c8d36cefdca5c6ba159a9d45 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 14 Jan 2020 10:24:15 +0100 Subject: [PATCH 105/130] Eliminate use of uninitialized variable. --- src/libslic3r/SLA/SupportPointGenerator.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index 37a831b344..b8f5b607ea 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -79,7 +79,7 @@ public: ExPolygons overhangs; // Overhangs, where the surface must slope. ExPolygons overhangs_slopes; - float overhangs_area; + float overhangs_area = 0.f; bool overlaps(const Structure &rhs) const { return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon)); From a36c7c76ccacb0c7ec60ab383938daf4012885d4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 15 Jan 2020 10:39:07 +0100 Subject: [PATCH 106/130] Fix scene not being centered --- sandboxes/opencsg/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ab379c196e..a0d5c4ceb0 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -446,13 +446,13 @@ void MyFrame::activate_canvas_display() m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { // This is required even though dc is not used otherwise. wxPaintDC dc(this); - const wxSize csize = GetClientSize(); + const wxSize csize = m_canvas->GetClientSize(); m_canvas->get_display()->set_screen_size(csize.x, csize.y); m_canvas->get_display()->repaint(); }); - m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent &) { - const wxSize csize = GetClientSize(); + m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent &) { + const wxSize csize = m_canvas->GetClientSize(); m_canvas->get_display()->set_screen_size(csize.x, csize.y); m_canvas->get_display()->repaint(); }); From 3ab246df6b892c549979a801fd13e372e84cd93d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 15 Jan 2020 12:43:58 +0100 Subject: [PATCH 107/130] Remove completely redundant code --- sandboxes/opencsg/Engine.cpp | 13 ------------- sandboxes/opencsg/Engine.hpp | 17 ----------------- sandboxes/opencsg/ShaderCSGDisplay.cpp | 13 ------------- 3 files changed, 43 deletions(-) diff --git a/sandboxes/opencsg/Engine.cpp b/sandboxes/opencsg/Engine.cpp index bd9da6540a..f110b23c5c 100644 --- a/sandboxes/opencsg/Engine.cpp +++ b/sandboxes/opencsg/Engine.cpp @@ -420,19 +420,6 @@ void CSGDisplay::on_scene_updated(const Scene &scene) mshinst.require_shared_vertices(); m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection, m_csgsettings.get_convexity()); - - auto tr = Transform3f::Identity(); - tr.translate(-center); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.pos; - }); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.normal; - }); } for (const sla::DrainHole &holept : holedata) { diff --git a/sandboxes/opencsg/Engine.hpp b/sandboxes/opencsg/Engine.hpp index c078a39d86..fc76c1b313 100644 --- a/sandboxes/opencsg/Engine.hpp +++ b/sandboxes/opencsg/Engine.hpp @@ -171,23 +171,6 @@ public: // Try to enable or disable multisampling. bool enable_multisampling(bool e = true); -template::value_type> -inline std::vector transform_pts( - It from, It to, Trafo &&tr, GetPt &&point) -{ - vector ret; - ret.reserve(to - from); - for(auto it = from; it != to; ++it) { - V v = *it; - v.pos = tr * point(*it); - ret.emplace_back(std::move(v)); - } - return ret; -} - class Volume { IndexedVertexArray m_geom; Geometry::Transformation m_trafo; diff --git a/sandboxes/opencsg/ShaderCSGDisplay.cpp b/sandboxes/opencsg/ShaderCSGDisplay.cpp index 7339e408c5..8ceb234be0 100644 --- a/sandboxes/opencsg/ShaderCSGDisplay.cpp +++ b/sandboxes/opencsg/ShaderCSGDisplay.cpp @@ -53,19 +53,6 @@ void ShaderCSGDisplay::on_scene_updated(const Scene &scene) mshinst.require_shared_vertices(); add_mesh(mshinst); - - auto tr = Transform3f::Identity(); - tr.translate(-center); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.pos; - }); - - transform_pts(holedata.begin(), holedata.end(), tr, - [](const sla::DrainHole &dh) { - return dh.normal; - }); } for (const sla::DrainHole &holept : holedata) { From 402ae12db214d127a246c8e3e28cd0ca81e7bc1f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 15 Jan 2020 12:54:06 +0100 Subject: [PATCH 108/130] Fix algorithm switching --- sandboxes/opencsg/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index a0d5c4ceb0..ec6c01789a 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -387,8 +387,8 @@ void MyFrame::read_csg_settings(const wxCmdLineParser &parser) void MyFrame::set_renderer_algorithm(const wxString &alg) { - long alg_idx = get_idx("EnricoShader", CSG_ALGS); - if (alg_idx < 0 || alg_idx >= CSG_ALGS.size()) return; + long alg_idx = get_idx(alg, CSG_ALGS); + if (alg_idx < 0 || alg_idx >= long(CSG_ALGS.size())) return; // If there is a valid display in place, save its camera. auto cam = m_canvas->get_display() ? From e6bdec4aa531e373724cb456d9ab76ff0af178b9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 15 Jan 2020 17:44:15 +0100 Subject: [PATCH 109/130] Remove wrapper around std::mt19937 --- src/libslic3r/SLA/SupportPointGenerator.cpp | 54 +++++---------------- src/libslic3r/SLA/SupportPointGenerator.hpp | 14 +++--- tests/sla_print/sla_print_tests.cpp | 5 +- tests/sla_print/sla_test_utils.cpp | 4 +- 4 files changed, 25 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 55124d4fc6..78c2ced356 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -49,26 +49,6 @@ float SupportPointGenerator::distance_limit(float angle) const return 1./(2.4*get_required_density(angle)); }*/ -class SupportPointGenerator::RandomGen { - std::mt19937 m_; -public: - - using result_type = long; - - RandomGen() - { - std::random_device rd; - m_.seed(rd()); - } - - explicit RandomGen(long seedval) { seed(seedval); } - - void seed(long s) { m_.seed(std::mt19937::result_type(s)); } - long operator() () { return long(m_()); } - long min() const { return m_.min(); } - long max() const { return m_.max(); } -}; - SupportPointGenerator::SupportPointGenerator( const sla::EigenMesh3D &emesh, const std::vector &slices, @@ -78,6 +58,8 @@ SupportPointGenerator::SupportPointGenerator( std::function statusfn) : SupportPointGenerator(emesh, config, throw_on_cancel, statusfn) { + std::random_device rd; + m_rng.seed(rd()); execute(slices, heights); } @@ -96,17 +78,7 @@ SupportPointGenerator::SupportPointGenerator( void SupportPointGenerator::execute(const std::vector &slices, const std::vector & heights) { - RandomGen rng; - process(slices, heights, rng); - project_onto_mesh(m_output); -} - -void SupportPointGenerator::execute(const std::vector &slices, - const std::vector & heights, - long seed) -{ - RandomGen rng(seed); - process(slices, heights, rng); + process(slices, heights); project_onto_mesh(m_output); } @@ -230,7 +202,7 @@ static std::vector make_layers( return layers; } -void SupportPointGenerator::process(const std::vector& slices, const std::vector& heights, RandomGen &rng) +void SupportPointGenerator::process(const std::vector& slices, const std::vector& heights) { #ifdef SLA_SUPPORTPOINTGEN_DEBUG std::vector> islands; @@ -285,15 +257,15 @@ void SupportPointGenerator::process(const std::vector& slices, const //float force_deficit = s.support_force_deficit(m_config.tear_pressure()); if (s.islands_below.empty()) { // completely new island - needs support no doubt - uniformly_cover({ *s.polygon }, s, point_grid, rng, true); + uniformly_cover({ *s.polygon }, s, point_grid, true); } else if (! s.dangling_areas.empty()) { // Let's see if there's anything that overlaps enough to need supports: // What we now have in polygons needs support, regardless of what the forces are, so we can add them. //FIXME is it an island point or not? Vojtech thinks it is. - uniformly_cover(s.dangling_areas, s, point_grid, rng); + uniformly_cover(s.dangling_areas, s, point_grid); } else if (! s.overhangs_slopes.empty()) { //FIXME add the support force deficit as a parameter, only cover until the defficiency is covered. - uniformly_cover(s.overhangs_slopes, s, point_grid, rng); + uniformly_cover(s.overhangs_slopes, s, point_grid); } } @@ -312,7 +284,7 @@ void SupportPointGenerator::process(const std::vector& slices, const } } -std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, SupportPointGenerator::RandomGen &rng) +std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng) { // Triangulate the polygon with holes into triplets of 3D points. std::vector triangles = Slic3r::triangulate_expolygon_2f(expoly); @@ -352,7 +324,7 @@ std::vector sample_expolygon(const ExPolygon &expoly, float samples_per_m return out; } -std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, SupportPointGenerator::RandomGen &rng) +std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) { std::vector out = sample_expolygon(expoly, samples_per_mm2, rng); double point_stepping_scaled = scale_(1.f) / samples_per_mm_boundary; @@ -365,7 +337,7 @@ std::vector sample_expolygon_with_boundary(const ExPolygon &expoly, float return out; } -std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, SupportPointGenerator::RandomGen &rng) +std::vector sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng) { std::vector out; for (const ExPolygon &expoly : expolys) @@ -488,7 +460,7 @@ static inline std::vector poisson_disk_from_samples(const std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); + std::vector raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, m_rng); std::vector poisson_samples; for (size_t iter = 0; iter < 4; ++ iter) { poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, @@ -541,7 +513,7 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure // assert(! poisson_samples.empty()); if (poisson_samples_target < poisson_samples.size()) { - std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng); + std::shuffle(poisson_samples.begin(), poisson_samples.end(), m_rng); poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); } for (const Vec2f &pt : poisson_samples) { diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp index b8f5b607ea..738fc57303 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.hpp +++ b/src/libslic3r/SLA/SupportPointGenerator.hpp @@ -1,6 +1,8 @@ #ifndef SLA_SUPPORTPOINTGENERATOR_HPP #define SLA_SUPPORTPOINTGENERATOR_HPP +#include + #include #include #include @@ -190,18 +192,14 @@ public: void execute(const std::vector &slices, const std::vector & heights); - void execute(const std::vector &slices, - const std::vector & heights, long seed); - - class RandomGen; - + void seed(std::mt19937::result_type s) { m_rng.seed(s); } private: std::vector m_output; SupportPointGenerator::Config m_config; - void process(const std::vector& slices, const std::vector& heights, RandomGen&); - void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, RandomGen&, bool is_new_island = false, bool just_one = false); + void process(const std::vector& slices, const std::vector& heights); + void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector& points) const; #ifdef SLA_SUPPORTPOINTGEN_DEBUG @@ -212,6 +210,8 @@ private: const EigenMesh3D& m_emesh; std::function m_throw_on_cancel; std::function m_statusfn; + + std::mt19937 m_rng; }; void remove_bottom_points(std::vector &pts, double gnd_lvl, double tolerance); diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 8f69b84342..b08c931e31 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -108,7 +108,8 @@ TEST_CASE("Support point generator should be deterministic if seeded", std::vector slices; slicer.slice(slicegrid, CLOSING_RADIUS, &slices, []{}); - point_gen.execute(slices, slicegrid, 0); + point_gen.seed(0); + point_gen.execute(slices, slicegrid); auto get_chksum = [](const std::vector &pts){ long long chksum = 0; @@ -126,7 +127,7 @@ TEST_CASE("Support point generator should be deterministic if seeded", for (int i = 0; i < 20; ++i) { point_gen.output().clear(); - point_gen.execute(slices, slicegrid, 0); + point_gen.execute(slices, slicegrid); REQUIRE(point_gen.output().size() == ptnum); REQUIRE(checksum == get_chksum(point_gen.output())); } diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index ab1bbac0eb..0804adb4f3 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -112,8 +112,8 @@ void test_supports(const std::string &obj_filename, autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm); sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}}; - long seed = 0; // Make the test repeatable - point_gen.execute(out.model_slices, out.slicegrid, seed); + point_gen.seed(0); // Make the test repeatable + point_gen.execute(out.model_slices, out.slicegrid); // Get the calculated support points. std::vector support_points = point_gen.output(); From a3f3c868eb9835b6086bb9f1aad238509ee4ebc7 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 14:28:22 +0100 Subject: [PATCH 110/130] Removed needless calls to instance_bounding_box --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index efb1101995..61f3c10b28 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -62,18 +62,21 @@ void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Select return; } - if (m_c->m_model_object != model_object || m_c->m_model_object_id != model_object->id()) { + bool something_changed = false; + + if (m_c->m_model_object != model_object + || m_c->m_model_object_id != model_object->id() + || m_c->m_active_instance != selection.get_instance_idx()) { m_c->m_model_object = model_object; m_c->m_print_object_idx = -1; + m_c->m_active_instance = selection.get_instance_idx(); + something_changed = true; } - m_c->m_active_instance = selection.get_instance_idx(); - - if (model_object && selection.is_from_single_instance()) + if (model_object && something_changed && selection.is_from_single_instance()) { // Cache the bb - it's needed for dealing with the clipping plane quite often // It could be done inside update_mesh but one has to account for scaling of the instance. - //FIXME calling ModelObject::instance_bounding_box() is expensive! m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); if (is_mesh_update_necessary()) { From 8c8256c6a23f159c9fb6e9f1c61c2689aa5d055e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 14:30:49 +0100 Subject: [PATCH 111/130] Fixed diameter/radius mismatch in gizmo UI --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 6 ++++-- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 61f3c10b28..5a99949bfb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -822,14 +822,16 @@ RENDER_AGAIN: // m_imgui->text(" "); // vertical gap ImGui::Separator(); - float diameter_upper_cap = 20.f; //static_cast(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value; + float diameter_upper_cap = 5.f; if (m_new_hole_radius > diameter_upper_cap) m_new_hole_radius = diameter_upper_cap; m_imgui->text(m_desc.at("hole_diameter")); ImGui::SameLine(diameter_slider_left); ImGui::PushItemWidth(window_width - diameter_slider_left); - ImGui::SliderFloat("", &m_new_hole_radius, 0.1f, diameter_upper_cap, "%.1f"); + float diam = 2.f * m_new_hole_radius; + ImGui::SliderFloat("", &diam, 1.f, diameter_upper_cap, "%.1f"); + m_new_hole_radius = diam / 2.f; bool clicked = ImGui::IsItemClicked(); bool edited = ImGui::IsItemEdited(); bool deactivated = ImGui::IsItemDeactivatedAfterEdit(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index ba2935a561..b6a125972a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -58,7 +58,7 @@ private: bool unsaved_changes() const; bool m_show_supports = true; - float m_new_hole_radius = 4.f; // Size of a new hole. + float m_new_hole_radius = 2.f; // Size of a new hole. float m_new_hole_height = 5.f; mutable std::vector m_selected; // which holes are currently selected From 6e8bdb2c862c95568b2b6f07d95e881cf66a4404 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 14:32:27 +0100 Subject: [PATCH 112/130] Fixed update of clipping plane when a gizmo was turned off --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 5 +---- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 5a99949bfb..17154de589 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -963,10 +963,6 @@ std::string GLGizmoHollow::on_get_name() const } -//const TriangleMesh* GLGizmoHollow::mesh() const { -// return (! m_c->m_mesh ? nullptr : (m_c->m_cavity_mesh ? m_c->m_cavity_mesh.get() : m_c->m_mesh)); -//} - void GLGizmoHollow::on_set_state() { @@ -1004,6 +1000,7 @@ void GLGizmoHollow::on_set_state() //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); m_clipping_plane_distance = 0.f; + update_clipping_plane(); // Release clippers and the AABB raycaster. m_c->m_object_clipper.reset(); m_c->m_supports_clipper.reset(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 4e00b0f77f..9b04fd6aba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -1082,6 +1082,7 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(true); m_normal_cache.clear(); m_clipping_plane_distance = 0.f; + update_clipping_plane(); // Release clippers and the AABB raycaster. m_its = nullptr; m_c->m_object_clipper.reset(); From 902d3bb904fc4f69afb7a798f5e9b63fbd84a19a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 14 Jan 2020 14:33:56 +0100 Subject: [PATCH 113/130] Fixed loading of hole_height when a hole is selected (hollowing gizmo) --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 17154de589..ebff04007f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -1079,7 +1079,7 @@ void GLGizmoHollow::select_point(int i) if (i == AllPoints) { m_new_hole_radius = m_c->m_model_object->sla_drain_holes[0].radius; - m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[0].height - HoleStickOutLength; } } else { @@ -1088,7 +1088,7 @@ void GLGizmoHollow::select_point(int i) m_selected[i] = true; m_selection_empty = false; m_new_hole_radius = m_c->m_model_object->sla_drain_holes[i].radius; - m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height; + m_new_hole_height = m_c->m_model_object->sla_drain_holes[i].height - HoleStickOutLength; } } From 822f9ff4a6921a7b42af4689da8dc387716129e0 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 16 Jan 2020 12:43:42 +0100 Subject: [PATCH 114/130] Fixed issues in SLA gizmos related to showing/hiding of the model/support structures --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++------ src/slic3r/GUI/GLCanvas3D.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 17 ++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 20 ++++++++++---------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ce5609ecae..6392b97c0a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1558,10 +1558,9 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } -bool GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) +void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { - if (m_render_sla_auxiliaries == visible) - return false; + m_render_sla_auxiliaries = visible; for (GLVolume* vol : m_volumes.volumes) { if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) @@ -1569,9 +1568,6 @@ bool GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObje && vol->composite_id.volume_id < 0) vol->is_active = visible; } - - m_render_sla_auxiliaries = visible; - return true; } void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject* mo, int instance_idx) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d68d5bb72f..3e8b56bd05 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -500,7 +500,7 @@ public: void reset_volumes(); int check_volumes_outside_state() const; - bool toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); + void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void update_instance_printable_state_for_object(size_t obj_idx); void update_instance_printable_state_for_objects(std::vector& object_idxs); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index ebff04007f..a832b5e5c0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -87,7 +87,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Select if (m_state == On) { m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -132,9 +132,6 @@ void GLGizmoHollow::on_render() const m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } - // Show/hide the original object - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); @@ -649,6 +646,9 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) m_c->m_volume_with_cavity->finalize_geometry(true); m_c->m_volume_with_cavity->force_transparent = false; + m_parent.toggle_model_objects_visibility(false, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); + // Reset raycaster so it works with the new mesh: m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); } @@ -908,8 +908,10 @@ RENDER_AGAIN: update_clipping_plane(true); // make sure supports are shown/hidden as appropriate - m_imgui->checkbox(m_desc["show_supports"], m_show_supports); - force_refresh = m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + if (m_imgui->checkbox(m_desc["show_supports"], m_show_supports)) { + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + force_refresh = true; + } m_imgui->end(); @@ -990,7 +992,8 @@ void GLGizmoHollow::on_set_state() m_parent.toggle_model_objects_visibility(false); if (m_c->m_model_object) - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. //const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 9b04fd6aba..45a766ccc1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -88,7 +88,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S if (m_state == On) { m_parent.toggle_model_objects_visibility(false); m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); } else m_parent.toggle_model_objects_visibility(true, nullptr, -1); @@ -144,8 +144,8 @@ void GLGizmoSlaSupports::on_render() const m_parent.get_shader().stop_using(); } // Show/hide the original object - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(bool(m_c->m_cavity_mesh), m_c->m_model_object, m_c->m_active_instance); + //m_parent.toggle_model_objects_visibility(! m_editing_mode && ! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + //m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); @@ -967,12 +967,6 @@ RENDER_AGAIN: m_imgui->end(); - // Make sure that the supports are (not) visible as they should be. This - // is done on each refresh because the user can switch the editing mode - // before background process finishes. - force_refresh = m_parent.toggle_sla_auxiliaries_visibility( - ! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); - if (remove_selected || remove_all) { force_refresh = false; m_parent.set_as_dirty(); @@ -1052,8 +1046,9 @@ void GLGizmoSlaSupports::on_set_state() reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_c->m_model_object) + if (m_c->m_model_object && ! m_c->m_cavity_mesh) m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; @@ -1305,6 +1300,9 @@ void GLGizmoSlaSupports::switch_to_editing_mode() for (const sla::SupportPoint& sp : m_normal_cache) m_editing_cache.emplace_back(sp); select_point(NoPoints); + + m_parent.toggle_sla_auxiliaries_visibility(false, m_c->m_model_object, m_c->m_active_instance); + m_parent.set_as_dirty(); } @@ -1313,6 +1311,8 @@ void GLGizmoSlaSupports::disable_editing_mode() if (m_editing_mode) { m_editing_mode = false; wxGetApp().plater()->leave_gizmos_stack(); + m_parent.toggle_sla_auxiliaries_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.set_as_dirty(); } } From d9786f2bcddf2d79aead22354b7b8ca99d5736fd Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 16 Jan 2020 13:08:37 +0100 Subject: [PATCH 115/130] Fixed SLA gizmos picking problem - hollowed mesh was not rendered for picking --- src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp | 28 +++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 31 ++++++++++++-------- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 1 + 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index a832b5e5c0..d02fdd0776 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -115,6 +115,23 @@ void GLGizmoHollow::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + + render_hollowed_mesh(); + + if (m_quadric != nullptr && selection.is_from_single_instance()) + render_points(selection, false); + + m_selection_rectangle.render(m_parent); + render_clipping_plane(selection); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoHollow::render_hollowed_mesh() const +{ if (m_c->m_volume_with_cavity) { m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); m_parent.get_shader().start_using(); @@ -132,16 +149,6 @@ void GLGizmoHollow::on_render() const m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } - - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); - - if (m_quadric != nullptr && selection.is_from_single_instance()) - render_points(selection, false); - - m_selection_rectangle.render(m_parent); - render_clipping_plane(selection); - - glsafe(::glDisable(GL_BLEND)); } @@ -241,6 +248,7 @@ void GLGizmoHollow::on_render_for_picking() const glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); + render_hollowed_mesh(); } void GLGizmoHollow::render_points(const Selection& selection, bool picking) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index b6a125972a..f6560c861e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -52,6 +52,7 @@ private: void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; + void render_hollowed_mesh() const; bool is_mesh_update_necessary() const; void update_mesh(); void hollow_mesh(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 45a766ccc1..c6e0d9007b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -126,6 +126,23 @@ void GLGizmoSlaSupports::on_render() const glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); + m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); + + render_hollowed_mesh(); + + if (m_quadric != nullptr && selection.is_from_single_instance()) + render_points(selection, false); + + m_selection_rectangle.render(m_parent); + render_clipping_plane(selection); + + glsafe(::glDisable(GL_BLEND)); +} + + + +void GLGizmoSlaSupports::render_hollowed_mesh() const +{ if (m_c->m_volume_with_cavity) { m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); m_parent.get_shader().start_using(); @@ -143,19 +160,6 @@ void GLGizmoSlaSupports::on_render() const m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); } - // Show/hide the original object - //m_parent.toggle_model_objects_visibility(! m_editing_mode && ! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - //m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); - - m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); - - if (m_quadric != nullptr && selection.is_from_single_instance()) - render_points(selection, false); - - m_selection_rectangle.render(m_parent); - render_clipping_plane(selection); - - glsafe(::glDisable(GL_BLEND)); } @@ -256,6 +260,7 @@ void GLGizmoSlaSupports::on_render_for_picking() const glsafe(::glEnable(GL_DEPTH_TEST)); render_points(selection, true); + render_hollowed_mesh(); } void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 7700ad3a61..3697e7af69 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -90,6 +90,7 @@ private: //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; + void render_hollowed_mesh() const; bool is_mesh_update_necessary() const; void update_mesh(); bool unsaved_changes() const; From 79e546816da97356fa0f4f74797048fc30aa926a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 16 Jan 2020 14:03:43 +0100 Subject: [PATCH 116/130] Fixed unit test (sla_print_tests - random number generator seeding) --- tests/sla_print/sla_print_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index b08c931e31..f34fb90968 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -127,6 +127,7 @@ TEST_CASE("Support point generator should be deterministic if seeded", for (int i = 0; i < 20; ++i) { point_gen.output().clear(); + point_gen.seed(0); point_gen.execute(slices, slicegrid); REQUIRE(point_gen.output().size() == ptnum); REQUIRE(checksum == get_chksum(point_gen.output())); From fada7224f17a189d5ea749633da16b81cb35a9e2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jan 2020 13:20:17 +0100 Subject: [PATCH 117/130] MeshBooleans with CGAL only --- sandboxes/CMakeLists.txt | 2 +- sandboxes/meshboolean/CMakeLists.txt | 8 +- sandboxes/meshboolean/MeshBoolean.cpp | 131 +++++++++++------------ src/libslic3r/MeshBoolean.cpp | 143 ++++++++++++++++++++++---- src/libslic3r/MeshBoolean.hpp | 21 ++++ 5 files changed, 209 insertions(+), 96 deletions(-) diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 181c70d48e..a2bd13bb0b 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,4 +1,4 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) -#add_subdirectory(meshboolean) +add_subdirectory(meshboolean) add_subdirectory(opencsg) diff --git a/sandboxes/meshboolean/CMakeLists.txt b/sandboxes/meshboolean/CMakeLists.txt index 17e876573b..55fb42fd14 100644 --- a/sandboxes/meshboolean/CMakeLists.txt +++ b/sandboxes/meshboolean/CMakeLists.txt @@ -1,12 +1,6 @@ -if (SLIC3R_STATIC) - set(CGAL_Boost_USE_STATIC_LIBS ON) -endif () - -find_package(CGAL REQUIRED) - add_executable(meshboolean MeshBoolean.cpp) -target_link_libraries(meshboolean libslic3r CGAL::CGAL) +target_link_libraries(meshboolean libslic3r) if (WIN32) prusaslicer_copy_dlls(meshboolean) diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp index 1aaa4d2b8b..3fd45ccffd 100644 --- a/sandboxes/meshboolean/MeshBoolean.cpp +++ b/sandboxes/meshboolean/MeshBoolean.cpp @@ -1,85 +1,76 @@ -#include -#undef PI -#include -//#undef IGL_STATIC_LIBRARY -#include - -#include #include +#include -#include +#include +#include +#include +#include +#include + +#include -#include #include namespace Slic3r { -bool its_write_obj(const Eigen::MatrixXd &V, Eigen::MatrixXi &F, 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 < V.rows(); ++ i) - fprintf(fp, "v %lf %lf %lf\n", V(i, 0), V(i, 1), V(i, 2)); - for (size_t i = 0; i < F.rows(); ++ i) - fprintf(fp, "f %d %d %d\n", F(i, 0) + 1, F(i, 1) + 1, F(i, 2) + 1); - fclose(fp); - return true; -} - -void mesh_boolean_test(const std::string &fname) -{ - using namespace Eigen; - using namespace std; -// igl::readOFF(TUTORIAL_SHARED_PATH "/cheburashka.off",VA,FA); -// igl::readOFF(TUTORIAL_SHARED_PATH "/decimated-knight.off",VB,FB); - // Plot the mesh with pseudocolors -// igl::opengl::glfw::Viewer viewer; - - // Initialize -// update(viewer); - - //igl::copyleft::cgal::mesh_boolean(VA,FA,VB,FB,boolean_type,VC,FC,J); - - - std::cout << fname.c_str() << std::endl; - TriangleMesh mesh; - - mesh.ReadSTLFile(fname.c_str()); - mesh.repair(true); - its_write_obj(mesh.its, (fname + "-imported0.obj").c_str()); - - - Eigen::MatrixXd VA,VB,VC; - Eigen::VectorXi J,I; - Eigen::MatrixXi FA,FB,FC; - igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION); - - - typedef Eigen::Map> MapMatrixXfUnaligned; - typedef Eigen::Map> MapMatrixXiUnaligned; - - Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); - Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); - - its_write_obj(V, F, (fname + "-imported.obj").c_str()); - // Self-union to clean up - igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC); - - its_write_obj(VC, FC, (fname + "-fixed.obj").c_str()); -} - } // namespace Slic3r int main(const int argc, const char * argv[]) { - if (argc < 1) return -1; + using namespace Slic3r; - Slic3r::mesh_boolean_test(argv[1]); + if (argc < 1) return EXIT_FAILURE; + + DynamicPrintConfig cfg; + auto model = Model::read_from_file(argv[1], &cfg); + + if (model.objects.empty()) return EXIT_SUCCESS; + + SLAPrint print; + print.apply(model, cfg); + PrintBase::TaskParams task; + task.to_object_step = slaposHollowing; + + print.set_task(task); + print.process(); + + Benchmark bench; + + for (SLAPrintObject *po : print.objects()) { + TriangleMesh holes; + sla::DrainHoles holepts = po->transformed_drainhole_points(); + + for (auto &hole: holepts) + holes.merge(sla::to_triangle_mesh(hole.to_mesh())); + + TriangleMesh hollowed_mesh = po->transformed_mesh(); + hollowed_mesh.merge(po->hollowed_interior_mesh()); + + hollowed_mesh.require_shared_vertices(); + holes.require_shared_vertices(); + + TriangleMesh drilled_mesh_igl = hollowed_mesh; + bench.start(); + MeshBoolean::minus(drilled_mesh_igl, holes); + bench.stop(); + + std::cout << "Mesh boolean duration with IGL: " << bench.getElapsedSec() << std::endl; + + TriangleMesh drilled_mesh_cgal = hollowed_mesh; + bench.start(); + MeshBoolean::cgal::self_union(drilled_mesh_cgal); + MeshBoolean::cgal::minus(drilled_mesh_cgal, holes); + bench.stop(); + + std::cout << "Mesh boolean duration with CGAL: " << bench.getElapsedSec() << std::endl; + + std::string name("obj"), outf; + outf = name + "igl" + std::to_string(po->model_object()->id().id) + ".obj"; + drilled_mesh_igl.WriteOBJFile(outf.c_str()); + + outf = name + "cgal" + std::to_string(po->model_object()->id().id) + ".obj"; + drilled_mesh_cgal.WriteOBJFile(outf.c_str()); + } return 0; } diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index e639319a20..69db96d3fc 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -1,11 +1,15 @@ #include "MeshBoolean.hpp" +#include "libslic3r/TriangleMesh.hpp" +#undef PI // Include igl first. It defines "L" macro which then clashes with our localization #include #undef L -#include "libslic3r/TriangleMesh.hpp" - +// CGAL headers +#include +#include +#include namespace Slic3r { namespace MeshBoolean { @@ -13,27 +17,41 @@ namespace MeshBoolean { typedef Eigen::Map> MapMatrixXfUnaligned; typedef Eigen::Map> MapMatrixXiUnaligned; +typedef std::pair EigenMesh; + static TriangleMesh eigen_to_triangle_mesh(const Eigen::MatrixXd& VC, const Eigen::MatrixXi& FC) { - Pointf3s vertices; - for (size_t i=0; i facets; - for (size_t i=0; i facets(size_t(FC.rows())); + + for (Eigen::Index i = 0; i < VC.rows(); ++i) + points[size_t(i)] = VC.row(i); + + for (Eigen::Index i = 0; i < FC.rows(); ++i) + facets[size_t(i)] = FC.row(i); + + TriangleMesh out{points, facets}; out.require_shared_vertices(); return out; } +static EigenMesh triangle_mesh_to_eigen_mesh(const TriangleMesh &mesh) +{ + EigenMesh emesh; + emesh.first = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), + Eigen::Index(mesh.its.vertices.size()), + 3).cast(); + + emesh.second = MapMatrixXiUnaligned(mesh.its.indices.front().data(), + Eigen::Index(mesh.its.indices.size()), + 3); + return emesh; +} + void minus(TriangleMesh& A, const TriangleMesh& B) { - Eigen::MatrixXd VA = MapMatrixXfUnaligned(A.its.vertices.front().data(), A.its.vertices.size(), 3).cast(); - Eigen::MatrixXi FA = MapMatrixXiUnaligned(A.its.indices.front().data(), A.its.indices.size(), 3); - Eigen::MatrixXd VB = MapMatrixXfUnaligned(B.its.vertices.front().data(), B.its.vertices.size(), 3).cast(); - Eigen::MatrixXi FB = MapMatrixXiUnaligned(B.its.indices.front().data(), B.its.indices.size(), 3); + auto [VA, FA] = triangle_mesh_to_eigen_mesh(A); + auto [VB, FB] = triangle_mesh_to_eigen_mesh(B); Eigen::MatrixXd VC; Eigen::MatrixXi FC; @@ -43,21 +61,110 @@ void minus(TriangleMesh& A, const TriangleMesh& B) A = eigen_to_triangle_mesh(VC, FC); } - void self_union(TriangleMesh& mesh) { - Eigen::MatrixXd V = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3).cast(); - Eigen::MatrixXi F = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); + auto [V, F] = triangle_mesh_to_eigen_mesh(mesh); Eigen::MatrixXd VC; Eigen::MatrixXi FC; igl::MeshBooleanType boolean_type(igl::MESH_BOOLEAN_TYPE_UNION); igl::copyleft::cgal::mesh_boolean(V, F, Eigen::MatrixXd(), Eigen::MatrixXi(), boolean_type, VC, FC); + mesh = eigen_to_triangle_mesh(VC, FC); } +namespace cgal { +namespace CGALProc = CGAL::Polygon_mesh_processing; +namespace CGALParams = CGAL::Polygon_mesh_processing::parameters; + +using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; +using _CGALMesh = CGAL::Surface_mesh; + +struct CGALMesh { _CGALMesh m; }; + +static void triangle_mesh_to_cgal(const TriangleMesh &M, _CGALMesh &out) +{ + for (const Vec3f &v : M.its.vertices) + out.add_vertex(_CGALMesh::Point(v.x(), v.y(), v.z())); + + for (const Vec3crd &face : M.its.indices) { + auto f = face.cast(); + out.add_face(f(0), f(1), f(2)); + } +} + +static TriangleMesh cgal_to_triangle_mesh(const _CGALMesh &cgalmesh) +{ + Pointf3s points; + std::vector facets; + points.reserve(cgalmesh.num_vertices()); + facets.reserve(cgalmesh.num_faces()); + + for (auto &vi : cgalmesh.vertices()) { + auto &v = cgalmesh.point(vi); // Don't ask... + points.emplace_back(v.x(), v.y(), v.z()); + } + + for (auto &face : cgalmesh.faces()) { + auto vtc = cgalmesh.vertices_around_face(cgalmesh.halfedge(face)); + int i = 0; + Vec3crd trface; + for (auto v : vtc) trface(i++) = int(v.idx()); + facets.emplace_back(trface); + } + + TriangleMesh out{points, facets}; + out.require_shared_vertices(); + return out; +} + +std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M) +{ + auto out = std::make_unique(); + triangle_mesh_to_cgal(M, out->m); + return out; +} + +void cgal_to_triangle_mesh(const CGALMesh &cgalmesh, TriangleMesh &out) +{ + out = cgal_to_triangle_mesh(cgalmesh.m); +} + +void minus(CGALMesh &A, CGALMesh &B) +{ + CGALProc::corefine_and_compute_difference(A.m, B.m, A.m); +} + +void self_union(CGALMesh &A) +{ + CGALProc::corefine(A.m, A.m); +} + +void minus(TriangleMesh &A, const TriangleMesh &B) +{ + CGALMesh meshA; + CGALMesh meshB; + triangle_mesh_to_cgal(A, meshA.m); + triangle_mesh_to_cgal(B, meshB.m); + + CGALMesh meshResult; + CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m); + + A = cgal_to_triangle_mesh(meshResult.m); +} + +void self_union(TriangleMesh &m) +{ + _CGALMesh cgalmesh; + triangle_mesh_to_cgal(m, cgalmesh); + CGALProc::corefine(cgalmesh, cgalmesh); + + m = cgal_to_triangle_mesh(cgalmesh); +} + +} // namespace cgal } // namespace MeshBoolean } // namespace Slic3r diff --git a/src/libslic3r/MeshBoolean.hpp b/src/libslic3r/MeshBoolean.hpp index 783bde3f58..14e3d3b7b2 100644 --- a/src/libslic3r/MeshBoolean.hpp +++ b/src/libslic3r/MeshBoolean.hpp @@ -1,6 +1,7 @@ #ifndef libslic3r_MeshBoolean_hpp_ #define libslic3r_MeshBoolean_hpp_ +#include namespace Slic3r { @@ -11,6 +12,26 @@ namespace MeshBoolean { void minus(TriangleMesh& A, const TriangleMesh& B); void self_union(TriangleMesh& mesh); +namespace cgal { + +struct CGALMesh; + +std::unique_ptr triangle_mesh_to_cgal(const TriangleMesh &M); +void cgal_to_triangle_mesh(const CGALMesh &cgalmesh, TriangleMesh &out); + +// Do boolean mesh difference with CGAL bypassing igl. +void minus(TriangleMesh &A, const TriangleMesh &B); + +// Do self union only with CGAL. +void self_union(TriangleMesh& mesh); + +// does A = A - B +// CGAL takes non-const objects as arguments. I suppose it doesn't change B but +// there is no official garantee. +void minus(CGALMesh &A, CGALMesh &B); +void self_union(CGALMesh &A); + +} } // namespace MeshBoolean } // namespace Slic3r From a1dc7a5c41102c991bab3aed0682cef7d7a9582a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jan 2020 14:25:01 +0100 Subject: [PATCH 118/130] Fix build issues on Windows --- CMakeLists.txt | 1 + cmake/modules/FindOpenVDB.cmake | 2 +- sandboxes/meshboolean/MeshBoolean.cpp | 2 +- sandboxes/opencsg/CMakeLists.txt | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 218e53add6..a3c2eca123 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ endif() find_package(OpenVDB 5.0 REQUIRED COMPONENTS openvdb) if(OpenVDB_FOUND) slic3r_remap_configs(IlmBase::Half RelWithDebInfo Release) + slic3r_remap_configs(Blosc::blosc RelWithDebInfo Release) endif() set(TOP_LEVEL_PROJECT_DIR ${PROJECT_SOURCE_DIR}) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 7bc44bebb5..b9d19ed1eb 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -247,7 +247,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_${_BUILD_TYPE}}) - if (NOT MSVC AND NOT OpenVDB_${COMPONENT}_LIBRARY) + if (NOT OpenVDB_${COMPONENT}_LIBRARY) set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) endif () diff --git a/sandboxes/meshboolean/MeshBoolean.cpp b/sandboxes/meshboolean/MeshBoolean.cpp index 3fd45ccffd..d339ef5c30 100644 --- a/sandboxes/meshboolean/MeshBoolean.cpp +++ b/sandboxes/meshboolean/MeshBoolean.cpp @@ -19,7 +19,7 @@ int main(const int argc, const char * argv[]) { using namespace Slic3r; - if (argc < 1) return EXIT_FAILURE; + if (argc <= 1) return EXIT_FAILURE; DynamicPrintConfig cfg; auto model = Model::read_from_file(argv[1], &cfg); diff --git a/sandboxes/opencsg/CMakeLists.txt b/sandboxes/opencsg/CMakeLists.txt index e9a51b0f4e..ec1f4cae91 100644 --- a/sandboxes/opencsg/CMakeLists.txt +++ b/sandboxes/opencsg/CMakeLists.txt @@ -20,6 +20,7 @@ target_link_libraries(opencsg_example libslic3r) target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS}) target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS}) +slic3r_remap_configs(OpenCSG::opencsg RelWithDebInfo Release) target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} OpenCSG::opencsg GLEW::GLEW From ea6844c31da7f31980cc3265e8d170dc8dd5608b Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jan 2020 15:19:46 +0100 Subject: [PATCH 119/130] Follow up for Windows build fixes --- cmake/modules/FindOpenVDB.cmake | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index b9d19ed1eb..6d3bc06ec8 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -242,6 +242,8 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) else() set(OpenVDB_${COMPONENT}_FOUND FALSE) endif() + + set(OpenVDB_${COMPONENT}_LIBRARY ${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}) else () string(TOUPPER "${CMAKE_BUILD_TYPE}" _BUILD_TYPE) @@ -259,6 +261,7 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) set(OpenVDB_${COMPONENT}_FOUND FALSE) endif() endif () + endforeach() if(UNIX AND OPENVDB_USE_STATIC_LIBS) @@ -522,18 +525,17 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) IMPORTED_LINK_DEPENDENT_LIBRARIES "${_OPENVDB_HIDDEN_DEPENDENCIES}" # non visible deps INTERFACE_LINK_LIBRARIES "${_OPENVDB_VISIBLE_DEPENDENCIES}" # visible deps (headers) INTERFACE_COMPILE_FEATURES cxx_std_11 - ) - - if (_is_multi) - set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" - ) - else () - set_target_properties(OpenVDB::${COMPONENT} PROPERTIES - IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" - ) - endif () + ) + + # if (_is_multi) + # set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + # IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" + # IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" + # ) + # endif () if (OPENVDB_USE_STATIC_LIBS) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES From 70ecb634b7e6bd29cf69b791abf92f3b819a5301 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 16 Jan 2020 15:38:59 +0100 Subject: [PATCH 120/130] fix gui artifacts on Windows --- sandboxes/opencsg/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandboxes/opencsg/main.cpp b/sandboxes/opencsg/main.cpp index ec6c01789a..adf9cc457f 100644 --- a/sandboxes/opencsg/main.cpp +++ b/sandboxes/opencsg/main.cpp @@ -445,7 +445,7 @@ void MyFrame::activate_canvas_display() m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) { // This is required even though dc is not used otherwise. - wxPaintDC dc(this); + wxPaintDC dc(m_canvas.get()); const wxSize csize = m_canvas->GetClientSize(); m_canvas->get_display()->set_screen_size(csize.x, csize.y); m_canvas->get_display()->repaint(); From 49678fe418c8bbdfcd60cc0996a0e2c4f9b44033 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 17 Jan 2020 12:45:46 +0100 Subject: [PATCH 121/130] Fix FindOpenVDB on Linux --- cmake/modules/FindOpenVDB.cmake | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 6d3bc06ec8..3e9e5f082a 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -526,16 +526,14 @@ foreach(COMPONENT ${OpenVDB_FIND_COMPONENTS}) INTERFACE_LINK_LIBRARIES "${_OPENVDB_VISIBLE_DEPENDENCIES}" # visible deps (headers) INTERFACE_COMPILE_FEATURES cxx_std_11 IMPORTED_LOCATION "${OpenVDB_${COMPONENT}_LIBRARY}" - IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" - IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" ) - # if (_is_multi) - # set_target_properties(OpenVDB::${COMPONENT} PROPERTIES - # IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" - # IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" - # ) - # endif () + if (_is_multi) + set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + IMPORTED_LOCATION_RELEASE "${OpenVDB_${COMPONENT}_LIBRARY_RELEASE}" + IMPORTED_LOCATION_DEBUG "${OpenVDB_${COMPONENT}_LIBRARY_DEBUG}" + ) + endif () if (OPENVDB_USE_STATIC_LIBS) set_target_properties(OpenVDB::${COMPONENT} PROPERTIES From d8f2c8cdab10247fe54421bc3aaf46f668ad81a3 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 17 Jan 2020 16:01:49 +0100 Subject: [PATCH 122/130] Fixed a bug in the hole-aware raycaster --- src/libslic3r/SLA/Hollowing.cpp | 5 ++++- tests/sla_print/sla_raycast_tests.cpp | 32 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 8dc2d30929..5ab85c1cc4 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -223,7 +223,10 @@ bool DrainHole::get_intersections(const Vec3f& s, const Vec3f& dir, // they are on the cylinder and not past it: for (int i=-1; i<=1 && found !=2; i+=2) { Vec3f isect = closest + i*dist * projected_ray.direction(); - float par = (isect-proj_origin).norm() / par_scale; + Vec3f to_isect = isect-proj_origin; + float par = to_isect.norm() / par_scale; + if (to_isect.normalized().dot(proj_dir.normalized()) < 0.f) + par *= -1.f; Vec3d hit_normal = (pos-isect).normalized().cast(); isect = ray.pointAt(par); // check that the intersection is between the base planes: diff --git a/tests/sla_print/sla_raycast_tests.cpp b/tests/sla_print/sla_raycast_tests.cpp index 4a994f2a93..74c7994723 100644 --- a/tests/sla_print/sla_raycast_tests.cpp +++ b/tests/sla_print/sla_raycast_tests.cpp @@ -8,6 +8,38 @@ using namespace Slic3r; +// First do a simple test of the hole raycaster. +TEST_CASE("Raycaster - find intersections of a line and cylinder") +{ + sla::DrainHole hole{Vec3f(0,0,0), Vec3f(0,0,1), 5, 10}; + std::array, 2> out; + Vec3f s; + Vec3f dir; + + // Start inside the hole and cast perpendicular to its axis. + s = {-1.f, 0, 5.f}; + dir = {1.f, 0, 0}; + hole.get_intersections(s, dir, out); + REQUIRE(out[0].first == Approx(-4.f)); + REQUIRE(out[1].first == Approx(6.f)); + + // Start outside and cast parallel to axis. + s = {0, 0, -1.f}; + dir = {0, 0, 1.f}; + hole.get_intersections(s, dir, out); + REQUIRE(std::abs(out[0].first - 1.f) < 0.001f); + REQUIRE(std::abs(out[1].first - 11.f) < 0.001f); + + // Start outside and cast so that entry is in base and exit on the cylinder + s = {0, -1.f, -1.f}; + dir = {0, 1.f, 1.f}; + dir.normalize(); + hole.get_intersections(s, dir, out); + REQUIRE(std::abs(out[0].first - std::sqrt(2.f)) < 0.001f); + REQUIRE(std::abs(out[1].first - std::sqrt(72.f)) < 0.001f); +} + + // Create a simple scene with a 20mm cube and a big hole in the front wall // with 5mm radius. Then shoot rays from interesting positions and see where // they land. From ce34ae471650947216c820e4f6a6caf9600f7740 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jan 2020 10:38:40 +0100 Subject: [PATCH 123/130] Try to fix libatomic linkage on raspbian --- src/libslic3r/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 8a3817b95e..f31c1a2b9c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -262,6 +262,12 @@ target_link_libraries(libslic3r ${CMAKE_DL_LIBS} ) + +find_library(CXX_ATOMIC_LIBRARY atomic) +if (CXX_ATOMIC_LIBRARY) + target_link_libraries(libslic3r ${CXX_ATOMIC_LIBRARY}) +endif() + if (TARGET OpenVDB::openvdb) target_link_libraries(libslic3r OpenVDB::openvdb) endif() From 6cfad6848f06bbb8926eecce46aca3cd56a5ae01 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jan 2020 11:20:18 +0100 Subject: [PATCH 124/130] Fix incorrect filename case in test_3mf --- tests/libslic3r/test_3mf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 5215e2ebd2..f3e30ea615 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -26,7 +26,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { GIVEN("world vertices coordinates before save") { // load a model from stl file Model src_model; - std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.stl"; + std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/Prusa.stl"; load_stl(src_file.c_str(), &src_model); src_model.add_default_instances(); From 87f2578ee71c9baa13dcfc09c1afb4d751cb2ed3 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Jan 2020 12:50:48 +0100 Subject: [PATCH 125/130] Try to fix libatomic linkage on raspbian SPE-1073 --- src/libslic3r/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index f31c1a2b9c..9245e5c2af 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -262,10 +262,8 @@ target_link_libraries(libslic3r ${CMAKE_DL_LIBS} ) - -find_library(CXX_ATOMIC_LIBRARY atomic) -if (CXX_ATOMIC_LIBRARY) - target_link_libraries(libslic3r ${CXX_ATOMIC_LIBRARY}) +if (NOT MSVC) + target_link_libraries(libslic3r atomic) # no cmake magic for this... endif() if (TARGET OpenVDB::openvdb) From f8a5796ca5f676c8fa32629fdad6cb0a4559b4d4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 23 Jan 2020 10:57:51 +0100 Subject: [PATCH 126/130] add mesh simplification. SPE-1072 Working but flipped normals with the interior. Testing on treefrog passed Oversampling for hollowed mesh should not be less than 3x Flip back normals after simplify and remove redundant test code. --- src/libslic3r/CMakeLists.txt | 3 + src/libslic3r/OpenVDBUtils.cpp | 12 +- src/libslic3r/SLA/Hollowing.cpp | 69 ++- src/libslic3r/SimplifyMesh.cpp | 66 +++ src/libslic3r/SimplifyMesh.hpp | 25 + src/libslic3r/SimplifyMeshImpl.hpp | 699 ++++++++++++++++++++++++++ src/libslic3r/TriangleMesh.cpp | 28 ++ src/libslic3r/TriangleMesh.hpp | 1 + tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_hollowing.cpp | 4 + tests/libslic3r/test_meshsimplify.cpp | 11 + 11 files changed, 875 insertions(+), 44 deletions(-) create mode 100644 src/libslic3r/SimplifyMesh.cpp create mode 100644 src/libslic3r/SimplifyMesh.hpp create mode 100644 src/libslic3r/SimplifyMeshImpl.hpp create mode 100644 tests/libslic3r/test_meshsimplify.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 9245e5c2af..d01c01ec2d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -183,6 +183,9 @@ add_library(libslic3r STATIC MinAreaBoundingBox.cpp miniz_extension.hpp miniz_extension.cpp + SimplifyMesh.hpp + SimplifyMeshImpl.hpp + SimplifyMesh.cpp ${OpenVDBUtils_SOURCES} SLA/Common.hpp SLA/Common.cpp diff --git a/src/libslic3r/OpenVDBUtils.cpp b/src/libslic3r/OpenVDBUtils.cpp index c76bad96c5..c30052036e 100644 --- a/src/libslic3r/OpenVDBUtils.cpp +++ b/src/libslic3r/OpenVDBUtils.cpp @@ -84,9 +84,9 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh, template sla::Contour3D _volumeToMesh(const Grid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { openvdb::initialize(); @@ -110,9 +110,9 @@ sla::Contour3D _volumeToMesh(const Grid &grid, } TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, - double isovalue, - double adaptivity, - bool relaxDisorientedTriangles) + double isovalue, + double adaptivity, + bool relaxDisorientedTriangles) { return to_triangle_mesh( _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles)); diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 5ab85c1cc4..1ce0c4c679 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -24,41 +25,15 @@ template> inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } template> -inline void _scale(S s, Contour3D &m) -{ - for (auto &p : m.points) p *= s; -} +inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; } -template -remove_cvref_t _grid_to_mesh(const openvdb::FloatGrid &grid, - double isosurf, - double adapt); - -template<> -TriangleMesh _grid_to_mesh(const openvdb::FloatGrid &grid, - double isosurf, - double adapt) +static TriangleMesh _generate_interior(const TriangleMesh &mesh, + const JobController &ctl, + double min_thickness, + double voxel_scale, + double closing_dist) { - return grid_to_mesh(grid, isosurf, adapt); -} - -template<> -Contour3D _grid_to_mesh(const openvdb::FloatGrid &grid, - double isosurf, - double adapt) -{ - return grid_to_contour3d(grid, isosurf, adapt); -} - -template -remove_cvref_t _generate_interior(Mesh &&mesh, - const JobController &ctl, - double min_thickness, - double voxel_scale, - double closing_dist) -{ - using MMesh = remove_cvref_t; - MMesh imesh{std::forward(mesh)}; + TriangleMesh imesh{mesh}; _scale(voxel_scale, imesh); @@ -76,7 +51,7 @@ remove_cvref_t _generate_interior(Mesh &&mesh, if (!gridptr) { BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL"; - return MMesh{}; + return {}; } if (ctl.stopcondition()) return {}; @@ -93,7 +68,7 @@ remove_cvref_t _generate_interior(Mesh &&mesh, double iso_surface = D; double adaptivity = 0.; - auto omesh = _grid_to_mesh(*gridptr, iso_surface, adaptivity); + auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity); _scale(1. / voxel_scale, omesh); @@ -107,7 +82,8 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, const HollowingConfig &hc, const JobController & ctl) { - static const double MAX_OVERSAMPL = 7.; + static const double MIN_OVERSAMPL = 3.; + static const double MAX_OVERSAMPL = 8.; // I can't figure out how to increase the grid resolution through openvdb // API so the model will be scaled up before conversion and the result @@ -116,10 +92,27 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, // voxels. // // max 8x upscale, min is native voxel size - auto voxel_scale = (1.0 + MAX_OVERSAMPL * hc.quality); - return std::make_unique( + auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality; + auto meshptr = std::make_unique( _generate_interior(mesh, ctl, hc.min_thickness, voxel_scale, hc.closing_distance)); + + if (meshptr) { + + // This flips the normals to be outward facing... + meshptr->require_shared_vertices(); + indexed_triangle_set its = std::move(meshptr->its); + + Slic3r::simplify_mesh(its); + + // flip normals back... + for (stl_triangle_vertex_indices &ind : its.indices) + std::swap(ind(0), ind(2)); + + *meshptr = Slic3r::TriangleMesh{its}; + } + + return meshptr; } Contour3D DrainHole::to_mesh() const diff --git a/src/libslic3r/SimplifyMesh.cpp b/src/libslic3r/SimplifyMesh.cpp new file mode 100644 index 0000000000..d30ecfec57 --- /dev/null +++ b/src/libslic3r/SimplifyMesh.cpp @@ -0,0 +1,66 @@ +#include "SimplifyMesh.hpp" +#include "SimplifyMeshImpl.hpp" + +namespace SimplifyMesh { + +template<> struct vertex_traits { + using coord_type = float; + using compute_type = double; + + static inline float x(const stl_vertex &v) { return v.x(); } + static inline float& x(stl_vertex &v) { return v.x(); } + + static inline float y(const stl_vertex &v) { return v.y(); } + static inline float& y(stl_vertex &v) { return v.y(); } + + static inline float z(const stl_vertex &v) { return v.z(); } + static inline float& z(stl_vertex &v) { return v.z(); } +}; + +template<> struct mesh_traits { + using vertex_t = stl_vertex; + static size_t face_count(const indexed_triangle_set &m) + { + return m.indices.size(); + } + static size_t vertex_count(const indexed_triangle_set &m) + { + return m.vertices.size(); + } + static vertex_t vertex(const indexed_triangle_set &m, size_t idx) + { + return m.vertices[idx]; + } + static void vertex(indexed_triangle_set &m, size_t idx, const vertex_t &v) + { + m.vertices[idx] = v; + } + static Index3 triangle(const indexed_triangle_set &m, size_t idx) + { + std::array t; + for (size_t i = 0; i < 3; ++i) t[i] = size_t(m.indices[idx](int(i))); + return t; + } + static void triangle(indexed_triangle_set &m, size_t fidx, const Index3 &t) + { + auto &face = m.indices[fidx]; + face(0) = int(t[0]); face(1) = int(t[1]); face(2) = int(t[2]); + } + static void update(indexed_triangle_set &m, size_t vc, size_t fc) + { + m.vertices.resize(vc); + m.indices.resize(fc); + } +}; + +} // namespace SimplifyMesh + +namespace Slic3r { + +void simplify_mesh(indexed_triangle_set &m) +{ + SimplifyMesh::implementation::SimplifiableMesh sm{&m}; + sm.simplify_mesh_lossless(); +} + +} diff --git a/src/libslic3r/SimplifyMesh.hpp b/src/libslic3r/SimplifyMesh.hpp new file mode 100644 index 0000000000..fb3e73d049 --- /dev/null +++ b/src/libslic3r/SimplifyMesh.hpp @@ -0,0 +1,25 @@ +#ifndef MESHSIMPLIFY_HPP +#define MESHSIMPLIFY_HPP + +#include + +#include + +namespace Slic3r { + +void simplify_mesh(indexed_triangle_set &); + +// TODO: (but this can be done with IGL as well) +// void simplify_mesh(indexed_triangle_set &, int face_count, float agressiveness = 0.5f); + +template void simplify_mesh(TriangleMesh &m, Args &&...a) +{ + m.require_shared_vertices(); + simplify_mesh(m.its, std::forward(a)...); + m = TriangleMesh{m.its}; + m.require_shared_vertices(); +} + +} // namespace Slic3r + +#endif // MESHSIMPLIFY_H diff --git a/src/libslic3r/SimplifyMeshImpl.hpp b/src/libslic3r/SimplifyMeshImpl.hpp new file mode 100644 index 0000000000..6add08930c --- /dev/null +++ b/src/libslic3r/SimplifyMeshImpl.hpp @@ -0,0 +1,699 @@ +// /////////////////////////////////////////// +// +// Mesh Simplification Tutorial +// +// (C) by Sven Forstmann in 2014 +// +// License : MIT +// http://opensource.org/licenses/MIT +// +// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification +// +// 5/2016: Chris Rorden created minimal version for OSX/Linux/Windows compile +// https://github.com/sp4cerat/Fast-Quadric-Mesh-Simplification/ +// +// libslic3r refactor by tamasmeszaros + +#ifndef SIMPLIFYMESHIMPL_HPP +#define SIMPLIFYMESHIMPL_HPP + +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +namespace SimplifyMesh { + +using Bary = std::array; +using Index3 = std::array; + +template struct vertex_traits { + using coord_type = typename Vertex::coord_type; + using compute_type = coord_type; + + static coord_type x(const Vertex &v); + static coord_type& x(Vertex &v); + + static coord_type y(const Vertex &v); + static coord_type& y(Vertex &v); + + static coord_type z(const Vertex &v); + static coord_type& z(Vertex &v); +}; + +template struct mesh_traits { + using vertex_t = typename Mesh::vertex_t; + + static size_t face_count(const Mesh &m); + static size_t vertex_count(const Mesh &m); + static vertex_t vertex(const Mesh &m, size_t vertex_idx); + static void vertex(Mesh &m, size_t vertex_idx, const vertex_t &v); + static Index3 triangle(const Mesh &m, size_t face_idx); + static void triangle(Mesh &m, size_t face_idx, const Index3 &t); + static void update(Mesh &m, size_t vertex_count, size_t face_count); +}; + +namespace implementation { + +// A shorter C++14 style form of the enable_if metafunction +template +using enable_if_t = typename std::enable_if::type; + +// Meta predicates for floating, 'scaled coord' and generic arithmetic types +template +using FloatingOnly = enable_if_t::value, O>; + +template +using IntegerOnly = enable_if_t::value, O>; + +template +using ArithmeticOnly = enable_if_t::value, O>; + +template< class T > +struct remove_cvref { + using type = typename std::remove_cv< + typename std::remove_reference::type>::type; +}; + +template< class T > +using remove_cvref_t = typename remove_cvref::type; + +struct DOut { +#ifndef NDEBUG + std::ostream& out = std::cout; +#endif +}; + +template +inline DOut&& operator<<( DOut&& out, T&& d) { +#ifndef NDEBUG + out.out << d; +#endif + return std::move(out); +} + +inline DOut dout() { return DOut(); } + +template FloatingOnly is_approx(T val, T ref) { return std::abs(val - ref) < 1e-8; } +template IntegerOnly is_approx(T val, T ref) { val == ref; } + +template class SymetricMatrix { +public: + + explicit SymetricMatrix(ArithmeticOnly c = T()) { std::fill(m, m + N, c); } + + SymetricMatrix(T m11, T m12, T m13, T m14, + T m22, T m23, T m24, + T m33, T m34, + T m44) + { + m[0] = m11; m[1] = m12; m[2] = m13; m[3] = m14; + m[4] = m22; m[5] = m23; m[6] = m24; + m[7] = m33; m[8] = m34; + m[9] = m44; + } + + // Make plane + SymetricMatrix(T a, T b, T c, T d) + { + m[0] = a * a; m[1] = a * b; m[2] = a * c; m[3] = a * d; + m[4] = b * b; m[5] = b * c; m[6] = b * d; + m[7] = c * c; m[8] = c * d; + m[9] = d * d; + } + + T operator[](int c) const { return m[c]; } + + // Determinant + T det(int a11, int a12, int a13, + int a21, int a22, int a23, + int a31, int a32, int a33) + { + T det = m[a11] * m[a22] * m[a33] + m[a13] * m[a21] * m[a32] + + m[a12] * m[a23] * m[a31] - m[a13] * m[a22] * m[a31] - + m[a11] * m[a23] * m[a32] - m[a12] * m[a21] * m[a33]; + + return det; + } + + const SymetricMatrix operator+(const SymetricMatrix& n) const + { + return SymetricMatrix(m[0] + n[0], m[1] + n[1], m[2] + n[2], m[3]+n[3], + m[4] + n[4], m[5] + n[5], m[6] + n[6], + m[7] + n[7], m[8] + n[8], + m[9] + n[9]); + } + + SymetricMatrix& operator+=(const SymetricMatrix& n) + { + m[0]+=n[0]; m[1]+=n[1]; m[2]+=n[2]; m[3]+=n[3]; + m[4]+=n[4]; m[5]+=n[5]; m[6]+=n[6]; m[7]+=n[7]; + m[8]+=n[8]; m[9]+=n[9]; + + return *this; + } + + T m[N]; +}; + +template using TCoord = typename vertex_traits>::coord_type; +template using TCompute = typename vertex_traits>::compute_type; +template inline TCoord x(const V &v) { return vertex_traits>::x(v); } +template inline TCoord y(const V &v) { return vertex_traits>::y(v); } +template inline TCoord z(const V &v) { return vertex_traits>::z(v); } +template inline TCoord& x(V &v) { return vertex_traits>::x(v); } +template inline TCoord& y(V &v) { return vertex_traits>::y(v); } +template inline TCoord& z(V &v) { return vertex_traits>::z(v); } +template using TVertex = typename mesh_traits>::vertex_t; +template using TMeshCoord = TCoord>; + +template TCompute dot(const Vertex &v1, const Vertex &v2) +{ + return TCompute(x(v1)) * x(v2) + + TCompute(y(v1)) * y(v2) + + TCompute(z(v1)) * z(v2); +} + +template Vertex cross(const Vertex &a, const Vertex &b) +{ + return Vertex{y(a) * z(b) - z(a) * y(b), + z(a) * x(b) - x(a) * z(b), + x(a) * y(b) - y(a) * x(b)}; +} + +template TCompute lengthsq(const Vertex &v) +{ + return TCompute(x(v)) * x(v) + TCompute(y(v)) * y(v) + + TCompute(z(v)) * z(v); +} + +template void normalize(Vertex &v) +{ + double square = std::sqrt(lengthsq(v)); + x(v) /= square; y(v) /= square; z(v) /= square; +} + +using Bary = std::array; + +template +Bary barycentric(const Vertex &p, const Vertex &a, const Vertex &b, const Vertex &c) +{ + Vertex v0 = (b - a); + Vertex v1 = (c - a); + Vertex v2 = (p - a); + + double d00 = dot(v0, v0); + double d01 = dot(v0, v1); + double d11 = dot(v1, v1); + double d20 = dot(v2, v0); + double d21 = dot(v2, v1); + double denom = d00 * d11 - d01 * d01; + double v = (d11 * d20 - d01 * d21) / denom; + double w = (d00 * d21 - d01 * d20) / denom; + double u = 1.0 - v - w; + + return {u, v, w}; +} + +template class SimplifiableMesh { + Mesh *m_mesh; + + using Vertex = TVertex; + using Coord = TMeshCoord; + using HiPrecison = TCompute>; + using SymMat = SymetricMatrix; + + struct FaceInfo { + size_t idx; + double err[4] = {0.}; + bool deleted = false, dirty = false; + Vertex n; + explicit FaceInfo(size_t id): idx(id) {} + }; + + struct VertexInfo { + size_t idx; + size_t tstart = 0, tcount = 0; + bool border = false; + SymMat q; + explicit VertexInfo(size_t id): idx(id) {} + }; + + struct Ref { size_t face; size_t vertex; }; + + std::vector m_refs; + std::vector m_faceinfo; + std::vector m_vertexinfo; + + void compact_faces(); + void compact(); + + size_t mesh_vcount() const { return mesh_traits::vertex_count(*m_mesh); } + size_t mesh_facecount() const { return mesh_traits::face_count(*m_mesh); } + + size_t vcount() const { return m_vertexinfo.size(); } + + inline Vertex read_vertex(size_t vi) const + { + return mesh_traits::vertex(*m_mesh, vi); + } + + inline Vertex read_vertex(const VertexInfo &vinf) const + { + return read_vertex(vinf.idx); + } + + inline void write_vertex(size_t idx, const Vertex &v) const + { + mesh_traits::vertex(*m_mesh, idx, v); + } + + inline void write_vertex(const VertexInfo &vinf, const Vertex &v) const + { + write_vertex(vinf.idx, v); + } + + inline Index3 read_triangle(size_t fi) const + { + return mesh_traits::triangle(*m_mesh, fi); + } + + inline Index3 read_triangle(const FaceInfo &finf) const + { + return read_triangle(finf.idx); + } + + inline void write_triangle(size_t idx, const Index3 &t) + { + return mesh_traits::triangle(*m_mesh, idx, t); + } + + inline void write_triangle(const FaceInfo &finf, const Index3 &t) + { + return write_triangle(finf.idx, t); + } + + inline std::array triangle_vertices(const Index3 &f) const + { + std::array p; + for (size_t i = 0; i < 3; ++i) p[i] = read_vertex(f[i]); + return p; + } + + // Error between vertex and Quadric + static double vertex_error(const SymMat &q, const Vertex &v) + { + Coord _x = x(v) , _y = y(v), _z = z(v); + return q[0] * _x * _x + 2 * q[1] * _x * _y + 2 * q[2] * _x * _z + + 2 * q[3] * _x + q[4] * _y * _y + 2 * q[5] * _y * _z + + 2 * q[6] * _y + q[7] * _z * _z + 2 * q[8] * _z + q[9]; + } + + // Error for one edge + double calculate_error(size_t id_v1, size_t id_v2, Vertex &p_result); + + void calculate_error(FaceInfo &fi) + { + Vertex p; + Index3 t = read_triangle(fi); + for (size_t j = 0; j < 3; ++j) + fi.err[j] = calculate_error(t[j], t[(j + 1) % 3], p); + + fi.err[3] = std::min(fi.err[0], std::min(fi.err[1], fi.err[2])); + } + + void update_mesh(int iteration); + + // Update triangle connections and edge error after a edge is collapsed + void update_triangles(size_t i, VertexInfo &vi, std::vector &deleted, int &deleted_triangles); + + // Check if a triangle flips when this edge is removed + bool flipped(const Vertex &p, size_t i0, size_t i1, VertexInfo &v0, VertexInfo &v1, std::vector &deleted); + +public: + + explicit SimplifiableMesh(Mesh *m) : m_mesh{m} + { + static_assert( + std::is_arithmetic::value, + "Coordinate type of mesh has to be an arithmetic type!"); + + m_faceinfo.reserve(mesh_traits::face_count(*m)); + m_vertexinfo.reserve(mesh_traits::vertex_count(*m)); + for (size_t i = 0; i < mesh_facecount(); ++i) m_faceinfo.emplace_back(i); + for (size_t i = 0; i < mesh_vcount(); ++i) m_vertexinfo.emplace_back(i); + + } + + void simplify_mesh_lossless(); +}; + + +template void SimplifiableMesh::compact_faces() +{ + auto it = std::remove_if(m_faceinfo.begin(), m_faceinfo.end(), + [](const FaceInfo &inf) { return inf.deleted; }); + + m_faceinfo.erase(it, m_faceinfo.end()); +} + +template void SimplifiableMesh::compact() +{ + for (auto &vi : m_vertexinfo) vi.tcount = 0; + + compact_faces(); + + for (FaceInfo &fi : m_faceinfo) + for (size_t vidx : read_triangle(fi)) m_vertexinfo[vidx].tcount = 1; + + size_t dst = 0; + for (VertexInfo &vi : m_vertexinfo) { + if (vi.tcount) { + vi.tstart = dst; + write_vertex(dst++, read_vertex(vi)); + } + } + + size_t vertex_count = dst; + + dst = 0; + for (const FaceInfo &fi : m_faceinfo) { + Index3 t = read_triangle(fi); + for (size_t &idx : t) idx = m_vertexinfo[idx].tstart; + write_triangle(dst++, t); + } + + mesh_traits::update(*m_mesh, vertex_count, m_faceinfo.size()); +} + +template +double SimplifiableMesh::calculate_error(size_t id_v1, size_t id_v2, Vertex &p_result) +{ + // compute interpolated vertex + + SymMat q = m_vertexinfo[id_v1].q + m_vertexinfo[id_v2].q; + + bool border = m_vertexinfo[id_v1].border & m_vertexinfo[id_v2].border; + double error = 0; + HiPrecison det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); + + if (!is_approx(det, HiPrecison(0)) && !border) + { + // q_delta is invertible + x(p_result) = Coord(-1) / det * q.det(1, 2, 3, 4, 5, 6, 5, 7, 8); // vx = A41/det(q_delta) + y(p_result) = Coord( 1) / det * q.det(0, 2, 3, 1, 5, 6, 2, 7, 8); // vy = A42/det(q_delta) + z(p_result) = Coord(-1) / det * q.det(0, 1, 3, 1, 4, 6, 2, 5, 8); // vz = A43/det(q_delta) + + error = vertex_error(q, p_result); + } else { + // det = 0 -> try to find best result + Vertex p1 = read_vertex(id_v1); + Vertex p2 = read_vertex(id_v2); + Vertex p3 = (p1 + p2) / 2; + double error1 = vertex_error(q, p1); + double error2 = vertex_error(q, p2); + double error3 = vertex_error(q, p3); + error = std::min(error1, std::min(error2, error3)); + + if (is_approx(error1, error)) p_result = p1; + if (is_approx(error2, error)) p_result = p2; + if (is_approx(error3, error)) p_result = p3; + } + + return error; +} + +template void SimplifiableMesh::update_mesh(int iteration) +{ + if (iteration > 0) compact_faces(); + + assert(mesh_vcount() == m_vertexinfo.size()); + + // + // Init Quadrics by Plane & Edge Errors + // + // required at the beginning ( iteration == 0 ) + // recomputing during the simplification is not required, + // but mostly improves the result for closed meshes + // + if (iteration == 0) { + + for (VertexInfo &vinf : m_vertexinfo) vinf.q = SymMat{}; + for (FaceInfo &finf : m_faceinfo) { + Index3 t = read_triangle(finf); + std::array p = triangle_vertices(t); + Vertex n = cross(Vertex(p[1] - p[0]), Vertex(p[2] - p[0])); + normalize(n); + finf.n = n; + + for (size_t fi : t) + m_vertexinfo[fi].q += SymMat(x(n), y(n), z(n), -dot(n, p[0])); + + calculate_error(finf); + } + } + + // Init Reference ID list + for (VertexInfo &vi : m_vertexinfo) { vi.tstart = 0; vi.tcount = 0; } + + for (FaceInfo &fi : m_faceinfo) + for (size_t vidx : read_triangle(fi)) + m_vertexinfo[vidx].tcount++; + + size_t tstart = 0; + for (VertexInfo &vi : m_vertexinfo) { + vi.tstart = tstart; + tstart += vi.tcount; + vi.tcount = 0; + } + + // Write References + m_refs.resize(m_faceinfo.size() * 3); + for (size_t i = 0; i < m_faceinfo.size(); ++i) { + const FaceInfo &fi = m_faceinfo[i]; + Index3 t = read_triangle(fi); + for (size_t j = 0; j < 3; ++j) { + VertexInfo &vi = m_vertexinfo[t[j]]; + + assert(vi.tstart + vi.tcount < m_refs.size()); + + Ref &ref = m_refs[vi.tstart + vi.tcount]; + ref.face = i; + ref.vertex = j; + vi.tcount++; + } + } + + // Identify boundary : vertices[].border=0,1 + if (iteration == 0) { + for (VertexInfo &vi: m_vertexinfo) vi.border = false; + + std::vector vcount, vids; + + for (VertexInfo &vi: m_vertexinfo) { + vcount.clear(); + vids.clear(); + + for(size_t j = 0; j < vi.tcount; ++j) { + assert(vi.tstart + j < m_refs.size()); + FaceInfo &fi = m_faceinfo[m_refs[vi.tstart + j].face]; + Index3 t = read_triangle(fi); + + for (size_t fid : t) { + size_t ofs=0; + while (ofs < vcount.size()) + { + if (vids[ofs] == fid) break; + ofs++; + } + if (ofs == vcount.size()) + { + vcount.emplace_back(1); + vids.emplace_back(fid); + } + else + vcount[ofs]++; + } + } + + for (size_t j = 0; j < vcount.size(); ++j) + if(vcount[j] == 1) m_vertexinfo[vids[j]].border = true; + } + } +} + +template +void SimplifiableMesh::update_triangles(size_t i0, + VertexInfo & vi, + std::vector &deleted, + int &deleted_triangles) +{ + Vertex p; + for (size_t k = 0; k < vi.tcount; ++k) { + assert(vi.tstart + k < m_refs.size()); + + Ref &r = m_refs[vi.tstart + k]; + FaceInfo &fi = m_faceinfo[r.face]; + + if (fi.deleted) continue; + + if (deleted[k]) { + fi.deleted = true; + deleted_triangles++; + continue; + } + + Index3 t = read_triangle(fi); + t[r.vertex] = i0; + write_triangle(fi, t); + + fi.dirty = true; + fi.err[0] = calculate_error(t[0], t[1], p); + fi.err[1] = calculate_error(t[1], t[2], p); + fi.err[2] = calculate_error(t[2], t[0], p); + fi.err[3] = std::min(fi.err[0], std::min(fi.err[1], fi.err[2])); + m_refs.emplace_back(r); + } +} + +template +bool SimplifiableMesh::flipped(const Vertex & p, + size_t /*i0*/, + size_t i1, + VertexInfo & v0, + VertexInfo & /*v1*/, + std::vector &deleted) +{ + for (size_t k = 0; k < v0.tcount; ++k) { + size_t ridx = v0.tstart + k; + assert(ridx < m_refs.size()); + + FaceInfo &fi = m_faceinfo[m_refs[ridx].face]; + if (fi.deleted) continue; + + Index3 t = read_triangle(fi); + int s = m_refs[ridx].vertex; + size_t id1 = t[(s+1) % 3]; + size_t id2 = t[(s+2) % 3]; + + if(id1 == i1 || id2 == i1) // delete ? + { + deleted[k] = true; + continue; + } + + Vertex d1 = read_vertex(id1) - p; + normalize(d1); + Vertex d2 = read_vertex(id2) - p; + normalize(d2); + + if (std::abs(dot(d1, d2)) > 0.999) return true; + + Vertex n = cross(d1, d2); + normalize(n); + + deleted[k] = false; + if (dot(n, fi.n) < 0.2) return true; + } + + return false; +} + +template +void SimplifiableMesh::simplify_mesh_lossless() +{ + // init + for (FaceInfo &fi : m_faceinfo) fi.deleted = false; + + // main iteration loop + int deleted_triangles=0; + std::vector deleted0, deleted1; + + for (int iteration = 0; iteration < 9999; iteration ++) { + // update mesh constantly + update_mesh(iteration); + + // clear dirty flag + for (FaceInfo &fi : m_faceinfo) fi.dirty = false; + + // + // All triangles with edges below the threshold will be removed + // + // The following numbers works well for most models. + // If it does not, try to adjust the 3 parameters + // + double threshold = std::numeric_limits::epsilon(); //1.0E-3 EPS; // Really? (tm) + + dout() << "lossless iteration " << iteration << "\n"; + + for (FaceInfo &fi : m_faceinfo) { + if (fi.err[3] > threshold || fi.deleted || fi.dirty) continue; + + for (size_t j = 0; j < 3; ++j) { + if (fi.err[j] > threshold) continue; + + Index3 t = read_triangle(fi); + size_t i0 = t[j]; + VertexInfo &v0 = m_vertexinfo[i0]; + + size_t i1 = t[(j + 1) % 3]; + VertexInfo &v1 = m_vertexinfo[i1]; + + // Border check + if(v0.border != v1.border) continue; + + // Compute vertex to collapse to + Vertex p; + calculate_error(i0, i1, p); + + deleted0.resize(v0.tcount); // normals temporarily + deleted1.resize(v1.tcount); // normals temporarily + + // don't remove if flipped + if (flipped(p, i0, i1, v0, v1, deleted0)) continue; + if (flipped(p, i1, i0, v1, v0, deleted1)) continue; + + // not flipped, so remove edge + write_vertex(v0, p); + v0.q = v1.q + v0.q; + size_t tstart = m_refs.size(); + + update_triangles(i0, v0, deleted0, deleted_triangles); + update_triangles(i0, v1, deleted1, deleted_triangles); + + assert(m_refs.size() >= tstart); + + size_t tcount = m_refs.size() - tstart; + + if(tcount <= v0.tcount) + { + // save ram + if (tcount) { + auto from = m_refs.begin() + tstart, to = from + tcount; + std::copy(from, to, m_refs.begin() + v0.tstart); + } + } + else + // append + v0.tstart = tstart; + + v0.tcount = tcount; + break; + } + } + + if (deleted_triangles <= 0) break; + deleted_triangles = 0; + } + + compact(); +} + +} // namespace implementation +} // namespace SimplifyMesh + +#endif // SIMPLIFYMESHIMPL_HPP diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5cd97522d9..4c6cd62cf2 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -70,6 +70,34 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl_get_size(&stl); } +TriangleMesh::TriangleMesh(const indexed_triangle_set &M) +{ + stl.stats.type = inmemory; + + // count facets and allocate memory + stl.stats.number_of_facets = uint32_t(M.indices.size()); + stl.stats.original_num_facets = int(stl.stats.number_of_facets); + stl_allocate(&stl); + + for (uint32_t i = 0; i < stl.stats.number_of_facets; ++ i) { + stl_facet facet; + facet.vertex[0] = M.vertices[size_t(M.indices[i](0))]; + facet.vertex[1] = M.vertices[size_t(M.indices[i](1))]; + facet.vertex[2] = M.vertices[size_t(M.indices[i](2))]; + facet.extra[0] = 0; + facet.extra[1] = 0; + + stl_normal normal; + stl_calculate_normal(normal, &facet); + stl_normalize_vector(normal); + facet.normal = normal; + + stl.facet_start[i] = facet; + } + + stl_get_size(&stl); +} + // #define SLIC3R_TRACE_REPAIR void TriangleMesh::repair(bool update_shared_vertices) diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index ef935455ef..1a22a93435 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -23,6 +23,7 @@ class TriangleMesh public: TriangleMesh() : repaired(false) {} TriangleMesh(const Pointf3s &points, const std::vector &facets); + explicit TriangleMesh(const indexed_triangle_set &M); 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, ""); } diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 8ee43acc8f..adcb2722d8 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(${_TEST_NAME}_tests test_placeholder_parser.cpp test_polygon.cpp test_stl.cpp + test_meshsimplify.cpp ) if (TARGET OpenVDB::openvdb) diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index 0cb1ac343c..65b87c2a23 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -9,6 +9,8 @@ #include +#include + #if defined(WIN32) || defined(_WIN32) #define PATH_SEPARATOR R"(\)" #else @@ -23,6 +25,7 @@ static Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } + TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") { Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj"); @@ -40,3 +43,4 @@ TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]") in_mesh.require_shared_vertices(); in_mesh.WriteOBJFile("merged_out.obj"); } + diff --git a/tests/libslic3r/test_meshsimplify.cpp b/tests/libslic3r/test_meshsimplify.cpp new file mode 100644 index 0000000000..d21c3a8924 --- /dev/null +++ b/tests/libslic3r/test_meshsimplify.cpp @@ -0,0 +1,11 @@ +#include +#include + +//#include + +//TEST_CASE("Mesh simplification", "[mesh_simplify]") { +// Simplify::load_obj(TEST_DATA_DIR PATH_SEPARATOR "zaba.obj"); +// Simplify::simplify_mesh_lossless(); +// Simplify::write_obj("zaba_simplified.obj"); +//} + From 75260eea127e04de778a747dc7f5f75d8cebee63 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 23 Jan 2020 13:18:44 +0100 Subject: [PATCH 127/130] Fix latomic linking on Mac SPE-1079 --- cmake/modules/CheckAtomic.cmake | 106 ++++++++++++++++++++++++++++++++ src/libslic3r/CMakeLists.txt | 7 ++- 2 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 cmake/modules/CheckAtomic.cmake diff --git a/cmake/modules/CheckAtomic.cmake b/cmake/modules/CheckAtomic.cmake new file mode 100644 index 0000000000..c045e30b22 --- /dev/null +++ b/cmake/modules/CheckAtomic.cmake @@ -0,0 +1,106 @@ +# atomic builtins are required for threading support. + +INCLUDE(CheckCXXSourceCompiles) +INCLUDE(CheckLibraryExists) + +# Sometimes linking against libatomic is required for atomic ops, if +# the platform doesn't support lock-free atomics. + +function(check_working_cxx_atomics varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") + CHECK_CXX_SOURCE_COMPILES(" +#include +std::atomic x; +int main() { + return x; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics) + +function(check_working_cxx_atomics64 varname) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") + CHECK_CXX_SOURCE_COMPILES(" +#include +#include +std::atomic x (0); +int main() { + uint64_t i = x.load(std::memory_order_relaxed); + return 0; +} +" ${varname}) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) +endfunction(check_working_cxx_atomics64) + + +# This isn't necessary on MSVC, so avoid command-line switch annoyance +# by only running on GCC-like hosts. +if (LLVM_COMPILER_IS_GCC_COMPATIBLE) + # First check if atomics work without the library. + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) + # If not, check if the library exists, and atomics work with it. + if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) + check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) + if( HAVE_LIBATOMIC ) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS_WITH_LIB) + message(FATAL_ERROR "Host compiler must support std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") + endif() + endif() +endif() + +# Check for 64 bit atomic operations. +if(MSVC) + set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) +else() + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) +endif() + +# If not, check if the library exists, and atomics work with it. +if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) + check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) + if(HAVE_CXX_LIBATOMICS64) + list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") + check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) + if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) + message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!") + endif() + else() + message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.") + endif() +endif() + +## TODO: This define is only used for the legacy atomic operations in +## llvm's Atomic.h, which should be replaced. Other code simply +## assumes C++11 works. +CHECK_CXX_SOURCE_COMPILES(" +#ifdef _MSC_VER +#include +#endif +int main() { +#ifdef _MSC_VER + volatile LONG val = 1; + MemoryBarrier(); + InterlockedCompareExchange(&val, 0, 1); + InterlockedIncrement(&val); + InterlockedDecrement(&val); +#else + volatile unsigned long val = 1; + __sync_synchronize(); + __sync_val_compare_and_swap(&val, 1, 0); + __sync_add_and_fetch(&val, 1); + __sync_sub_and_fetch(&val, 1); +#endif + return 0; + } +" LLVM_HAS_ATOMICS) + +if( NOT LLVM_HAS_ATOMICS ) + message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing") +endif() \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index d01c01ec2d..e24880ca63 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -265,8 +265,11 @@ target_link_libraries(libslic3r ${CMAKE_DL_LIBS} ) -if (NOT MSVC) - target_link_libraries(libslic3r atomic) # no cmake magic for this... +include(CheckAtomic) + +check_working_cxx_atomics(HAS_ATOMIC) +if (NOT HAS_ATOMIC) + target_link_libraries(libslic3r atomic) endif() if (TARGET OpenVDB::openvdb) From fde0803a8987bab7afd44b99d28967410141355e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 23 Jan 2020 13:45:20 +0100 Subject: [PATCH 128/130] Upgrade GMP version (link errors similar to RPI) --- deps/GMP/GMP.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/GMP/GMP.cmake b/deps/GMP/GMP.cmake index 8bcf948592..4e8228cbac 100644 --- a/deps/GMP/GMP.cmake +++ b/deps/GMP/GMP.cmake @@ -18,7 +18,8 @@ if (MSVC) else () ExternalProject_Add(dep_GMP - URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2 + # URL https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2 + URL https://gmplib.org/download/gmp/gmp-6.2.0.tar.lz BUILD_IN_SOURCE ON CONFIGURE_COMMAND ./configure --enable-shared=no --enable-cxx=yes --enable-static=yes "--prefix=${DESTDIR}/usr/local" --with-pic BUILD_COMMAND make -j From 81b7f437da596e88712fb8af7fa2010241ae392c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 23 Jan 2020 16:02:23 +0100 Subject: [PATCH 129/130] update openvdb build patch to solve latomic --- deps/openvdb-mods.patch | 653 ++++++++++++++++++++++++---------------- 1 file changed, 389 insertions(+), 264 deletions(-) diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index 023cb53087..c365a43058 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,24 +1,25 @@ -From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001 +From c660786dbac9e4c284049af4da56c34a998a4b68 Mon Sep 17 00:00:00 2001 From: tamasmeszaros -Date: Wed, 16 Oct 2019 17:42:50 +0200 -Subject: [PATCH] Build fixes for PrusaSlicer integration +Date: Thu, 23 Jan 2020 16:27:21 +0100 +Subject: [PATCH] openvdb-mods -Signed-off-by: tamasmeszaros --- CMakeLists.txt | 3 - - cmake/FindBlosc.cmake | 218 --------------- + cmake/CheckAtomic.cmake | 106 ++++++ + cmake/FindBlosc.cmake | 218 ------------ cmake/FindCppUnit.cmake | 4 +- - cmake/FindIlmBase.cmake | 337 ---------------------- - cmake/FindOpenEXR.cmake | 329 ---------------------- + cmake/FindIlmBase.cmake | 337 ------------------ + cmake/FindOpenEXR.cmake | 329 ------------------ cmake/FindOpenVDB.cmake | 19 +- - cmake/FindTBB.cmake | 605 ++++++++++++++++++++-------------------- + cmake/FindTBB.cmake | 599 ++++++++++++++++---------------- openvdb/CMakeLists.txt | 13 +- openvdb/Grid.cc | 3 + openvdb/PlatformConfig.h | 9 +- - openvdb/cmd/CMakeLists.txt | 4 +- + openvdb/cmd/CMakeLists.txt | 11 +- openvdb/unittest/CMakeLists.txt | 3 +- openvdb/unittest/TestFile.cc | 2 +- - 13 files changed, 336 insertions(+), 1213 deletions(-) + 14 files changed, 446 insertions(+), 1210 deletions(-) + create mode 100644 cmake/CheckAtomic.cmake delete mode 100644 cmake/FindBlosc.cmake delete mode 100644 cmake/FindIlmBase.cmake delete mode 100644 cmake/FindOpenEXR.cmake @@ -40,6 +41,119 @@ index 580b353..6d364c1 100644 cmake/FindOpenVDB.cmake cmake/FindTBB.cmake cmake/OpenVDBGLFW3Setup.cmake +diff --git a/cmake/CheckAtomic.cmake b/cmake/CheckAtomic.cmake +new file mode 100644 +index 0000000..c045e30 +--- /dev/null ++++ b/cmake/CheckAtomic.cmake +@@ -0,0 +1,106 @@ ++# atomic builtins are required for threading support. ++ ++INCLUDE(CheckCXXSourceCompiles) ++INCLUDE(CheckLibraryExists) ++ ++# Sometimes linking against libatomic is required for atomic ops, if ++# the platform doesn't support lock-free atomics. ++ ++function(check_working_cxx_atomics varname) ++ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) ++ set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -std=c++11") ++ CHECK_CXX_SOURCE_COMPILES(" ++#include ++std::atomic x; ++int main() { ++ return x; ++} ++" ${varname}) ++ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) ++endfunction(check_working_cxx_atomics) ++ ++function(check_working_cxx_atomics64 varname) ++ set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) ++ set(CMAKE_REQUIRED_FLAGS "-std=c++11 ${CMAKE_REQUIRED_FLAGS}") ++ CHECK_CXX_SOURCE_COMPILES(" ++#include ++#include ++std::atomic x (0); ++int main() { ++ uint64_t i = x.load(std::memory_order_relaxed); ++ return 0; ++} ++" ${varname}) ++ set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) ++endfunction(check_working_cxx_atomics64) ++ ++ ++# This isn't necessary on MSVC, so avoid command-line switch annoyance ++# by only running on GCC-like hosts. ++if (LLVM_COMPILER_IS_GCC_COMPATIBLE) ++ # First check if atomics work without the library. ++ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB) ++ # If not, check if the library exists, and atomics work with it. ++ if(NOT HAVE_CXX_ATOMICS_WITHOUT_LIB) ++ check_library_exists(atomic __atomic_fetch_add_4 "" HAVE_LIBATOMIC) ++ if( HAVE_LIBATOMIC ) ++ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") ++ check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB) ++ if (NOT HAVE_CXX_ATOMICS_WITH_LIB) ++ message(FATAL_ERROR "Host compiler must support std::atomic!") ++ endif() ++ else() ++ message(FATAL_ERROR "Host compiler appears to require libatomic, but cannot find it.") ++ endif() ++ endif() ++endif() ++ ++# Check for 64 bit atomic operations. ++if(MSVC) ++ set(HAVE_CXX_ATOMICS64_WITHOUT_LIB True) ++else() ++ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITHOUT_LIB) ++endif() ++ ++# If not, check if the library exists, and atomics work with it. ++if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB) ++ check_library_exists(atomic __atomic_load_8 "" HAVE_CXX_LIBATOMICS64) ++ if(HAVE_CXX_LIBATOMICS64) ++ list(APPEND CMAKE_REQUIRED_LIBRARIES "atomic") ++ check_working_cxx_atomics64(HAVE_CXX_ATOMICS64_WITH_LIB) ++ if (NOT HAVE_CXX_ATOMICS64_WITH_LIB) ++ message(FATAL_ERROR "Host compiler must support 64-bit std::atomic!") ++ endif() ++ else() ++ message(FATAL_ERROR "Host compiler appears to require libatomic for 64-bit operations, but cannot find it.") ++ endif() ++endif() ++ ++## TODO: This define is only used for the legacy atomic operations in ++## llvm's Atomic.h, which should be replaced. Other code simply ++## assumes C++11 works. ++CHECK_CXX_SOURCE_COMPILES(" ++#ifdef _MSC_VER ++#include ++#endif ++int main() { ++#ifdef _MSC_VER ++ volatile LONG val = 1; ++ MemoryBarrier(); ++ InterlockedCompareExchange(&val, 0, 1); ++ InterlockedIncrement(&val); ++ InterlockedDecrement(&val); ++#else ++ volatile unsigned long val = 1; ++ __sync_synchronize(); ++ __sync_val_compare_and_swap(&val, 1, 0); ++ __sync_add_and_fetch(&val, 1); ++ __sync_sub_and_fetch(&val, 1); ++#endif ++ return 0; ++ } ++" LLVM_HAS_ATOMICS) ++ ++if( NOT LLVM_HAS_ATOMICS ) ++ message(STATUS "Warning: LLVM will be built thread-unsafe because atomic builtins are missing") ++endif() +\ No newline at end of file diff --git a/cmake/FindBlosc.cmake b/cmake/FindBlosc.cmake deleted file mode 100644 index 5aacfdd..0000000 @@ -965,7 +1079,7 @@ index 339c1a2..0000000 - message(FATAL_ERROR "Unable to find OpenEXR") -endif() diff --git a/cmake/FindOpenVDB.cmake b/cmake/FindOpenVDB.cmake -index 63a2eda..6211071 100644 +index 63a2eda..d9f6d07 100644 --- a/cmake/FindOpenVDB.cmake +++ b/cmake/FindOpenVDB.cmake @@ -244,7 +244,7 @@ set(OpenVDB_LIB_COMPONENTS "") @@ -1004,7 +1118,7 @@ index 63a2eda..6211071 100644 ) + + if (OPENVDB_USE_STATIC_LIBS) -+ set_target_properties(OpenVDB::${COMPONENT} PROPERTIES ++ set_target_properties(OpenVDB::${COMPONENT} PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "OPENVDB_STATICLIB;OPENVDB_OPENEXR_STATICLIB" + ) + endif() @@ -1012,7 +1126,7 @@ index 63a2eda..6211071 100644 endforeach() diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake -index bdf9c81..c6bdec9 100644 +index bdf9c81..06093a4 100644 --- a/cmake/FindTBB.cmake +++ b/cmake/FindTBB.cmake @@ -1,333 +1,332 @@ @@ -1022,35 +1136,21 @@ index bdf9c81..c6bdec9 100644 -# All rights reserved. This software is distributed under the -# Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) +# Copyright (c) 2015 Justus Calvin -+# + # +-# Redistributions of source code must retain the above copyright +-# and license notice and the following restrictions and disclaimer. +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in all -+# copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -+# SOFTWARE. -+ - # --# Redistributions of source code must retain the above copyright --# and license notice and the following restrictions and disclaimer. -+# FindTBB -+# ------- # -# * Neither the name of DreamWorks Animation nor the names of -# its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -+# Find TBB include directories and libraries. ++# The above copyright notice and this permission notice shall be included in all ++# copies or substantial portions of the Software. # -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT @@ -1065,7 +1165,14 @@ index bdf9c81..c6bdec9 100644 -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE -# LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00. -+# Usage: ++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++# SOFTWARE. ++ # -#[=======================================================================[.rst: - @@ -1142,19 +1249,26 @@ index bdf9c81..c6bdec9 100644 -if(POLICY CMP0057) - cmake_policy(SET CMP0057 NEW) -endif() ++# FindTBB ++# ------- ++# ++# Find TBB include directories and libraries. ++# ++# Usage: ++# +# find_package(TBB [major[.minor]] [EXACT] +# [QUIET] [REQUIRED] +# [[COMPONENTS] [components...]] -+# [OPTIONAL_COMPONENTS components...]) ++# [OPTIONAL_COMPONENTS components...]) +# -+# where the allowed components are tbbmalloc and tbb_preview. Users may modify ++# where the allowed components are tbbmalloc and tbb_preview. Users may modify +# the behavior of this module with the following variables: +# +# * TBB_ROOT_DIR - The base directory the of TBB installation. +# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. +# * TBB_LIBRARY - The directory that contains the TBB library files. -+# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -+# These libraries, if specified, override the ++# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. ++# These libraries, if specified, override the +# corresponding library search results, where +# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, +# tbb_preview, or tbb_preview_debug. @@ -1167,7 +1281,7 @@ index bdf9c81..c6bdec9 100644 +# Users may modify the behavior of this module with the following environment +# variables: +# -+# * TBB_INSTALL_DIR ++# * TBB_INSTALL_DIR +# * TBBROOT +# * LIBRARY_PATH +# @@ -1180,15 +1294,15 @@ index bdf9c81..c6bdec9 100644 +# * TBB_VERSION - The full version string +# * TBB_VERSION_MAJOR - The major version +# * TBB_VERSION_MINOR - The minor version -+# * TBB_INTERFACE_VERSION - The interface version number defined in ++# * TBB_INTERFACE_VERSION - The interface version number defined in +# tbb/tbb_stddef.h. -+# * TBB__LIBRARY_RELEASE - The path of the TBB release version of ++# * TBB__LIBRARY_RELEASE - The path of the TBB release version of +# , where may be tbb, tbb_debug, -+# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. -+# * TBB__LIBRARY_DEGUG - The path of the TBB release version of ++# * TBB__LIBRARY_DEGUG - The path of the TBB release version of +# , where may be tbb, tbb_debug, -+# tbbmalloc, tbbmalloc_debug, tbb_preview, or ++# tbbmalloc, tbbmalloc_debug, tbb_preview, or +# tbb_preview_debug. +# +# The following varibles should be used to build and link with TBB: @@ -1244,12 +1358,10 @@ index bdf9c81..c6bdec9 100644 - set(TBB_FIND_COMPONENTS ${_TBB_COMPONENT_LIST}) -endif() +include(FindPackageHandleStandardArgs) -+ -+find_package(Threads QUIET REQUIRED) -# Append TBB_ROOT or $ENV{TBB_ROOT} if set (prioritize the direct cmake var) -set(_TBB_ROOT_SEARCH_DIR "") -+if(NOT TBB_FOUND) ++find_package(Threads QUIET REQUIRED) -if(TBB_ROOT) - list(APPEND _TBB_ROOT_SEARCH_DIR ${TBB_ROOT}) @@ -1257,41 +1369,9 @@ index bdf9c81..c6bdec9 100644 - set(_ENV_TBB_ROOT $ENV{TBB_ROOT}) - if(_ENV_TBB_ROOT) - list(APPEND _TBB_ROOT_SEARCH_DIR ${_ENV_TBB_ROOT}) -+ ################################## -+ # Check the build type -+ ################################## -+ -+ if(NOT DEFINED TBB_USE_DEBUG_BUILD) -+ if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)") -+ set(TBB_BUILD_TYPE DEBUG) -+ else() -+ set(TBB_BUILD_TYPE RELEASE) -+ endif() -+ elseif(TBB_USE_DEBUG_BUILD) -+ set(TBB_BUILD_TYPE DEBUG) -+ else() -+ set(TBB_BUILD_TYPE RELEASE) - endif() +- endif() -endif() -+ -+ ################################## -+ # Set the TBB search directories -+ ################################## -+ -+ # Define search paths based on user input and environment variables -+ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) -+ -+ # Define the search directories based on the current platform -+ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") -+ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" -+ "C:/Program Files (x86)/Intel/TBB") -+ -+ # Set the target architecture -+ if(CMAKE_SIZEOF_VOID_P EQUAL 8) -+ set(TBB_ARCHITECTURE "intel64") -+ else() -+ set(TBB_ARCHITECTURE "ia32") -+ endif() ++if(NOT TBB_FOUND) -# Additionally try and use pkconfig to find Tbb - @@ -1339,6 +1419,57 @@ index bdf9c81..c6bdec9 100644 - - set(Tbb_VERSION ${Tbb_VERSION_MAJOR}.${Tbb_VERSION_MINOR}) -endif() ++ ################################## ++ # Check the build type ++ ################################## ++ ++ if(NOT DEFINED TBB_USE_DEBUG_BUILD) ++ if(CMAKE_BUILD_TYPE MATCHES "(Debug|DEBUG|debug)") ++ set(TBB_BUILD_TYPE DEBUG) ++ else() ++ set(TBB_BUILD_TYPE RELEASE) ++ endif() ++ elseif(TBB_USE_DEBUG_BUILD) ++ set(TBB_BUILD_TYPE DEBUG) ++ else() ++ set(TBB_BUILD_TYPE RELEASE) ++ endif() + +-# ------------------------------------------------------------------------ +-# Search for TBB lib DIR +-# ------------------------------------------------------------------------ ++ ################################## ++ # Set the TBB search directories ++ ################################## + +-set(_TBB_LIBRARYDIR_SEARCH_DIRS "") ++ # Define search paths based on user input and environment variables ++ set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) + +-# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order ++ # Define the search directories based on the current platform ++ if(CMAKE_SYSTEM_NAME STREQUAL "Windows") ++ set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" ++ "C:/Program Files (x86)/Intel/TBB") + +-set(_TBB_LIBRARYDIR_SEARCH_DIRS "") +-list(APPEND _TBB_LIBRARYDIR_SEARCH_DIRS +- ${TBB_LIBRARYDIR} +- ${_TBB_ROOT_SEARCH_DIR} +- ${PC_Tbb_LIBRARY_DIRS} +- ${SYSTEM_LIBRARY_PATHS} +-) ++ # Set the target architecture ++ if(CMAKE_SIZEOF_VOID_P EQUAL 8) ++ set(TBB_ARCHITECTURE "intel64") ++ else() ++ set(TBB_ARCHITECTURE "ia32") ++ endif() + +-set(TBB_PATH_SUFFIXES +- lib64 +- lib +-) + # Set the TBB search library path search suffix based on the version of VC + if(WINDOWS_STORE) + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") @@ -1352,104 +1483,16 @@ index bdf9c81..c6bdec9 100644 + set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") + endif() --# ------------------------------------------------------------------------ --# Search for TBB lib DIR --# ------------------------------------------------------------------------ +-# platform branching + # Add the library path search suffix for the VC independent version of TBB + list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") -+ -+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") -+ # OS X -+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") -+ -+ # TODO: Check to see which C++ library is being used by the compiler. -+ if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) -+ # The default C++ library on OS X 10.9 and later is libc++ -+ set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") -+ else() -+ set(TBB_LIB_PATH_SUFFIX "lib") -+ endif() -+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") -+ # Linux -+ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") -+ -+ # TODO: Check compiler version to see the suffix should be /gcc4.1 or -+ # /gcc4.1. For now, assume that the compiler is more recent than -+ # gcc 4.4.x or later. -+ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") -+ set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") -+ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") -+ set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") -+ endif() -+ endif() -+ -+ ################################## -+ # Find the TBB include dir -+ ################################## -+ -+ find_path(TBB_INCLUDE_DIRS tbb/tbb.h -+ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} -+ PATHS ${TBB_DEFAULT_SEARCH_DIR} -+ PATH_SUFFIXES include) -+ -+ ################################## -+ # Set version strings -+ ################################## -+ -+ if(TBB_INCLUDE_DIRS) -+ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) -+ string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" -+ TBB_VERSION_MAJOR "${_tbb_version_file}") -+ string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" -+ TBB_VERSION_MINOR "${_tbb_version_file}") -+ string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" -+ TBB_INTERFACE_VERSION "${_tbb_version_file}") -+ set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") -+ endif() - --set(_TBB_LIBRARYDIR_SEARCH_DIRS "") -+ ################################## -+ # Find TBB components -+ ################################## - --# Append to _TBB_LIBRARYDIR_SEARCH_DIRS in priority order -+ if(TBB_VERSION VERSION_LESS 4.3) -+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) -+ else() -+ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) -+ endif() - --set(_TBB_LIBRARYDIR_SEARCH_DIRS "") --list(APPEND _TBB_LIBRARYDIR_SEARCH_DIRS -- ${TBB_LIBRARYDIR} -- ${_TBB_ROOT_SEARCH_DIR} -- ${PC_Tbb_LIBRARY_DIRS} -- ${SYSTEM_LIBRARY_PATHS} --) -+ if(TBB_STATIC) -+ set(TBB_STATIC_SUFFIX "_static") -+ endif() - --set(TBB_PATH_SUFFIXES -- lib64 -- lib --) -+ # Find each component -+ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) -+ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") - --# platform branching -+ unset(TBB_${_comp}_LIBRARY_DEBUG CACHE) -+ unset(TBB_${_comp}_LIBRARY_RELEASE CACHE) -if(UNIX) - list(INSERT TBB_PATH_SUFFIXES 0 lib/x86_64-linux-gnu) -endif() -+ # Search for the libraries -+ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} -+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} -+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH -+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) ++ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") ++ # OS X ++ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") -if(APPLE) - if(TBB_FOR_CLANG) @@ -1471,29 +1514,33 @@ index bdf9c81..c6bdec9 100644 - list(GET GCC_VERSION_COMPONENTS 0 GCC_MAJOR) - list(GET GCC_VERSION_COMPONENTS 1 GCC_MINOR) - list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc${GCC_MAJOR}.${GCC_MINOR}) -- else() ++ # TODO: Check to see which C++ library is being used by the compiler. ++ if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) ++ # The default C++ library on OS X 10.9 and later is libc++ ++ set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") + else() - list(INSERT TBB_PATH_SUFFIXES 0 lib/intel64/gcc4.4) -- endif() -- endif() ++ set(TBB_LIB_PATH_SUFFIX "lib") ++ endif() ++ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") ++ # Linux ++ set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") ++ ++ # TODO: Check compiler version to see the suffix should be /gcc4.1 or ++ # /gcc4.1. For now, assume that the compiler is more recent than ++ # gcc 4.4.x or later. ++ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") ++ set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") ++ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") ++ set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") + endif() + endif() -endif() -+ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug -+ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} -+ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH -+ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - +- -if(UNIX AND TBB_USE_STATIC_LIBS) - set(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") -endif() -+ if(TBB_${_comp}_LIBRARY_DEBUG) -+ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") -+ endif() -+ if(TBB_${_comp}_LIBRARY_RELEASE) -+ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") -+ endif() -+ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) -+ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") -+ endif() -set(Tbb_LIB_COMPONENTS "") - @@ -1516,39 +1563,44 @@ index bdf9c81..c6bdec9 100644 - # Extract the directory and apply the matched text (in brackets) - get_filename_component(Tbb_${COMPONENT}_DIR "${Tbb_${COMPONENT}_LIBRARY}" DIRECTORY) - set(Tbb_${COMPONENT}_LIBRARY "${Tbb_${COMPONENT}_DIR}/${CMAKE_MATCH_1}") -+ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") -+ set(TBB_${_comp}_FOUND TRUE) -+ else() -+ set(TBB_${_comp}_FOUND FALSE) - endif() +- endif() +- endif() ++ ################################## ++ # Find the TBB include dir ++ ################################## + -+ # Mark internal variables as advanced -+ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) -+ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) -+ mark_as_advanced(TBB_${_comp}_LIBRARY) ++ find_path(TBB_INCLUDE_DIRS tbb/tbb.h ++ HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ++ PATH_SUFFIXES include) + - endif() -- endif() -+ endforeach() ++ ################################## ++ # Set version strings ++ ################################## ++ ++ if(TBB_INCLUDE_DIRS) ++ file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) ++ string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" ++ TBB_VERSION_MAJOR "${_tbb_version_file}") ++ string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" ++ TBB_VERSION_MINOR "${_tbb_version_file}") ++ string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" ++ TBB_INTERFACE_VERSION "${_tbb_version_file}") ++ set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") + endif() - list(APPEND Tbb_LIB_COMPONENTS ${Tbb_${COMPONENT}_LIBRARY}) + ################################## -+ # Set compile flags and libraries ++ # Find TBB components + ################################## - if(Tbb_${COMPONENT}_LIBRARY) - set(TBB_${COMPONENT}_FOUND TRUE) -- else() ++ if(TBB_VERSION VERSION_LESS 4.3) ++ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) + else() - set(TBB_${COMPONENT}_FOUND FALSE) -+ set(TBB_DEFINITIONS_RELEASE "") -+ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1") -+ -+ if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) -+ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") -+ endif() -+ -+ if(NOT MSVC AND NOT TBB_LIBRARIES) -+ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE}) ++ set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) endif() -endforeach() @@ -1556,61 +1608,51 @@ index bdf9c81..c6bdec9 100644 - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) - unset(_TBB_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES) -endif() -+ set(TBB_DEFINITIONS "") -+ if (MSVC AND TBB_STATIC) -+ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE) -+ endif () -+ -+ unset (TBB_STATIC_SUFFIX) -+ -+ find_package_handle_standard_args(TBB -+ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES -+ FAIL_MESSAGE "TBB library cannot be found. Consider set TBBROOT environment variable." -+ HANDLE_COMPONENTS -+ VERSION_VAR TBB_VERSION) -+ -+ ################################## -+ # Create targets -+ ################################## -+ -+ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) -+ add_library(TBB::tbb UNKNOWN IMPORTED) -+ set_target_properties(TBB::tbb PROPERTIES -+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}" -+ INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}" -+ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} -+ IMPORTED_LOCATION ${TBB_LIBRARIES}) -+ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) -+ set_target_properties(TBB::tbb PROPERTIES -+ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${TBB_DEFINITIONS_RELEASE}>" -+ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} -+ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE} -+ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} -+ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} -+ ) -+ endif() ++ if(TBB_STATIC) ++ set(TBB_STATIC_SUFFIX "_static") + endif() -# ------------------------------------------------------------------------ -# Cache and set TBB_FOUND -# ------------------------------------------------------------------------ -+ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) ++ # Find each component ++ foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) ++ if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") + -+ unset(TBB_ARCHITECTURE) -+ unset(TBB_BUILD_TYPE) -+ unset(TBB_LIB_PATH_SUFFIX) -+ unset(TBB_DEFAULT_SEARCH_DIR) ++ unset(TBB_${_comp}_LIBRARY_DEBUG CACHE) ++ unset(TBB_${_comp}_LIBRARY_RELEASE CACHE) + -+ if(TBB_DEBUG) -+ message(STATUS " TBB_FOUND = ${TBB_FOUND}") -+ message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") -+ message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") -+ message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") -+ message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}") -+ message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") -+ message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") -+ message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") -+ endif() ++ # Search for the libraries ++ find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp}${TBB_STATIC_SUFFIX} ++ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH ++ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) ++ ++ find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}${TBB_STATIC_SUFFIX}_debug ++ HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} ++ PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH ++ PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) ++ ++ if(TBB_${_comp}_LIBRARY_DEBUG) ++ list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") ++ endif() ++ if(TBB_${_comp}_LIBRARY_RELEASE) ++ list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") ++ endif() ++ if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) ++ set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") ++ endif() ++ ++ if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") ++ set(TBB_${_comp}_FOUND TRUE) ++ else() ++ set(TBB_${_comp}_FOUND FALSE) ++ endif() ++ ++ # Mark internal variables as advanced ++ mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) ++ mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) ++ mark_as_advanced(TBB_${_comp}_LIBRARY) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(TBB @@ -1646,10 +1688,79 @@ index bdf9c81..c6bdec9 100644 - INTERFACE_COMPILE_OPTIONS "${Tbb_DEFINITIONS}" - INTERFACE_INCLUDE_DIRECTORIES "${Tbb_INCLUDE_DIR}" - ) -- endif() -- endforeach() + endif() + endforeach() -elseif(TBB_FIND_REQUIRED) - message(FATAL_ERROR "Unable to find TBB") ++ ++ ################################## ++ # Set compile flags and libraries ++ ################################## ++ ++ set(TBB_DEFINITIONS_RELEASE "") ++ set(TBB_DEFINITIONS_DEBUG "TBB_USE_DEBUG=1") ++ ++ if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) ++ set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") ++ endif() ++ ++ if(NOT MSVC AND NOT TBB_LIBRARIES) ++ set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE}) ++ endif() ++ ++ set(TBB_DEFINITIONS "") ++ if (MSVC AND TBB_STATIC) ++ set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE) ++ endif () ++ ++ unset (TBB_STATIC_SUFFIX) ++ ++ find_package_handle_standard_args(TBB ++ REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES ++ FAIL_MESSAGE "TBB library cannot be found. Consider set TBBROOT environment variable." ++ HANDLE_COMPONENTS ++ VERSION_VAR TBB_VERSION) ++ ++ ################################## ++ # Create targets ++ ################################## ++ ++ if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) ++ add_library(TBB::tbb UNKNOWN IMPORTED) ++ set_target_properties(TBB::tbb PROPERTIES ++ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS}" ++ INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS}" ++ INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} ++ IMPORTED_LOCATION ${TBB_LIBRARIES}) ++ if(TBB_LIBRARIES_RELEASE AND TBB_LIBRARIES_DEBUG) ++ set_target_properties(TBB::tbb PROPERTIES ++ INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS};$<$,$>:${TBB_DEFINITIONS_DEBUG}>;$<$:${TBB_DEFINITIONS_RELEASE}>" ++ IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG} ++ IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE} ++ IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE} ++ IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE} ++ ) ++ endif() ++ endif() ++ ++ mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) ++ ++ unset(TBB_ARCHITECTURE) ++ unset(TBB_BUILD_TYPE) ++ unset(TBB_LIB_PATH_SUFFIX) ++ unset(TBB_DEFAULT_SEARCH_DIR) ++ ++ if(TBB_DEBUG) ++ message(STATUS " TBB_FOUND = ${TBB_FOUND}") ++ message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}") ++ message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}") ++ message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}") ++ message(STATUS " TBB_DEFINITIONS_DEBUG = ${TBB_DEFINITIONS_DEBUG}") ++ message(STATUS " TBB_LIBRARIES_DEBUG = ${TBB_LIBRARIES_DEBUG}") ++ message(STATUS " TBB_DEFINITIONS_RELEASE = ${TBB_DEFINITIONS_RELEASE}") ++ message(STATUS " TBB_LIBRARIES_RELEASE = ${TBB_LIBRARIES_RELEASE}") ++ endif() ++ endif() diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt index 89301bd..df27aae 100644 @@ -1728,7 +1839,7 @@ index 20ad9a3..c2dd1ef 100644 #endif // _WIN32 diff --git a/openvdb/cmd/CMakeLists.txt b/openvdb/cmd/CMakeLists.txt -index 57fbec0..55b3850 100644 +index 57fbec0..0379756 100644 --- a/openvdb/cmd/CMakeLists.txt +++ b/openvdb/cmd/CMakeLists.txt @@ -74,8 +74,9 @@ if(WIN32) @@ -1750,6 +1861,20 @@ index 57fbec0..55b3850 100644 ) if(OPENVDB_BUILD_CORE) list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) +@@ -116,6 +116,13 @@ if(OPENVDB_BUILD_VDB_PRINT) + ) + endif() + ++ include(CheckAtomic) ++ ++ check_working_cxx_atomics(HAS_ATOMIC) ++ if (NOT HAS_ATOMIC) ++ target_link_libraries(vdb_print atomic) ++ endif() ++ + install(TARGETS vdb_print DESTINATION bin) + endif() + diff --git a/openvdb/unittest/CMakeLists.txt b/openvdb/unittest/CMakeLists.txt index c9e0c34..7e261c0 100644 --- a/openvdb/unittest/CMakeLists.txt @@ -1779,5 +1904,5 @@ index df51830..0ab0c12 100644 /// @todo This changes the compressor setting globally. if (blosc_set_compressor(compname) < 0) continue; -- -2.16.2.windows.1 +2.17.1 From cb39f6b212eab1be2b916fb8f9125a89818b252c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 23 Jan 2020 17:19:09 +0100 Subject: [PATCH 130/130] Use CheckAtomic differently --- deps/openvdb-mods.patch | 46 ++++++++++++++++++------------------ src/libslic3r/CMakeLists.txt | 7 ------ 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/deps/openvdb-mods.patch b/deps/openvdb-mods.patch index c365a43058..d80d0ffde1 100644 --- a/deps/openvdb-mods.patch +++ b/deps/openvdb-mods.patch @@ -1,6 +1,6 @@ -From c660786dbac9e4c284049af4da56c34a998a4b68 Mon Sep 17 00:00:00 2001 +From d359098d9989ac7dbd149611d6ac941529fb4157 Mon Sep 17 00:00:00 2001 From: tamasmeszaros -Date: Thu, 23 Jan 2020 16:27:21 +0100 +Date: Thu, 23 Jan 2020 17:17:36 +0100 Subject: [PATCH] openvdb-mods --- @@ -12,13 +12,13 @@ Subject: [PATCH] openvdb-mods cmake/FindOpenEXR.cmake | 329 ------------------ cmake/FindOpenVDB.cmake | 19 +- cmake/FindTBB.cmake | 599 ++++++++++++++++---------------- - openvdb/CMakeLists.txt | 13 +- + openvdb/CMakeLists.txt | 16 +- openvdb/Grid.cc | 3 + openvdb/PlatformConfig.h | 9 +- - openvdb/cmd/CMakeLists.txt | 11 +- + openvdb/cmd/CMakeLists.txt | 4 +- openvdb/unittest/CMakeLists.txt | 3 +- openvdb/unittest/TestFile.cc | 2 +- - 14 files changed, 446 insertions(+), 1210 deletions(-) + 14 files changed, 442 insertions(+), 1210 deletions(-) create mode 100644 cmake/CheckAtomic.cmake delete mode 100644 cmake/FindBlosc.cmake delete mode 100644 cmake/FindIlmBase.cmake @@ -1763,7 +1763,7 @@ index bdf9c81..06093a4 100644 + endif() diff --git a/openvdb/CMakeLists.txt b/openvdb/CMakeLists.txt -index 89301bd..df27aae 100644 +index 89301bd..6a3c90c 100644 --- a/openvdb/CMakeLists.txt +++ b/openvdb/CMakeLists.txt @@ -78,7 +78,7 @@ else() @@ -1775,7 +1775,21 @@ index 89301bd..df27aae 100644 message(DEPRECATION "Support for TBB versions < ${FUTURE_MINIMUM_TBB_VERSION} " "is deprecated and will be removed.") endif() -@@ -185,11 +185,6 @@ if(WIN32) +@@ -129,10 +129,13 @@ endif() + # include paths from shared installs (including houdini) may pull in the wrong + # headers + ++include (CheckAtomic) ++ + set(OPENVDB_CORE_DEPENDENT_LIBS + Boost::iostreams + Boost::system + IlmBase::Half ++ ${CMAKE_REQUIRED_LIBRARIES} + ) + + if(USE_EXR) +@@ -185,11 +188,6 @@ if(WIN32) endif() endif() @@ -1787,7 +1801,7 @@ index 89301bd..df27aae 100644 ##### Core library configuration set(OPENVDB_LIBRARY_SOURCE_FILES -@@ -374,10 +369,16 @@ set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES +@@ -374,10 +372,16 @@ set(OPENVDB_LIBRARY_UTIL_INCLUDE_FILES if(OPENVDB_CORE_SHARED) add_library(openvdb_shared SHARED ${OPENVDB_LIBRARY_SOURCE_FILES}) @@ -1839,7 +1853,7 @@ index 20ad9a3..c2dd1ef 100644 #endif // _WIN32 diff --git a/openvdb/cmd/CMakeLists.txt b/openvdb/cmd/CMakeLists.txt -index 57fbec0..0379756 100644 +index 57fbec0..55b3850 100644 --- a/openvdb/cmd/CMakeLists.txt +++ b/openvdb/cmd/CMakeLists.txt @@ -74,8 +74,9 @@ if(WIN32) @@ -1861,20 +1875,6 @@ index 57fbec0..0379756 100644 ) if(OPENVDB_BUILD_CORE) list(APPEND RPATHS ${CMAKE_INSTALL_PREFIX}/lib) -@@ -116,6 +116,13 @@ if(OPENVDB_BUILD_VDB_PRINT) - ) - endif() - -+ include(CheckAtomic) -+ -+ check_working_cxx_atomics(HAS_ATOMIC) -+ if (NOT HAS_ATOMIC) -+ target_link_libraries(vdb_print atomic) -+ endif() -+ - install(TARGETS vdb_print DESTINATION bin) - endif() - diff --git a/openvdb/unittest/CMakeLists.txt b/openvdb/unittest/CMakeLists.txt index c9e0c34..7e261c0 100644 --- a/openvdb/unittest/CMakeLists.txt diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e24880ca63..d92bb4cac7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -265,13 +265,6 @@ target_link_libraries(libslic3r ${CMAKE_DL_LIBS} ) -include(CheckAtomic) - -check_working_cxx_atomics(HAS_ATOMIC) -if (NOT HAS_ATOMIC) - target_link_libraries(libslic3r atomic) -endif() - if (TARGET OpenVDB::openvdb) target_link_libraries(libslic3r OpenVDB::openvdb) endif()